torin/values/
size.rs

1use std::{
2    fmt::Debug,
3    hash::Hash,
4    sync::Arc,
5};
6
7pub use euclid::Rect;
8
9use crate::{
10    geometry::Length,
11    measure::Phase,
12    scaled::Scaled,
13};
14
15pub struct SizeFnContext {
16    pub parent: f32,
17    pub available_parent: f32,
18    pub parent_margin: f32,
19    pub root: f32,
20    pub phase: Phase,
21}
22
23#[cfg(feature = "serde")]
24pub use serde::*;
25
26#[derive(Clone)]
27pub struct SizeFn(Arc<dyn Fn(SizeFnContext) -> Option<f32> + Sync + Send>, u64);
28
29#[cfg(feature = "serde")]
30impl Serialize for SizeFn {
31    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
32    where
33        S: Serializer,
34    {
35        serializer.serialize_str("Fn")
36    }
37}
38
39#[cfg(feature = "serde")]
40impl<'de> Deserialize<'de> for SizeFn {
41    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
42    where
43        D: Deserializer<'de>,
44    {
45        struct FnVisitor;
46        use serde::de::Visitor;
47
48        impl Visitor<'_> for FnVisitor {
49            type Value = SizeFn;
50
51            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
52                formatter.write_str("\"Fn\"")
53            }
54
55            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
56            where
57                E: de::Error,
58            {
59                if v == "Fn" {
60                    Ok(SizeFn(Arc::new(|_ctx| None), 0))
61                } else {
62                    Err(E::custom(format!("expected \"Fn\", got {v}")))
63                }
64            }
65        }
66
67        deserializer.deserialize_str(FnVisitor)
68    }
69}
70
71impl SizeFn {
72    pub fn new(func: impl Fn(SizeFnContext) -> Option<f32> + 'static + Sync + Send) -> Self {
73        Self(Arc::new(func), 0)
74    }
75
76    pub fn new_data<D: Hash>(
77        func: impl Fn(SizeFnContext) -> Option<f32> + 'static + Sync + Send,
78        data: &D,
79    ) -> Self {
80        use std::hash::Hasher;
81        let mut hasher = std::hash::DefaultHasher::default();
82        data.hash(&mut hasher);
83        Self(Arc::new(func), hasher.finish())
84    }
85
86    pub fn call(&self, context: SizeFnContext) -> Option<f32> {
87        (self.0)(context)
88    }
89}
90
91impl Debug for SizeFn {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        f.write_str("SizeFn")
94    }
95}
96
97impl PartialEq for SizeFn {
98    fn eq(&self, other: &Self) -> bool {
99        self.1 == other.1
100    }
101}
102
103#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
104#[derive(PartialEq, Clone, Debug)]
105pub enum Size {
106    Inner,
107    Fill,
108    FillMinimum,
109    Percentage(Length),
110    Pixels(Length),
111    RootPercentage(Length),
112    Fn(Box<SizeFn>),
113    Flex(Length),
114}
115
116impl Default for Size {
117    fn default() -> Self {
118        Self::Inner
119    }
120}
121
122impl Size {
123    pub(crate) fn flex_grow(&self) -> Option<Length> {
124        match self {
125            Self::Flex(f) => Some(*f),
126            _ => None,
127        }
128    }
129
130    pub(crate) fn is_flex(&self) -> bool {
131        matches!(self, Self::Flex(_))
132    }
133
134    pub(crate) fn inner_sized(&self) -> bool {
135        matches!(self, Self::Inner | Self::FillMinimum)
136    }
137
138    pub fn pretty(&self) -> String {
139        match self {
140            Self::Inner => "auto".to_string(),
141            Self::Pixels(s) => format!("{}", s.get()),
142            Self::Fn(_) => "Fn".to_string(),
143            Self::Percentage(p) => format!("{}%", p.get()),
144            Self::Fill => "fill".to_string(),
145            Self::FillMinimum => "fill-min".to_string(),
146            Self::RootPercentage(p) => format!("{}% of root", p.get()),
147            Self::Flex(f) => format!("flex({})", f.get()),
148        }
149    }
150
151    pub(crate) fn eval(
152        &self,
153        parent: f32,
154        available_parent: f32,
155        parent_margin: f32,
156        root: f32,
157        phase: Phase,
158    ) -> Option<f32> {
159        match self {
160            Self::Pixels(px) => Some(px.get() + parent_margin),
161            Self::Percentage(per) => Some(parent / 100.0 * per.get()),
162            Self::Fill => Some(available_parent),
163            Self::RootPercentage(per) => Some(root / 100.0 * per.get()),
164            Self::Flex(_) | Self::FillMinimum if phase == Phase::Final => Some(available_parent),
165            Self::Fn(f) => f.call(SizeFnContext {
166                parent,
167                available_parent,
168                parent_margin,
169                root,
170                phase,
171            }),
172            _ => None,
173        }
174    }
175
176    #[allow(clippy::too_many_arguments)]
177    pub(crate) fn min_max(
178        &self,
179        value: f32,
180        parent_value: f32,
181        available_parent_value: f32,
182        single_margin: f32,
183        margin: f32,
184        minimum: &Self,
185        maximum: &Self,
186        root_value: f32,
187        phase: Phase,
188    ) -> f32 {
189        let value = self
190            .eval(
191                parent_value,
192                available_parent_value,
193                margin,
194                root_value,
195                phase,
196            )
197            .unwrap_or(value + margin);
198
199        let minimum_value = minimum
200            .eval(
201                parent_value,
202                available_parent_value,
203                margin,
204                root_value,
205                phase,
206            )
207            .map(|v| v + single_margin);
208        let maximum_value = maximum.eval(
209            parent_value,
210            available_parent_value,
211            margin,
212            root_value,
213            phase,
214        );
215
216        let mut final_value = value;
217
218        if let Some(minimum_value) = minimum_value
219            && minimum_value > final_value
220        {
221            final_value = minimum_value;
222        }
223
224        if let Some(maximum_value) = maximum_value
225            && final_value > maximum_value
226        {
227            final_value = maximum_value;
228        }
229
230        final_value
231    }
232
233    pub(crate) fn most_fitting_size<'a>(&self, size: &'a f32, available_size: &'a f32) -> &'a f32 {
234        match self {
235            Self::Inner => available_size,
236            _ => size,
237        }
238    }
239}
240
241impl Scaled for Size {
242    fn scale(&mut self, scale_factor: f32) {
243        if let Self::Pixels(s) = self {
244            *s *= scale_factor;
245        }
246    }
247}