taffy/
style_helpers.rs

1//! Helper functions which it make it easier to create instances of types in the `style` and `geometry` modules.
2use crate::{
3    geometry::{Line, Point, Rect, Size},
4    style::LengthPercentage,
5};
6
7#[cfg(feature = "grid")]
8use crate::{
9    geometry::MinMax,
10    style::{
11        GridTemplateComponent, GridTemplateRepetition, MaxTrackSizingFunction, MinTrackSizingFunction, RepetitionCount,
12        TrackSizingFunction,
13    },
14    util::sys::Vec,
15    CheapCloneStr,
16};
17#[cfg(feature = "grid")]
18use core::fmt::Debug;
19
20/// Returns an auto-repeated track definition
21#[cfg(feature = "grid")]
22pub fn repeat<Input, S>(repetition_kind: Input, tracks: Vec<TrackSizingFunction>) -> GridTemplateComponent<S>
23where
24    Input: TryInto<RepetitionCount>,
25    <Input as TryInto<RepetitionCount>>::Error: Debug,
26    S: CheapCloneStr,
27{
28    GridTemplateComponent::Repeat(GridTemplateRepetition {
29        count: repetition_kind.try_into().unwrap(),
30        tracks,
31        line_names: Vec::new(),
32    })
33}
34
35#[cfg(feature = "grid")]
36/// Returns a grid template containing `count` evenly sized tracks
37pub fn evenly_sized_tracks<S: CheapCloneStr>(count: u16) -> Vec<GridTemplateComponent<S>> {
38    use crate::util::sys::new_vec_with_capacity;
39    let mut repeated_tracks = new_vec_with_capacity(1);
40    repeated_tracks.push(flex(1.0f32));
41    let mut tracks = new_vec_with_capacity(1);
42    tracks.push(repeat(count, repeated_tracks));
43    tracks
44}
45
46/// Specifies a grid line to place a grid item between in CSS Grid Line coordinates:
47///  - Positive indices count upwards from the start (top or left) of the explicit grid
48///  - Negative indices count downwards from the end (bottom or right) of the explicit grid
49///  - ZERO IS INVALID index, and will be treated as a GridPlacement::Auto.
50pub fn line<T: TaffyGridLine>(index: i16) -> T {
51    T::from_line_index(index)
52}
53/// Trait to abstract over grid line values
54pub trait TaffyGridLine {
55    /// Converts an i16 into Self
56    fn from_line_index(index: i16) -> Self;
57}
58
59/// Returns a GridPlacement::Span
60pub fn span<T: TaffyGridSpan>(span: u16) -> T {
61    T::from_span(span)
62}
63/// Trait to abstract over grid span values
64pub trait TaffyGridSpan {
65    /// Converts an u16 into Self
66    fn from_span(span: u16) -> Self;
67}
68
69/// Returns a MinMax with min value of min and max value of max
70#[cfg(feature = "grid")]
71pub fn minmax<Output>(min: MinTrackSizingFunction, max: MaxTrackSizingFunction) -> Output
72where
73    Output: From<MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>>,
74{
75    MinMax { min, max }.into()
76}
77
78/// Shorthand for minmax(0, Nfr). Probably what you want if you want exactly evenly sized tracks.
79#[cfg(feature = "grid")]
80pub fn flex<Input, Output>(flex_fraction: Input) -> Output
81where
82    Input: Into<f32> + Copy,
83    Output: From<MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>>,
84{
85    MinMax { min: zero(), max: fr(flex_fraction.into()) }.into()
86}
87
88/// Returns the zero value for that type
89pub const fn zero<T: TaffyZero>() -> T {
90    T::ZERO
91}
92
93/// Trait to abstract over zero values
94pub trait TaffyZero {
95    /// The zero value for type implementing TaffyZero
96    const ZERO: Self;
97}
98impl TaffyZero for f32 {
99    const ZERO: f32 = 0.0;
100}
101impl<T: TaffyZero> TaffyZero for Option<T> {
102    const ZERO: Option<T> = Some(T::ZERO);
103}
104impl<T: TaffyZero> TaffyZero for Point<T> {
105    const ZERO: Point<T> = Point { x: T::ZERO, y: T::ZERO };
106}
107impl<T: TaffyZero> Point<T> {
108    /// Returns a Point where both the x and y values are the zero value of the contained type
109    /// (e.g. 0.0, Some(0.0), or Dimension::Length(0.0))
110    pub const fn zero() -> Self {
111        zero::<Self>()
112    }
113}
114impl<T: TaffyZero> TaffyZero for Line<T> {
115    const ZERO: Line<T> = Line { start: T::ZERO, end: T::ZERO };
116}
117impl<T: TaffyZero> Line<T> {
118    /// Returns a Line where both the start and end values are the zero value of the contained type
119    /// (e.g. 0.0, Some(0.0), or Dimension::Length(0.0))
120    pub const fn zero() -> Self {
121        zero::<Self>()
122    }
123}
124impl<T: TaffyZero> TaffyZero for Size<T> {
125    const ZERO: Size<T> = Size { width: T::ZERO, height: T::ZERO };
126}
127impl<T: TaffyZero> Size<T> {
128    /// Returns a Size where both the width and height values are the zero value of the contained type
129    /// (e.g. 0.0, Some(0.0), or Dimension::Length(0.0))
130    pub const fn zero() -> Self {
131        zero::<Self>()
132    }
133}
134impl<T: TaffyZero> TaffyZero for Rect<T> {
135    const ZERO: Rect<T> = Rect { left: T::ZERO, right: T::ZERO, top: T::ZERO, bottom: T::ZERO };
136}
137impl<T: TaffyZero> Rect<T> {
138    /// Returns a Rect where the left, right, top, and bottom values are all the zero value of the contained type
139    /// (e.g. 0.0, Some(0.0), or Dimension::Length(0.0))
140    pub const fn zero() -> Self {
141        zero::<Self>()
142    }
143}
144
145/// Returns the auto value for that type
146pub const fn auto<T: TaffyAuto>() -> T {
147    T::AUTO
148}
149
150/// Trait to abstract over auto values
151pub trait TaffyAuto {
152    /// The auto value for type implementing TaffyAuto
153    const AUTO: Self;
154}
155impl<T: TaffyAuto> TaffyAuto for Option<T> {
156    const AUTO: Option<T> = Some(T::AUTO);
157}
158impl<T: TaffyAuto> TaffyAuto for Point<T> {
159    const AUTO: Point<T> = Point { x: T::AUTO, y: T::AUTO };
160}
161impl<T: TaffyAuto> Point<T> {
162    /// Returns a Point where both the x and y values are the auto value of the contained type
163    /// (e.g. Dimension::Auto or LengthPercentageAuto::Auto)
164    pub const fn auto() -> Self {
165        auto::<Self>()
166    }
167}
168impl<T: TaffyAuto> TaffyAuto for Line<T> {
169    const AUTO: Line<T> = Line { start: T::AUTO, end: T::AUTO };
170}
171impl<T: TaffyAuto> Line<T> {
172    /// Returns a Line where both the start and end values are the auto value of the contained type
173    /// (e.g. Dimension::Auto or LengthPercentageAuto::Auto)
174    pub const fn auto() -> Self {
175        auto::<Self>()
176    }
177}
178impl<T: TaffyAuto> TaffyAuto for Size<T> {
179    const AUTO: Size<T> = Size { width: T::AUTO, height: T::AUTO };
180}
181impl<T: TaffyAuto> Size<T> {
182    /// Returns a Size where both the width and height values are the auto value of the contained type
183    /// (e.g. Dimension::Auto or LengthPercentageAuto::Auto)
184    pub const fn auto() -> Self {
185        auto::<Self>()
186    }
187}
188impl<T: TaffyAuto> TaffyAuto for Rect<T> {
189    const AUTO: Rect<T> = Rect { left: T::AUTO, right: T::AUTO, top: T::AUTO, bottom: T::AUTO };
190}
191impl<T: TaffyAuto> Rect<T> {
192    /// Returns a Rect where the left, right, top, and bottom values are all the auto value of the contained type
193    /// (e.g. Dimension::Auto or LengthPercentageAuto::Auto)
194    pub const fn auto() -> Self {
195        auto::<Self>()
196    }
197}
198
199/// Returns the auto value for that type
200pub const fn min_content<T: TaffyMinContent>() -> T {
201    T::MIN_CONTENT
202}
203
204/// Trait to abstract over min_content values
205pub trait TaffyMinContent {
206    /// The min_content value for type implementing TaffyZero
207    const MIN_CONTENT: Self;
208}
209impl<T: TaffyMinContent> TaffyMinContent for Option<T> {
210    const MIN_CONTENT: Option<T> = Some(T::MIN_CONTENT);
211}
212impl<T: TaffyMinContent> TaffyMinContent for Point<T> {
213    const MIN_CONTENT: Point<T> = Point { x: T::MIN_CONTENT, y: T::MIN_CONTENT };
214}
215impl<T: TaffyMinContent> Point<T> {
216    /// Returns a Point where both the x and y values are the min_content value of the contained type
217    /// (e.g. Dimension::Auto or LengthPercentageAuto::Auto)
218    pub const fn min_content() -> Self {
219        min_content::<Self>()
220    }
221}
222impl<T: TaffyMinContent> TaffyMinContent for Line<T> {
223    const MIN_CONTENT: Line<T> = Line { start: T::MIN_CONTENT, end: T::MIN_CONTENT };
224}
225impl<T: TaffyMinContent> Line<T> {
226    /// Returns a Line where both the start and end values are the min_content value of the contained type
227    /// (e.g. Dimension::Auto or LengthPercentageAuto::Auto)
228    pub const fn min_content() -> Self {
229        min_content::<Self>()
230    }
231}
232impl<T: TaffyMinContent> TaffyMinContent for Size<T> {
233    const MIN_CONTENT: Size<T> = Size { width: T::MIN_CONTENT, height: T::MIN_CONTENT };
234}
235impl<T: TaffyMinContent> Size<T> {
236    /// Returns a Size where both the width and height values are the min_content value of the contained type
237    /// (e.g. Dimension::Auto or LengthPercentageAuto::Auto)
238    pub const fn min_content() -> Self {
239        min_content::<Self>()
240    }
241}
242impl<T: TaffyMinContent> TaffyMinContent for Rect<T> {
243    const MIN_CONTENT: Rect<T> =
244        Rect { left: T::MIN_CONTENT, right: T::MIN_CONTENT, top: T::MIN_CONTENT, bottom: T::MIN_CONTENT };
245}
246impl<T: TaffyMinContent> Rect<T> {
247    /// Returns a Rect where the left, right, top, and bottom values are all the min_content value of the contained type
248    /// (e.g. Dimension::Auto or LengthPercentageAuto::Auto)
249    pub const fn min_content() -> Self {
250        min_content::<Self>()
251    }
252}
253
254/// Returns the auto value for that type
255pub const fn max_content<T: TaffyMaxContent>() -> T {
256    T::MAX_CONTENT
257}
258
259/// Trait to abstract over max_content values
260pub trait TaffyMaxContent {
261    /// The max_content value for type implementing TaffyZero
262    const MAX_CONTENT: Self;
263}
264impl<T: TaffyMaxContent> TaffyMaxContent for Option<T> {
265    const MAX_CONTENT: Option<T> = Some(T::MAX_CONTENT);
266}
267impl<T: TaffyMaxContent> TaffyMaxContent for Point<T> {
268    const MAX_CONTENT: Point<T> = Point { x: T::MAX_CONTENT, y: T::MAX_CONTENT };
269}
270impl<T: TaffyMaxContent> Point<T> {
271    /// Returns a Point where both the x and y values are the max_content value of the contained type
272    /// (e.g. Dimension::Auto or LengthPercentageAuto::Auto)
273    pub const fn max_content() -> Self {
274        max_content::<Self>()
275    }
276}
277impl<T: TaffyMaxContent> TaffyMaxContent for Line<T> {
278    const MAX_CONTENT: Line<T> = Line { start: T::MAX_CONTENT, end: T::MAX_CONTENT };
279}
280impl<T: TaffyMaxContent> Line<T> {
281    /// Returns a Line where both the start and end values are the max_content value of the contained type
282    /// (e.g. Dimension::Auto or LengthPercentageAuto::Auto)
283    pub const fn max_content() -> Self {
284        max_content::<Self>()
285    }
286}
287impl<T: TaffyMaxContent> TaffyMaxContent for Size<T> {
288    const MAX_CONTENT: Size<T> = Size { width: T::MAX_CONTENT, height: T::MAX_CONTENT };
289}
290impl<T: TaffyMaxContent> Size<T> {
291    /// Returns a Size where both the width and height values are the max_content value of the contained type
292    /// (e.g. Dimension::Auto or LengthPercentageAuto::Auto)
293    pub const fn max_content() -> Self {
294        max_content::<Self>()
295    }
296}
297impl<T: TaffyMaxContent> TaffyMaxContent for Rect<T> {
298    const MAX_CONTENT: Rect<T> =
299        Rect { left: T::MAX_CONTENT, right: T::MAX_CONTENT, top: T::MAX_CONTENT, bottom: T::MAX_CONTENT };
300}
301impl<T: TaffyMaxContent> Rect<T> {
302    /// Returns a Rect where the left, right, top, and bottom values are all the max_content value of the contained type
303    /// (e.g. Dimension::Auto or LengthPercentageAuto::Auto)
304    pub const fn max_content() -> Self {
305        max_content::<Self>()
306    }
307}
308
309/// Returns a value of the inferred type which represent a `fit-content(…)` value
310/// with the given argument.
311pub fn fit_content<T: TaffyFitContent>(argument: LengthPercentage) -> T {
312    T::fit_content(argument)
313}
314
315/// Trait to create `fit-content(…)` values from plain numbers
316pub trait TaffyFitContent {
317    /// Converts a LengthPercentage into Self
318    fn fit_content(argument: LengthPercentage) -> Self;
319}
320impl<T: TaffyFitContent> TaffyFitContent for Point<T> {
321    fn fit_content(argument: LengthPercentage) -> Self {
322        Point { x: T::fit_content(argument), y: T::fit_content(argument) }
323    }
324}
325impl<T: TaffyFitContent> Point<T> {
326    /// Returns a Point with x and y set to the same `fit-content(…)` value
327    /// with the given argument.
328    pub fn fit_content(argument: LengthPercentage) -> Self {
329        fit_content(argument)
330    }
331}
332impl<T: TaffyFitContent> TaffyFitContent for Line<T> {
333    fn fit_content(argument: LengthPercentage) -> Self {
334        Line { start: T::fit_content(argument), end: T::fit_content(argument) }
335    }
336}
337impl<T: TaffyFitContent> Line<T> {
338    /// Returns a Line with start and end set to the same `fit-content(…)` value
339    /// with the given argument.
340    pub fn fit_content(argument: LengthPercentage) -> Self {
341        fit_content(argument)
342    }
343}
344impl<T: TaffyFitContent> TaffyFitContent for Size<T> {
345    fn fit_content(argument: LengthPercentage) -> Self {
346        Size { width: T::fit_content(argument), height: T::fit_content(argument) }
347    }
348}
349impl<T: TaffyFitContent> Size<T> {
350    /// Returns a Size where with width and height set to the same `fit-content(…)` value
351    /// with the given argument.
352    pub fn fit_content(argument: LengthPercentage) -> Self {
353        fit_content(argument)
354    }
355}
356impl<T: TaffyFitContent> TaffyFitContent for Rect<T> {
357    fn fit_content(argument: LengthPercentage) -> Self {
358        Rect {
359            left: T::fit_content(argument),
360            right: T::fit_content(argument),
361            top: T::fit_content(argument),
362            bottom: T::fit_content(argument),
363        }
364    }
365}
366impl<T: TaffyFitContent> Rect<T> {
367    /// Returns a Rect where the left, right, top and bottom values are all constant fit_content value of the contained type
368    /// (e.g. 2.1, Some(2.1), or Dimension::Length(2.1))
369    pub fn fit_content(argument: LengthPercentage) -> Self {
370        fit_content(argument)
371    }
372}
373
374/// Returns a value of the inferred type which represent an absolute length
375pub fn length<Input: Into<f32> + Copy, T: FromLength>(value: Input) -> T {
376    T::from_length(value)
377}
378
379/// Trait to create absolute length values from plain numbers
380pub trait FromLength {
381    /// Converts into an `Into<f32>` into Self
382    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self;
383}
384impl FromLength for f32 {
385    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
386        value.into()
387    }
388}
389impl FromLength for Option<f32> {
390    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
391        Some(value.into())
392    }
393}
394impl<T: FromLength> FromLength for Point<T> {
395    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
396        Point { x: T::from_length(value.into()), y: T::from_length(value.into()) }
397    }
398}
399impl<T: FromLength> Point<T> {
400    /// Returns a Point where x and y values are the same given absolute length
401    pub fn length<Input: Into<f32> + Copy>(value: Input) -> Self {
402        length::<Input, Self>(value)
403    }
404}
405impl<T: FromLength> FromLength for Line<T> {
406    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
407        Line { start: T::from_length(value.into()), end: T::from_length(value.into()) }
408    }
409}
410impl<T: FromLength> Line<T> {
411    /// Returns a Line where both the start and end values are the same given absolute length
412    pub fn length<Input: Into<f32> + Copy>(value: Input) -> Self {
413        length::<Input, Self>(value)
414    }
415}
416impl<T: FromLength> FromLength for Size<T> {
417    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
418        Size { width: T::from_length(value.into()), height: T::from_length(value.into()) }
419    }
420}
421impl<T: FromLength> Size<T> {
422    /// Returns a Size where both the width and height values the same given absolute length
423    pub fn length<Input: Into<f32> + Copy>(value: Input) -> Self {
424        length::<Input, Self>(value)
425    }
426}
427impl<T: FromLength> FromLength for Rect<T> {
428    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
429        Rect {
430            left: T::from_length(value.into()),
431            right: T::from_length(value.into()),
432            top: T::from_length(value.into()),
433            bottom: T::from_length(value.into()),
434        }
435    }
436}
437impl<T: FromLength> Rect<T> {
438    /// Returns a Rect where the left, right, top and bottom values are all the same given absolute length
439    pub fn length<Input: Into<f32> + Copy>(value: Input) -> Self {
440        length::<Input, Self>(value)
441    }
442}
443
444/// Returns a value of the inferred type which represent a percentage
445pub fn percent<Input: Into<f32> + Copy, T: FromPercent>(percent: Input) -> T {
446    T::from_percent(percent)
447}
448
449/// Trait to create constant percent values from plain numbers
450pub trait FromPercent {
451    /// Converts into an `Into<f32>` into Self
452    fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self;
453}
454impl FromPercent for f32 {
455    fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
456        percent.into()
457    }
458}
459impl FromPercent for Option<f32> {
460    fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
461        Some(percent.into())
462    }
463}
464impl<T: FromPercent> FromPercent for Point<T> {
465    fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
466        Point { x: T::from_percent(percent.into()), y: T::from_percent(percent.into()) }
467    }
468}
469impl<T: FromPercent> Point<T> {
470    /// Returns a Point where both the x and y values are the constant percent value of the contained type
471    /// (e.g. 2.1, Some(2.1), or Dimension::Length(2.1))
472    pub fn percent<Input: Into<f32> + Copy>(percent_value: Input) -> Self {
473        percent::<Input, Self>(percent_value)
474    }
475}
476impl<T: FromPercent> FromPercent for Line<T> {
477    fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
478        Line { start: T::from_percent(percent.into()), end: T::from_percent(percent.into()) }
479    }
480}
481impl<T: FromPercent> Line<T> {
482    /// Returns a Line where both the start and end values are the constant percent value of the contained type
483    /// (e.g. 2.1, Some(2.1), or Dimension::Length(2.1))
484    pub fn percent<Input: Into<f32> + Copy>(percent_value: Input) -> Self {
485        percent::<Input, Self>(percent_value)
486    }
487}
488impl<T: FromPercent> FromPercent for Size<T> {
489    fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
490        Size { width: T::from_percent(percent.into()), height: T::from_percent(percent.into()) }
491    }
492}
493impl<T: FromPercent> Size<T> {
494    /// Returns a Size where both the width and height values are the constant percent value of the contained type
495    /// (e.g. 2.1, Some(2.1), or Dimension::Length(2.1))
496    pub fn percent<Input: Into<f32> + Copy>(percent_value: Input) -> Self {
497        percent::<Input, Self>(percent_value)
498    }
499}
500impl<T: FromPercent> FromPercent for Rect<T> {
501    fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
502        Rect {
503            left: T::from_percent(percent.into()),
504            right: T::from_percent(percent.into()),
505            top: T::from_percent(percent.into()),
506            bottom: T::from_percent(percent.into()),
507        }
508    }
509}
510impl<T: FromPercent> Rect<T> {
511    /// Returns a Rect where the left, right, top and bottom values are all constant percent value of the contained type
512    /// (e.g. 2.1, Some(2.1), or Dimension::Length(2.1))
513    pub fn percent<Input: Into<f32> + Copy>(percent_value: Input) -> Self {
514        percent::<Input, Self>(percent_value)
515    }
516}
517
518/// Create a `Fraction` track sizing function (`fr` in CSS)
519#[cfg(feature = "grid")]
520pub fn fr<Input: Into<f32> + Copy, T: FromFr>(flex: Input) -> T {
521    T::from_fr(flex)
522}
523
524/// Trait to create constant percent values from plain numbers
525pub trait FromFr {
526    /// Converts into an `Into<f32>` into Self
527    fn from_fr<Input: Into<f32> + Copy>(flex: Input) -> Self;
528}
529
530#[cfg(feature = "grid")]
531#[cfg(test)]
532mod repeat_fn_tests {
533    type S = crate::sys::DefaultCheapStr;
534    use super::repeat;
535    use crate::{
536        style::{GridTemplateComponent, RepetitionCount, TrackSizingFunction},
537        GridTemplateRepetition,
538    };
539
540    const TEST_VEC: Vec<TrackSizingFunction> = Vec::new();
541
542    #[test]
543    fn test_repeat_u16() {
544        assert_eq!(
545            repeat::<_, S>(123, TEST_VEC),
546            GridTemplateComponent::Repeat(GridTemplateRepetition {
547                count: RepetitionCount::Count(123),
548                tracks: TEST_VEC,
549                line_names: Vec::new(),
550            })
551        );
552    }
553
554    #[test]
555    fn test_repeat_auto_fit_str() {
556        assert_eq!(
557            repeat::<_, S>("auto-fit", TEST_VEC),
558            GridTemplateComponent::Repeat(GridTemplateRepetition {
559                count: RepetitionCount::AutoFit,
560                tracks: TEST_VEC,
561                line_names: Vec::new(),
562            })
563        );
564    }
565
566    #[test]
567    fn test_repeat_auto_fill_str() {
568        assert_eq!(
569            repeat::<_, S>("auto-fill", TEST_VEC),
570            GridTemplateComponent::Repeat(GridTemplateRepetition {
571                count: RepetitionCount::AutoFill,
572                tracks: TEST_VEC,
573                line_names: Vec::new(),
574            })
575        );
576    }
577}