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    /// Sizes the element based on its content. This is the default.
107    ///
108    /// Can also be created with [`Size::auto`].
109    ///
110    /// ```
111    /// # use torin::prelude::*;
112    /// let size = Size::auto();
113    /// ```
114    Inner,
115
116    /// Expands to fill all the available space from its parent.
117    ///
118    /// Can also be created with [`Size::fill`].
119    ///
120    /// ```
121    /// # use torin::prelude::*;
122    /// let size = Size::fill();
123    /// ```
124    Fill,
125
126    /// Expand to the biggest sibling when using [`Content::fit`](crate::content::Content::fit).
127    ///
128    /// Can also be created with [`Size::fill_minimum`].
129    ///
130    /// ```
131    /// # use torin::prelude::*;
132    /// let size = Size::fill_minimum();
133    /// ```
134    FillMinimum,
135
136    /// Sizes as a percentage relative to the parent's size.
137    ///
138    /// Can also be created with [`Size::percent`].
139    ///
140    /// ```
141    /// # use torin::prelude::*;
142    /// let size = Size::percent(50.0);
143    /// ```
144    Percentage(Length),
145
146    /// Fixed size in pixels.
147    ///
148    /// Can also be created with [`Size::px`].
149    ///
150    /// ```
151    /// # use torin::prelude::*;
152    /// let size = Size::px(200.0);
153    /// ```
154    Pixels(Length),
155
156    /// Sizes as a percentage relative to the root (window) size.
157    ///
158    /// Can also be created with [`Size::window_percent`].
159    ///
160    /// ```
161    /// # use torin::prelude::*;
162    /// let size = Size::window_percent(80.0);
163    /// ```
164    RootPercentage(Length),
165
166    /// Dynamic size computed by a closure at layout time.
167    ///
168    /// Can also be created with [`Size::func`] or [`Size::func_data`].
169    Fn(Box<SizeFn>),
170
171    /// Flex grow factor, fills the available space proportionally in the final layout phase.
172    ///
173    /// Can also be created with [`Size::flex`].
174    ///
175    /// ```
176    /// # use torin::prelude::*;
177    /// let size = Size::flex(1.0);
178    /// ```
179    Flex(Length),
180}
181
182impl Default for Size {
183    fn default() -> Self {
184        Self::Inner
185    }
186}
187
188impl Size {
189    /// Use an [`Inner`](Size::Inner) size.
190    pub fn auto() -> Size {
191        Size::Inner
192    }
193
194    /// Use a [`Fill`](Size::Fill) size.
195    pub fn fill() -> Size {
196        Size::Fill
197    }
198
199    /// Use a [`FillMinimum`](Size::FillMinimum) size.
200    pub fn fill_minimum() -> Size {
201        Size::FillMinimum
202    }
203
204    /// Use a [`Percentage`](Size::Percentage) size.
205    pub fn percent(percent: impl Into<f32>) -> Size {
206        Size::Percentage(Length::new(percent.into()))
207    }
208
209    /// Use a [`Pixels`](Size::Pixels) size.
210    pub fn px(px: impl Into<f32>) -> Size {
211        Size::Pixels(Length::new(px.into()))
212    }
213
214    /// Use a [`RootPercentage`](Size::RootPercentage) size.
215    pub fn window_percent(percent: impl Into<f32>) -> Size {
216        Size::RootPercentage(Length::new(percent.into()))
217    }
218
219    /// Use a [`Flex`](Size::Flex) size.
220    pub fn flex(flex: impl Into<f32>) -> Size {
221        Size::Flex(Length::new(flex.into()))
222    }
223
224    /// Use a dynamic [`Fn`](Size::Fn) size computed by the given closure.
225    pub fn func(func: impl Fn(SizeFnContext) -> Option<f32> + 'static + Sync + Send) -> Size {
226        Self::Fn(Box::new(SizeFn::new(func)))
227    }
228
229    /// Use a dynamic [`Fn`](Size::Fn) size with hashable data for equality checks.
230    pub fn func_data<D: Hash>(
231        func: impl Fn(SizeFnContext) -> Option<f32> + 'static + Sync + Send,
232        data: &D,
233    ) -> Size {
234        Self::Fn(Box::new(SizeFn::new_data(func, data)))
235    }
236
237    pub(crate) fn flex_grow(&self) -> Option<Length> {
238        match self {
239            Self::Flex(f) => Some(*f),
240            _ => None,
241        }
242    }
243
244    pub(crate) fn is_flex(&self) -> bool {
245        matches!(self, Self::Flex(_))
246    }
247
248    pub(crate) fn inner_sized(&self) -> bool {
249        matches!(self, Self::Inner | Self::FillMinimum)
250    }
251
252    pub fn pretty(&self) -> String {
253        match self {
254            Self::Inner => "auto".to_string(),
255            Self::Pixels(s) => format!("{}", s.get()),
256            Self::Fn(_) => "Fn".to_string(),
257            Self::Percentage(p) => format!("{}%", p.get()),
258            Self::Fill => "fill".to_string(),
259            Self::FillMinimum => "fill-min".to_string(),
260            Self::RootPercentage(p) => format!("{}% of root", p.get()),
261            Self::Flex(f) => format!("flex({})", f.get()),
262        }
263    }
264
265    pub(crate) fn eval(
266        &self,
267        parent: f32,
268        available_parent: f32,
269        parent_margin: f32,
270        root: f32,
271        phase: Phase,
272    ) -> Option<f32> {
273        match self {
274            Self::Pixels(px) => Some(px.get() + parent_margin),
275            Self::Percentage(per) => Some(parent / 100.0 * per.get()),
276            Self::Fill => Some(available_parent),
277            Self::RootPercentage(per) => Some(root / 100.0 * per.get()),
278            Self::Flex(_) | Self::FillMinimum if phase == Phase::Final => Some(available_parent),
279            Self::Fn(f) => f.call(SizeFnContext {
280                parent,
281                available_parent,
282                parent_margin,
283                root,
284                phase,
285            }),
286            _ => None,
287        }
288    }
289
290    #[allow(clippy::too_many_arguments)]
291    pub(crate) fn min_max(
292        &self,
293        value: f32,
294        parent_value: f32,
295        available_parent_value: f32,
296        single_margin: f32,
297        margin: f32,
298        minimum: &Self,
299        maximum: &Self,
300        root_value: f32,
301        phase: Phase,
302    ) -> f32 {
303        let value = self
304            .eval(
305                parent_value,
306                available_parent_value,
307                margin,
308                root_value,
309                phase,
310            )
311            .unwrap_or(value + margin);
312
313        let minimum_value = minimum
314            .eval(
315                parent_value,
316                available_parent_value,
317                margin,
318                root_value,
319                phase,
320            )
321            .map(|v| v + single_margin);
322        let maximum_value = maximum.eval(
323            parent_value,
324            available_parent_value,
325            margin,
326            root_value,
327            phase,
328        );
329
330        let mut final_value = value;
331
332        if let Some(minimum_value) = minimum_value
333            && minimum_value > final_value
334        {
335            final_value = minimum_value;
336        }
337
338        if let Some(maximum_value) = maximum_value
339            && final_value > maximum_value
340        {
341            final_value = maximum_value;
342        }
343
344        final_value
345    }
346
347    pub(crate) fn most_fitting_size<'a>(&self, size: &'a f32, available_size: &'a f32) -> &'a f32 {
348        match self {
349            Self::Inner => available_size,
350            _ => size,
351        }
352    }
353}
354
355impl Scaled for Size {
356    fn scale(&mut self, scale_factor: f32) {
357        if let Self::Pixels(s) = self {
358            *s *= scale_factor;
359        }
360    }
361}