egui_extras/
sizing.rs

1use egui::Rangef;
2
3/// Size hint for table column/strip cell.
4#[derive(Clone, Debug, Copy)]
5pub enum Size {
6    /// Absolute size in points, with a given range of allowed sizes to resize within.
7    Absolute { initial: f32, range: Rangef },
8
9    /// Relative size relative to all available space.
10    Relative { fraction: f32, range: Rangef },
11
12    /// Multiple remainders each get the same space.
13    Remainder { range: Rangef },
14}
15
16impl Size {
17    /// Exactly this big, with no room for resize.
18    pub fn exact(points: f32) -> Self {
19        Self::Absolute {
20            initial: points,
21            range: Rangef::new(points, points),
22        }
23    }
24
25    /// Initially this big, but can resize.
26    pub fn initial(points: f32) -> Self {
27        Self::Absolute {
28            initial: points,
29            range: Rangef::new(0.0, f32::INFINITY),
30        }
31    }
32
33    /// Relative size relative to all available space. Values must be in range `0.0..=1.0`.
34    pub fn relative(fraction: f32) -> Self {
35        debug_assert!(0.0 <= fraction && fraction <= 1.0);
36        Self::Relative {
37            fraction,
38            range: Rangef::new(0.0, f32::INFINITY),
39        }
40    }
41
42    /// Multiple remainders each get the same space.
43    pub fn remainder() -> Self {
44        Self::Remainder {
45            range: Rangef::new(0.0, f32::INFINITY),
46        }
47    }
48
49    /// Won't shrink below this size (in points).
50    #[inline]
51    pub fn at_least(mut self, minimum: f32) -> Self {
52        self.range_mut().min = minimum;
53        self
54    }
55
56    /// Won't grow above this size (in points).
57    #[inline]
58    pub fn at_most(mut self, maximum: f32) -> Self {
59        self.range_mut().max = maximum;
60        self
61    }
62
63    #[inline]
64    pub fn with_range(mut self, range: Rangef) -> Self {
65        *self.range_mut() = range;
66        self
67    }
68
69    /// Allowed range of movement (in points), if in a resizable [`Table`](crate::table::Table).
70    pub fn range(self) -> Rangef {
71        match self {
72            Self::Absolute { range, .. }
73            | Self::Relative { range, .. }
74            | Self::Remainder { range, .. } => range,
75        }
76    }
77
78    pub fn range_mut(&mut self) -> &mut Rangef {
79        match self {
80            Self::Absolute { range, .. }
81            | Self::Relative { range, .. }
82            | Self::Remainder { range, .. } => range,
83        }
84    }
85
86    #[inline]
87    pub fn is_absolute(&self) -> bool {
88        matches!(self, Self::Absolute { .. })
89    }
90
91    #[inline]
92    pub fn is_relative(&self) -> bool {
93        matches!(self, Self::Relative { .. })
94    }
95
96    #[inline]
97    pub fn is_remainder(&self) -> bool {
98        matches!(self, Self::Remainder { .. })
99    }
100}
101
102#[derive(Clone, Default)]
103pub struct Sizing {
104    pub(crate) sizes: Vec<Size>,
105}
106
107impl Sizing {
108    pub fn add(&mut self, size: Size) {
109        self.sizes.push(size);
110    }
111
112    pub fn to_lengths(&self, length: f32, spacing: f32) -> Vec<f32> {
113        if self.sizes.is_empty() {
114            return vec![];
115        }
116
117        let mut num_remainders = 0;
118        let sum_non_remainder = self
119            .sizes
120            .iter()
121            .map(|&size| match size {
122                Size::Absolute { initial, .. } => initial,
123                Size::Relative { fraction, range } => {
124                    assert!(0.0 <= fraction && fraction <= 1.0);
125                    range.clamp(length * fraction)
126                }
127                Size::Remainder { .. } => {
128                    num_remainders += 1;
129                    0.0
130                }
131            })
132            .sum::<f32>()
133            + spacing * (self.sizes.len() - 1) as f32;
134
135        let avg_remainder_length = if num_remainders == 0 {
136            0.0
137        } else {
138            let mut remainder_length = length - sum_non_remainder;
139            let avg_remainder_length = 0.0f32.max(remainder_length / num_remainders as f32).floor();
140            for &size in &self.sizes {
141                if let Size::Remainder { range } = size {
142                    if avg_remainder_length < range.min {
143                        remainder_length -= range.min;
144                        num_remainders -= 1;
145                    }
146                }
147            }
148            if num_remainders > 0 {
149                0.0f32.max(remainder_length / num_remainders as f32)
150            } else {
151                0.0
152            }
153        };
154
155        self.sizes
156            .iter()
157            .map(|&size| match size {
158                Size::Absolute { initial, .. } => initial,
159                Size::Relative { fraction, range } => range.clamp(length * fraction),
160                Size::Remainder { range } => range.clamp(avg_remainder_length),
161            })
162            .collect()
163    }
164}
165
166impl From<Vec<Size>> for Sizing {
167    fn from(sizes: Vec<Size>) -> Self {
168        Self { sizes }
169    }
170}
171
172#[test]
173fn test_sizing() {
174    let sizing: Sizing = vec![].into();
175    assert_eq!(sizing.to_lengths(50.0, 0.0), Vec::<f32>::new());
176
177    let sizing: Sizing = vec![Size::remainder().at_least(20.0), Size::remainder()].into();
178    assert_eq!(sizing.to_lengths(50.0, 0.0), vec![25.0, 25.0]);
179    assert_eq!(sizing.to_lengths(30.0, 0.0), vec![20.0, 10.0]);
180    assert_eq!(sizing.to_lengths(20.0, 0.0), vec![20.0, 0.0]);
181    assert_eq!(sizing.to_lengths(10.0, 0.0), vec![20.0, 0.0]);
182    assert_eq!(sizing.to_lengths(20.0, 10.0), vec![20.0, 0.0]);
183    assert_eq!(sizing.to_lengths(30.0, 10.0), vec![20.0, 0.0]);
184    assert_eq!(sizing.to_lengths(40.0, 10.0), vec![20.0, 10.0]);
185    assert_eq!(sizing.to_lengths(110.0, 10.0), vec![50.0, 50.0]);
186
187    let sizing: Sizing = vec![Size::relative(0.5).at_least(10.0), Size::exact(10.0)].into();
188    assert_eq!(sizing.to_lengths(50.0, 0.0), vec![25.0, 10.0]);
189    assert_eq!(sizing.to_lengths(30.0, 0.0), vec![15.0, 10.0]);
190    assert_eq!(sizing.to_lengths(20.0, 0.0), vec![10.0, 10.0]);
191    assert_eq!(sizing.to_lengths(10.0, 0.0), vec![10.0, 10.0]);
192}