Skip to main content

sequoia_layout/
style.rs

1//! Per-view layout styles and value sanitizers.
2
3use crate::container::{Align, Axis};
4use crate::overflow::Overflow;
5use sequoia_core::{Edges, Length, Size};
6
7/// Positioning mode for a view.
8#[derive(Clone, Copy, Debug, Default, PartialEq)]
9#[non_exhaustive]
10pub enum Position {
11    /// Participate in normal flow.
12    #[default]
13    Flow,
14    /// Position relative to the container's padding box.
15    Absolute(Inset),
16}
17
18/// Absolute offsets from each edge of the containing block.
19///
20/// Each field is optional; `None` means "auto" and lets the layout choose.
21#[derive(Clone, Copy, Debug, Default, PartialEq)]
22#[non_exhaustive]
23pub struct Inset {
24    /// Offset from the left edge, or `None` for auto.
25    pub left: Option<f32>,
26    /// Offset from the top edge, or `None` for auto.
27    pub top: Option<f32>,
28    /// Offset from the right edge, or `None` for auto.
29    pub right: Option<f32>,
30    /// Offset from the bottom edge, or `None` for auto.
31    pub bottom: Option<f32>,
32}
33
34impl Inset {
35    /// An inset with every side set to auto.
36    pub const AUTO: Self = Self {
37        left: None,
38        top: None,
39        right: None,
40        bottom: None,
41    };
42
43    /// Creates an inset with explicit left and top offsets.
44    pub fn xy(left: f32, top: f32) -> Self {
45        Self {
46            left: Some(left),
47            top: Some(top),
48            right: None,
49            bottom: None,
50        }
51    }
52
53    /// Builds an inset from each side, `None` meaning "auto".
54    pub const fn only(
55        left: Option<f32>,
56        top: Option<f32>,
57        right: Option<f32>,
58        bottom: Option<f32>,
59    ) -> Self {
60        Self {
61            left,
62            top,
63            right,
64            bottom,
65        }
66    }
67}
68
69/// Optional indents from each edge of a container's content box.
70///
71/// An indent replaces the container gap on the boundary it touches instead of
72/// being added to it. Along the main axis, [`Length::Fill`] indents share free
73/// space like flexible spacers, so `indent_left(Length::Fill(1.0))` can push a
74/// child to the end of a row without adding a separate [`crate::FlexItem`].
75#[derive(Clone, Copy, Debug, Default, PartialEq)]
76pub struct Indents {
77    /// Indent from the left edge, or `None` to use the default.
78    pub left: Option<Length>,
79    /// Indent from the top edge, or `None` to use the default.
80    pub top: Option<Length>,
81    /// Indent from the right edge, or `None` to use the default.
82    pub right: Option<Length>,
83    /// Indent from the bottom edge, or `None` to use the default.
84    pub bottom: Option<Length>,
85}
86
87impl Indents {
88    /// Indents that leave every side unset.
89    pub const NONE: Self = Self {
90        left: None,
91        top: None,
92        right: None,
93        bottom: None,
94    };
95
96    /// Sets the same indent on every side.
97    pub fn all(v: f32) -> Self {
98        let v = Length::Px(sanitize_indent(v));
99        Self {
100            left: Some(v),
101            top: Some(v),
102            right: Some(v),
103            bottom: Some(v),
104        }
105    }
106
107    /// Sets horizontal and vertical indents.
108    pub fn xy(x: f32, y: f32) -> Self {
109        Self {
110            left: Some(Length::Px(sanitize_indent(x))),
111            top: Some(Length::Px(sanitize_indent(y))),
112            right: Some(Length::Px(sanitize_indent(x))),
113            bottom: Some(Length::Px(sanitize_indent(y))),
114        }
115    }
116
117    /// Sets horizontal and vertical indents.
118    pub fn axes(horizontal: f32, vertical: f32) -> Self {
119        Self::xy(horizontal, vertical)
120    }
121
122    /// Sets the left and right indents.
123    pub fn horizontal(v: f32) -> Self {
124        let v = Length::Px(sanitize_indent(v));
125        Self {
126            left: Some(v),
127            right: Some(v),
128            top: None,
129            bottom: None,
130        }
131    }
132
133    /// Sets the top and bottom indents.
134    pub fn vertical(v: f32) -> Self {
135        let v = Length::Px(sanitize_indent(v));
136        Self {
137            left: None,
138            right: None,
139            top: Some(v),
140            bottom: Some(v),
141        }
142    }
143
144    /// Builds indents from each side, `None` meaning unset.
145    pub const fn only(
146        left: Option<Length>,
147        top: Option<Length>,
148        right: Option<Length>,
149        bottom: Option<Length>,
150    ) -> Self {
151        Self {
152            left,
153            top,
154            right,
155            bottom,
156        }
157    }
158
159    /// Returns `true` if no side has an indent.
160    pub fn is_none(self) -> bool {
161        self.left.is_none() && self.top.is_none() && self.right.is_none() && self.bottom.is_none()
162    }
163}
164
165/// Layout properties for a single view.
166#[derive(Clone, Copy, Debug, PartialEq)]
167pub struct LayoutStyle {
168    /// Requested width.
169    pub width: Length,
170    /// Requested height.
171    pub height: Length,
172    /// Space between the border and the content.
173    pub padding: Edges,
174    /// Space around the view outside its border.
175    pub margin: Edges,
176    /// Lower bound for the resolved width.
177    pub min_width: f32,
178    /// Upper bound for the resolved width.
179    pub max_width: f32,
180    /// Lower bound for the resolved height.
181    pub min_height: f32,
182    /// Upper bound for the resolved height.
183    pub max_height: f32,
184    /// Grow weight along the parent's main axis.
185    pub flex: f32,
186    /// Shrink weight along the parent's main axis.
187    pub flex_shrink: f32,
188    /// Optional basis used before flex distribution.
189    pub flex_basis: Option<Length>,
190    /// Cross-axis alignment override; `None` inherits from the container.
191    pub align_self: Option<Align>,
192    /// Preferred width-to-height ratio.
193    pub aspect_ratio: Option<f32>,
194    /// Indent from the top edge; replaces the container gap when set.
195    pub indent_top: Option<Length>,
196    /// Indent from the bottom edge; replaces the container gap when set.
197    pub indent_bottom: Option<Length>,
198    /// Indent from the left edge; replaces the container gap when set.
199    pub indent_left: Option<Length>,
200    /// Indent from the right edge; replaces the container gap when set.
201    pub indent_right: Option<Length>,
202    /// Positioning mode.
203    pub position: Position,
204    /// Overflow handling for content that exceeds the view.
205    pub overflow: Overflow,
206    /// Whether the view participates in hit testing.
207    pub hit_testable: bool,
208}
209
210impl Default for LayoutStyle {
211    fn default() -> Self {
212        Self {
213            width: Length::Fit,
214            height: Length::Fit,
215            padding: Edges::ZERO,
216            margin: Edges::ZERO,
217            min_width: 0.0,
218            max_width: f32::INFINITY,
219            min_height: 0.0,
220            max_height: f32::INFINITY,
221            flex: 0.0,
222            flex_shrink: 1.0,
223            flex_basis: None,
224            align_self: None,
225            aspect_ratio: None,
226            indent_top: None,
227            indent_bottom: None,
228            indent_left: None,
229            indent_right: None,
230            position: Position::Flow,
231            overflow: Overflow::Visible,
232            hit_testable: true,
233        }
234    }
235}
236
237impl LayoutStyle {
238    /// Sets the requested width.
239    pub fn width(mut self, width: Length) -> Self {
240        self.width = width;
241        self
242    }
243
244    /// Sets the requested height.
245    pub fn height(mut self, height: Length) -> Self {
246        self.height = height;
247        self
248    }
249
250    /// Sets both dimensions to fit their content.
251    pub fn fit(mut self) -> Self {
252        self.width = Length::Fit;
253        self.height = Length::Fit;
254        self
255    }
256
257    /// Enables or disables hit testing for this view.
258    pub fn hit_testable(mut self, on: bool) -> Self {
259        self.hit_testable = on;
260        self
261    }
262
263    /// Sets the padding on every side.
264    pub fn padding(mut self, padding: impl Into<Edges>) -> Self {
265        self.padding = padding.into();
266        self
267    }
268
269    /// Alias for [`LayoutStyle::padding`].
270    pub fn pad(self, padding: impl Into<Edges>) -> Self {
271        self.padding(padding)
272    }
273
274    /// Sets the horizontal and vertical padding.
275    pub fn padding_xy(self, x: f32, y: f32) -> Self {
276        self.padding(Edges::xy(x, y))
277    }
278
279    /// Sets the left and right padding.
280    pub fn padding_x(mut self, x: f32) -> Self {
281        self.padding.left = x;
282        self.padding.right = x;
283        self
284    }
285
286    /// Sets the top and bottom padding.
287    pub fn padding_y(mut self, y: f32) -> Self {
288        self.padding.top = y;
289        self.padding.bottom = y;
290        self
291    }
292
293    /// Sets the left padding.
294    pub fn padding_left(mut self, v: f32) -> Self {
295        self.padding.left = v;
296        self
297    }
298
299    /// Sets the right padding.
300    pub fn padding_right(mut self, v: f32) -> Self {
301        self.padding.right = v;
302        self
303    }
304
305    /// Sets the top padding.
306    pub fn padding_top(mut self, v: f32) -> Self {
307        self.padding.top = v;
308        self
309    }
310
311    /// Sets the bottom padding.
312    pub fn padding_bottom(mut self, v: f32) -> Self {
313        self.padding.bottom = v;
314        self
315    }
316
317    /// Sets the minimum width.
318    pub fn min_width(mut self, v: f32) -> Self {
319        self.min_width = sanitize_min(v);
320        self
321    }
322
323    /// Sets the maximum width.
324    pub fn max_width(mut self, v: f32) -> Self {
325        self.max_width = sanitize_max(v);
326        self
327    }
328
329    /// Sets the minimum height.
330    pub fn min_height(mut self, v: f32) -> Self {
331        self.min_height = sanitize_min(v);
332        self
333    }
334
335    /// Sets the maximum height.
336    pub fn max_height(mut self, v: f32) -> Self {
337        self.max_height = sanitize_max(v);
338        self
339    }
340
341    /// Clamps a width to this style's minimum and maximum.
342    pub fn clamp_width(self, w: f32) -> f32 {
343        clamp_axis(w, self.min_width, self.max_width)
344    }
345
346    /// Clamps a height to this style's minimum and maximum.
347    pub fn clamp_height(self, h: f32) -> f32 {
348        clamp_axis(h, self.min_height, self.max_height)
349    }
350
351    /// Clamps a size to this style's minimum and maximum.
352    pub fn clamp_size(self, size: sequoia_core::Size) -> sequoia_core::Size {
353        sequoia_core::Size::new(self.clamp_width(size.width), self.clamp_height(size.height))
354    }
355
356    /// Sets the margin on every side.
357    pub fn margin(mut self, margin: impl Into<Edges>) -> Self {
358        let m = margin.into();
359        self.margin = Edges::only(
360            sanitize_edge(m.left),
361            sanitize_edge(m.top),
362            sanitize_edge(m.right),
363            sanitize_edge(m.bottom),
364        );
365        self
366    }
367
368    /// Sets the horizontal and vertical margin.
369    pub fn margin_xy(self, x: f32, y: f32) -> Self {
370        self.margin(Edges::xy(x, y))
371    }
372
373    /// Sets the left and right margin.
374    pub fn margin_x(mut self, x: f32) -> Self {
375        let x = sanitize_edge(x);
376        self.margin.left = x;
377        self.margin.right = x;
378        self
379    }
380
381    /// Sets the top and bottom margin.
382    pub fn margin_y(mut self, y: f32) -> Self {
383        let y = sanitize_edge(y);
384        self.margin.top = y;
385        self.margin.bottom = y;
386        self
387    }
388
389    /// Sets the left margin.
390    pub fn margin_left(mut self, v: f32) -> Self {
391        self.margin.left = sanitize_edge(v);
392        self
393    }
394
395    /// Sets the right margin.
396    pub fn margin_right(mut self, v: f32) -> Self {
397        self.margin.right = sanitize_edge(v);
398        self
399    }
400
401    /// Sets the top margin.
402    pub fn margin_top(mut self, v: f32) -> Self {
403        self.margin.top = sanitize_edge(v);
404        self
405    }
406
407    /// Sets the bottom margin.
408    pub fn margin_bottom(mut self, v: f32) -> Self {
409        self.margin.bottom = sanitize_edge(v);
410        self
411    }
412
413    /// Sets the flex grow weight.
414    pub fn flex(mut self, weight: f32) -> Self {
415        self.flex = sanitize_flex(weight);
416        self
417    }
418
419    /// Sets the flex grow weight.
420    pub fn flex_grow(mut self, weight: f32) -> Self {
421        self.flex = sanitize_flex(weight);
422        self
423    }
424
425    /// Sets the flex shrink weight.
426    pub fn flex_shrink(mut self, shrink: f32) -> Self {
427        self.flex_shrink = sanitize_shrink(shrink);
428        self
429    }
430
431    /// Sets the grow weight, shrink weight, and basis together.
432    pub fn flex_full(mut self, grow: f32, shrink: f32, basis: Option<Length>) -> Self {
433        self.flex = sanitize_flex(grow);
434        self.flex_shrink = sanitize_shrink(shrink);
435        self.flex_basis = basis;
436        self
437    }
438
439    /// Sets the flex basis.
440    pub fn flex_basis(mut self, basis: Option<Length>) -> Self {
441        self.flex_basis = basis;
442        self
443    }
444
445    /// Clears the flex basis.
446    pub fn no_basis(mut self) -> Self {
447        self.flex_basis = None;
448        self
449    }
450
451    /// Sets the flex grow weight to one.
452    pub fn expand(self) -> Self {
453        self.flex(1.0)
454    }
455
456    /// Returns the sanitized flex grow weight.
457    pub fn flex_weight(self) -> f32 {
458        sanitize_flex(self.flex)
459    }
460
461    /// Returns the sanitized flex grow weight.
462    pub fn flex_grow_weight(self) -> f32 {
463        sanitize_flex(self.flex)
464    }
465
466    /// Returns the sanitized flex shrink weight.
467    pub fn flex_shrink_weight(self) -> f32 {
468        sanitize_shrink(self.flex_shrink)
469    }
470
471    /// Sets the cross-axis alignment override.
472    pub fn align_self(mut self, align: Option<Align>) -> Self {
473        self.align_self = align;
474        self
475    }
476
477    /// Sets an explicit cross-axis alignment override.
478    pub fn align_self_value(mut self, align: Align) -> Self {
479        self.align_self = Some(align);
480        self
481    }
482
483    /// Clears the alignment override so the container's alignment is used.
484    pub fn auto_align(mut self) -> Self {
485        self.align_self = None;
486        self
487    }
488
489    /// Resolves the effective alignment against a container alignment.
490    pub fn effective_align(self, container: Align) -> Align {
491        Align::resolve(self.align_self, container)
492    }
493
494    /// Sets the aspect ratio, ignoring invalid values.
495    pub fn aspect_ratio(mut self, ratio: Option<f32>) -> Self {
496        self.aspect_ratio = sanitize_ratio(ratio);
497        self
498    }
499
500    /// Sets the aspect ratio to `ratio`.
501    pub fn aspect(mut self, ratio: f32) -> Self {
502        self.aspect_ratio = sanitize_ratio(Some(ratio));
503        self
504    }
505
506    /// Clears the aspect ratio.
507    pub fn no_aspect(mut self) -> Self {
508        self.aspect_ratio = None;
509        self
510    }
511
512    /// Sets the positioning mode.
513    pub fn position(mut self, position: Position) -> Self {
514        self.position = position;
515        self
516    }
517
518    /// Positions the view absolutely with auto insets.
519    pub fn absolute(mut self) -> Self {
520        self.position = Position::Absolute(Inset::AUTO);
521        self
522    }
523
524    /// Positions the view absolutely at the given left and top offsets.
525    pub fn absolute_at(mut self, left: f32, top: f32) -> Self {
526        self.position = Position::Absolute(Inset::xy(left, top));
527        self
528    }
529
530    /// Positions the view absolutely with the given insets.
531    pub fn absolute_inset(mut self, inset: Inset) -> Self {
532        self.position = Position::Absolute(inset);
533        self
534    }
535
536    /// Returns `true` if the view is positioned absolutely.
537    pub fn is_absolute(self) -> bool {
538        matches!(self.position, Position::Absolute(_))
539    }
540
541    /// Returns the absolute insets, or `None` for flow positioning.
542    pub fn absolute_inset_of(self) -> Option<Inset> {
543        match self.position {
544            Position::Absolute(inset) => Some(inset),
545            _ => None,
546        }
547    }
548
549    /// Returns the horizontal or vertical margin for the given axis.
550    pub fn margin_main(self, axis: Axis) -> f32 {
551        match axis {
552            Axis::Horizontal => self.margin.left + self.margin.right,
553            Axis::Vertical => self.margin.top + self.margin.bottom,
554        }
555    }
556
557    /// Returns the cross-axis margin for the given main axis.
558    pub fn margin_cross(self, axis: Axis) -> f32 {
559        match axis {
560            Axis::Horizontal => self.margin.top + self.margin.bottom,
561            Axis::Vertical => self.margin.left + self.margin.right,
562        }
563    }
564
565    /// Applies the aspect ratio to the size, deriving either auto axis.
566    pub fn apply_aspect(self, mut w: f32, mut h: f32, w_auto: bool, h_auto: bool) -> (f32, f32) {
567        let Some(r) = sanitize_ratio(self.aspect_ratio) else {
568            return (w, h);
569        };
570        if r <= 0.0 {
571            return (w, h);
572        }
573        match (w_auto, h_auto) {
574            (true, false) => {
575                w = (h * r).max(0.0);
576            }
577            (false, true) => {
578                h = if r != 0.0 { (w / r).max(0.0) } else { h };
579            }
580            (true, true) => {
581                if w <= 0.0 && h > 0.0 {
582                    w = (h * r).max(0.0);
583                } else if h <= 0.0 && w > 0.0 {
584                    h = (w / r).max(0.0);
585                }
586            }
587            (false, false) => {}
588        }
589        (w, h)
590    }
591
592    /// Expands an inner size by this style's margins.
593    pub fn outer_size(self, inner: Size) -> Size {
594        Size::new(
595            inner.width + self.margin.horizontal_sum(),
596            inner.height + self.margin.vertical_sum(),
597        )
598    }
599
600    /// Returns the minimum size along the given axis.
601    pub fn min_main(self, axis: Axis) -> f32 {
602        sanitize_min(match axis {
603            Axis::Horizontal => self.min_width,
604            Axis::Vertical => self.min_height,
605        })
606    }
607
608    /// Returns the maximum size along the given axis.
609    pub fn max_main(self, axis: Axis) -> f32 {
610        sanitize_max(match axis {
611            Axis::Horizontal => self.max_width,
612            Axis::Vertical => self.max_height,
613        })
614    }
615
616    /// Clamps a value to the minimum and maximum along the given axis.
617    pub fn clamp_main(self, value: f32, axis: Axis) -> f32 {
618        clamp_axis(value, self.min_main(axis), self.max_main(axis))
619    }
620
621    /// Sets the overflow behavior.
622    pub fn overflow(mut self, overflow: Overflow) -> Self {
623        self.overflow = overflow;
624        self
625    }
626
627    /// Sets the top indent.
628    pub fn indent_top(mut self, v: impl Into<Length>) -> Self {
629        self.indent_top = Some(sanitize_indent_length(v.into()));
630        self
631    }
632
633    /// Sets the bottom indent.
634    pub fn indent_bottom(mut self, v: impl Into<Length>) -> Self {
635        self.indent_bottom = Some(sanitize_indent_length(v.into()));
636        self
637    }
638
639    /// Sets the left indent.
640    pub fn indent_left(mut self, v: impl Into<Length>) -> Self {
641        self.indent_left = Some(sanitize_indent_length(v.into()));
642        self
643    }
644
645    /// Sets the right indent.
646    pub fn indent_right(mut self, v: impl Into<Length>) -> Self {
647        self.indent_right = Some(sanitize_indent_length(v.into()));
648        self
649    }
650
651    /// Clears the top indent.
652    pub fn clear_indent_top(mut self) -> Self {
653        self.indent_top = None;
654        self
655    }
656
657    /// Clears the bottom indent.
658    pub fn clear_indent_bottom(mut self) -> Self {
659        self.indent_bottom = None;
660        self
661    }
662
663    /// Clears the left indent.
664    pub fn clear_indent_left(mut self) -> Self {
665        self.indent_left = None;
666        self
667    }
668
669    /// Clears the right indent.
670    pub fn clear_indent_right(mut self) -> Self {
671        self.indent_right = None;
672        self
673    }
674
675    /// Clears every indent.
676    pub fn clear_indents(mut self) -> Self {
677        self.indent_top = None;
678        self.indent_bottom = None;
679        self.indent_left = None;
680        self.indent_right = None;
681        self
682    }
683
684    /// Applies the set sides from `indents`.
685    pub fn indent(mut self, indents: Indents) -> Self {
686        if let Some(v) = indents.top {
687            self.indent_top = Some(sanitize_indent_length(v));
688        }
689        if let Some(v) = indents.bottom {
690            self.indent_bottom = Some(sanitize_indent_length(v));
691        }
692        if let Some(v) = indents.left {
693            self.indent_left = Some(sanitize_indent_length(v));
694        }
695        if let Some(v) = indents.right {
696            self.indent_right = Some(sanitize_indent_length(v));
697        }
698        self
699    }
700
701    /// Sets the same indent on every side.
702    pub fn indent_all(mut self, v: f32) -> Self {
703        let v = Length::Px(sanitize_indent(v));
704        self.indent_top = Some(v);
705        self.indent_bottom = Some(v);
706        self.indent_left = Some(v);
707        self.indent_right = Some(v);
708        self
709    }
710
711    /// Sets the horizontal and vertical indents.
712    pub fn indent_xy(mut self, x: f32, y: f32) -> Self {
713        let x = Length::Px(sanitize_indent(x));
714        let y = Length::Px(sanitize_indent(y));
715        self.indent_left = Some(x);
716        self.indent_right = Some(x);
717        self.indent_top = Some(y);
718        self.indent_bottom = Some(y);
719        self
720    }
721
722    /// Sets the horizontal and vertical indents.
723    pub fn indent_axes(self, horizontal: f32, vertical: f32) -> Self {
724        self.indent_xy(horizontal, vertical)
725    }
726
727    /// Returns the indent at the main-axis start for the given axis.
728    pub fn indent_main_start(self, axis: Axis) -> Option<Length> {
729        match axis {
730            Axis::Horizontal => self.indent_left,
731            Axis::Vertical => self.indent_top,
732        }
733    }
734
735    /// Returns the indent at the main-axis end for the given axis.
736    pub fn indent_main_end(self, axis: Axis) -> Option<Length> {
737        match axis {
738            Axis::Horizontal => self.indent_right,
739            Axis::Vertical => self.indent_bottom,
740        }
741    }
742
743    /// Returns the indent at the cross-axis start for the given main axis.
744    pub fn indent_cross_start(self, axis: Axis) -> Option<Length> {
745        match axis {
746            Axis::Horizontal => self.indent_top,
747            Axis::Vertical => self.indent_left,
748        }
749    }
750
751    /// Returns the indent at the cross-axis end for the given main axis.
752    pub fn indent_cross_end(self, axis: Axis) -> Option<Length> {
753        match axis {
754            Axis::Horizontal => self.indent_bottom,
755            Axis::Vertical => self.indent_right,
756        }
757    }
758
759    /// Collects the four optional indents.
760    pub fn indents(self) -> Indents {
761        Indents {
762            left: self.indent_left,
763            top: self.indent_top,
764            right: self.indent_right,
765            bottom: self.indent_bottom,
766        }
767    }
768}
769
770/// Clamps a flex grow weight to a finite, non-negative value.
771pub fn sanitize_flex(v: f32) -> f32 {
772    if v.is_finite() && v > 0.0 { v } else { 0.0 }
773}
774
775/// Clamps a flex shrink weight; NaN becomes one and invalid values zero.
776pub fn sanitize_shrink(v: f32) -> f32 {
777    if v.is_finite() && v >= 0.0 {
778        v
779    } else if v.is_nan() {
780        1.0
781    } else {
782        0.0
783    }
784}
785
786/// Keeps an aspect ratio only when it is finite and positive.
787pub fn sanitize_ratio(v: Option<f32>) -> Option<f32> {
788    match v {
789        Some(r) if r.is_finite() && r > 0.0 => Some(r),
790        _ => None,
791    }
792}
793
794fn sanitize_edge(v: f32) -> f32 {
795    if v.is_finite() { v.max(0.0) } else { 0.0 }
796}
797
798/// Clamps an indent to a finite, non-negative value.
799pub fn sanitize_indent(v: f32) -> f32 {
800    if v.is_finite() { v.max(0.0) } else { 0.0 }
801}
802
803/// Sanitizes a length used as an indent.
804pub fn sanitize_indent_length(v: Length) -> Length {
805    match v {
806        Length::Px(v) => Length::Px(sanitize_indent(v)),
807        Length::Percent(v) => Length::Percent(sanitize_indent(v)),
808        Length::Fill(v) => Length::Fill(sanitize_flex(v)),
809        Length::Fit => Length::Px(0.0),
810    }
811}
812
813fn sanitize_min(v: f32) -> f32 {
814    if !v.is_finite() || v < 0.0 { 0.0 } else { v }
815}
816
817fn sanitize_max(v: f32) -> f32 {
818    if v.is_nan() {
819        f32::INFINITY
820    } else if v.is_infinite() {
821        if v > 0.0 { f32::INFINITY } else { 0.0 }
822    } else {
823        v.max(0.0)
824    }
825}
826
827fn clamp_axis(value: f32, min: f32, max: f32) -> f32 {
828    let (min, max) = if min > max { (max, max) } else { (min, max) };
829    if value < min {
830        min
831    } else if value > max {
832        max
833    } else {
834        value
835    }
836}