⎇ devin/1784819829-clamp-overlarge-grids
src/style/available_space.rs
Ñawpa willay
Pi imata llamkarqan
ch'uya
Change numeric style helpers to accept Into<f64> to avoid f32-fallback lint (#974) (#983)
1//! Style type for representing available space as a sizing constraint
2use crate::{
3 prelude::{FromLength, TaffyMaxContent, TaffyMinContent, TaffyZero},
4 sys::abs,
5 Size,
6};
7
8#[cfg(feature = "parse")]
9use crate::util::parse::{from_str_from_css, CssParseResult, FromCss, Parser, Token};
10
11/// The amount of space available to a node in a given axis
12/// <https://www.w3.org/TR/css-sizing-3/#available>
13#[derive(Copy, Clone, Debug, PartialEq)]
14#[cfg_attr(feature = "serde", derive(Serialize))]
15pub enum AvailableSpace {
16 /// The amount of space available is the specified number of pixels
17 Definite(f32),
18 /// The amount of space available is indefinite and the node should be laid out under a min-content constraint
19 MinContent,
20 /// The amount of space available is indefinite and the node should be laid out under a max-content constraint
21 MaxContent,
22}
23impl TaffyZero for AvailableSpace {
24 const ZERO: Self = Self::Definite(0.0);
25}
26impl TaffyMaxContent for AvailableSpace {
27 const MAX_CONTENT: Self = Self::MaxContent;
28}
29impl TaffyMinContent for AvailableSpace {
30 const MIN_CONTENT: Self = Self::MinContent;
31}
32impl FromLength for AvailableSpace {
33 fn from_length<Input: Into<f64> + Copy>(value: Input) -> Self {
34 Self::Definite(value.into() as f32)
35 }
36}
37
38#[cfg(feature = "parse")]
39impl FromCss for AvailableSpace {
40 fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
41 match parser.next()?.clone() {
42 Token::Number { value, .. } if value >= 0.0 => Ok(Self::Definite(value)),
43 Token::Dimension { value, .. } if value >= 0.0 => Ok(Self::Definite(value)),
44 Token::Ident(ident) if ident == "max-content" => Ok(Self::MaxContent),
45 Token::Ident(ident) if ident == "min-content" => Ok(Self::MinContent),
46 token => Err(parser.new_unexpected_token_error(token))?,
47 }
48 }
49}
50#[cfg(feature = "parse")]
51from_str_from_css!(AvailableSpace);
52
53impl AvailableSpace {
54 /// Returns true for definite values, else false
55 pub const fn is_definite(self) -> bool {
56 matches!(self, AvailableSpace::Definite(_))
57 }
58
59 /// Convert to Option
60 /// Definite values become Some(value). Constraints become None.
61 pub const fn into_option(self) -> Option<f32> {
62 match self {
63 AvailableSpace::Definite(value) => Some(value),
64 _ => None,
65 }
66 }
67
68 /// Return the definite value or a default value
69 pub fn unwrap_or(self, default: f32) -> f32 {
70 self.into_option().unwrap_or(default)
71 }
72
73 /// Return the definite value. Panic is the value is not definite.
74 #[track_caller]
75 pub fn unwrap(self) -> f32 {
76 self.into_option().unwrap()
77 }
78
79 /// Return self if definite or a default value
80 pub fn or(self, default: AvailableSpace) -> AvailableSpace {
81 match self {
82 AvailableSpace::Definite(_) => self,
83 _ => default,
84 }
85 }
86
87 /// Return self if definite or a the result of the default value callback
88 pub fn or_else(self, default_cb: impl FnOnce() -> AvailableSpace) -> AvailableSpace {
89 match self {
90 AvailableSpace::Definite(_) => self,
91 _ => default_cb(),
92 }
93 }
94
95 /// Return the definite value or the result of the default value callback
96 pub fn unwrap_or_else(self, default_cb: impl FnOnce() -> f32) -> f32 {
97 self.into_option().unwrap_or_else(default_cb)
98 }
99
100 /// If passed value is Some then return AvailableSpace::Definite containing that value, else return self
101 pub fn maybe_set(self, value: Option<f32>) -> AvailableSpace {
102 match value {
103 Some(value) => AvailableSpace::Definite(value),
104 None => self,
105 }
106 }
107
108 /// If passed value is Some then return AvailableSpace::Definite containing that value, else return self
109 pub fn map_definite_value(self, map_function: impl FnOnce(f32) -> f32) -> AvailableSpace {
110 match self {
111 AvailableSpace::Definite(value) => AvailableSpace::Definite(map_function(value)),
112 _ => self,
113 }
114 }
115
116 /// Compute free_space given the passed used_space
117 pub fn compute_free_space(&self, used_space: f32) -> f32 {
118 match self {
119 AvailableSpace::MaxContent => f32::INFINITY,
120 AvailableSpace::MinContent => 0.0,
121 AvailableSpace::Definite(available_space) => available_space - used_space,
122 }
123 }
124
125 /// Compare equality with another AvailableSpace, treating definite values
126 /// that are within f32::EPSILON of each other as equal
127 pub fn is_roughly_equal(self, other: AvailableSpace) -> bool {
128 use AvailableSpace::*;
129 match (self, other) {
130 (Definite(a), Definite(b)) => abs(a - b) < f32::EPSILON,
131 (MinContent, MinContent) => true,
132 (MaxContent, MaxContent) => true,
133 _ => false,
134 }
135 }
136}
137
138impl From<f32> for AvailableSpace {
139 fn from(value: f32) -> Self {
140 Self::Definite(value)
141 }
142}
143
144impl From<Option<f32>> for AvailableSpace {
145 fn from(option: Option<f32>) -> Self {
146 match option {
147 Some(value) => Self::Definite(value),
148 None => Self::MaxContent,
149 }
150 }
151}
152
153impl Size<AvailableSpace> {
154 /// Convert `Size<AvailableSpace>` into `Size<Option<f32>>`
155 pub fn into_options(self) -> Size<Option<f32>> {
156 Size { width: self.width.into_option(), height: self.height.into_option() }
157 }
158
159 /// If passed value is Some then return AvailableSpace::Definite containing that value, else return self
160 pub fn maybe_set(self, value: Size<Option<f32>>) -> Size<AvailableSpace> {
161 Size { width: self.width.maybe_set(value.width), height: self.height.maybe_set(value.height) }
162 }
163}