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(Default, PartialEq, Clone, Debug)]
105pub enum Size {
106 #[default]
115 Inner,
116
117 Fill,
126
127 FillMinimum,
136
137 Percentage(Length),
146
147 Pixels(Length),
156
157 RootPercentage(Length),
166
167 Fn(Box<SizeFn>),
171
172 Flex(Length),
181}
182
183impl Size {
184 pub fn auto() -> Size {
186 Size::Inner
187 }
188
189 pub fn fill() -> Size {
191 Size::Fill
192 }
193
194 pub fn fill_minimum() -> Size {
196 Size::FillMinimum
197 }
198
199 pub fn percent(percent: impl Into<f32>) -> Size {
201 Size::Percentage(Length::new(percent.into()))
202 }
203
204 pub fn px(px: impl Into<f32>) -> Size {
206 Size::Pixels(Length::new(px.into()))
207 }
208
209 pub fn window_percent(percent: impl Into<f32>) -> Size {
211 Size::RootPercentage(Length::new(percent.into()))
212 }
213
214 pub fn flex(flex: impl Into<f32>) -> Size {
216 Size::Flex(Length::new(flex.into()))
217 }
218
219 pub fn func(func: impl Fn(SizeFnContext) -> Option<f32> + 'static + Sync + Send) -> Size {
221 Self::Fn(Box::new(SizeFn::new(func)))
222 }
223
224 pub fn func_data<D: Hash>(
226 func: impl Fn(SizeFnContext) -> Option<f32> + 'static + Sync + Send,
227 data: &D,
228 ) -> Size {
229 Self::Fn(Box::new(SizeFn::new_data(func, data)))
230 }
231
232 pub(crate) fn flex_grow(&self) -> Option<Length> {
233 match self {
234 Self::Flex(f) => Some(*f),
235 _ => None,
236 }
237 }
238
239 pub(crate) fn is_flex(&self) -> bool {
240 matches!(self, Self::Flex(_))
241 }
242
243 pub(crate) fn inner_sized(&self) -> bool {
244 matches!(self, Self::Inner | Self::FillMinimum)
245 }
246
247 pub fn pretty(&self) -> String {
248 match self {
249 Self::Inner => "auto".to_string(),
250 Self::Pixels(s) => format!("{}", s.get()),
251 Self::Fn(_) => "Fn".to_string(),
252 Self::Percentage(p) => format!("{}%", p.get()),
253 Self::Fill => "fill".to_string(),
254 Self::FillMinimum => "fill-min".to_string(),
255 Self::RootPercentage(p) => format!("{}% of root", p.get()),
256 Self::Flex(f) => format!("flex({})", f.get()),
257 }
258 }
259
260 pub(crate) fn eval(
261 &self,
262 parent: f32,
263 available_parent: f32,
264 parent_margin: f32,
265 root: f32,
266 phase: Phase,
267 ) -> Option<f32> {
268 match self {
269 Self::Pixels(px) => Some(px.get() + parent_margin),
270 Self::Percentage(per) => Some(parent / 100.0 * per.get()),
271 Self::Fill => Some(available_parent),
272 Self::RootPercentage(per) => Some(root / 100.0 * per.get()),
273 Self::Flex(_) | Self::FillMinimum if phase == Phase::Final => Some(available_parent),
274 Self::Fn(f) => f.call(SizeFnContext {
275 parent,
276 available_parent,
277 parent_margin,
278 root,
279 phase,
280 }),
281 _ => None,
282 }
283 }
284
285 #[allow(clippy::too_many_arguments)]
286 pub(crate) fn min_max(
287 &self,
288 value: f32,
289 parent_value: f32,
290 available_parent_value: f32,
291 single_margin: f32,
292 margin: f32,
293 minimum: &Self,
294 maximum: &Self,
295 root_value: f32,
296 phase: Phase,
297 ) -> f32 {
298 let value = self
299 .eval(
300 parent_value,
301 available_parent_value,
302 margin,
303 root_value,
304 phase,
305 )
306 .unwrap_or(value + margin);
307
308 let minimum_value = minimum
309 .eval(
310 parent_value,
311 available_parent_value,
312 margin,
313 root_value,
314 phase,
315 )
316 .map(|v| v + single_margin);
317 let maximum_value = maximum.eval(
318 parent_value,
319 available_parent_value,
320 margin,
321 root_value,
322 phase,
323 );
324
325 let mut final_value = value;
326
327 if let Some(minimum_value) = minimum_value
328 && minimum_value > final_value
329 {
330 final_value = minimum_value;
331 }
332
333 if let Some(maximum_value) = maximum_value
334 && final_value > maximum_value
335 {
336 final_value = maximum_value;
337 }
338
339 final_value
340 }
341
342 pub(crate) fn most_fitting_size<'a>(&self, size: &'a f32, available_size: &'a f32) -> &'a f32 {
343 match self {
344 Self::Inner => available_size,
345 _ => size,
346 }
347 }
348}
349
350impl Scaled for Size {
351 fn scale(&mut self, scale_factor: f32) {
352 if let Self::Pixels(s) = self {
353 *s *= scale_factor;
354 }
355 }
356}