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