druid_widget_nursery/table/
table_column_width.rs1use std::ops::Range;
18
19#[derive(Copy, Clone, Debug)]
23pub enum TableColumnWidth {
24 Fixed(f64),
26 Flex(f64),
28 Fraction(f64),
30 Intrinsic,
32}
33
34impl TableColumnWidth {
35 fn resolve_width(
36 &self,
37 total_width: f64,
38 intrinsic_width: f64,
39 px_per_flex: f64,
40 ) -> (f64, f64, f64) {
41 match self {
42 Self::Fixed(w) => (*w, *w, 0f64),
43 Self::Flex(f) => (f * px_per_flex, 0f64, *f),
44 Self::Fraction(f) => {
45 let w = f * total_width;
46 (w, w, 0f64)
47 }
48 Self::Intrinsic => (intrinsic_width, intrinsic_width, 0f64),
49 }
50 }
51
52 fn need_intrinsic_width(&self) -> bool {
53 matches!(self, Self::Intrinsic)
54 }
55}
56
57impl From<f64> for TableColumnWidth {
58 fn from(fixed_width: f64) -> Self {
59 TableColumnWidth::Fixed(fixed_width)
60 }
61}
62
63#[derive(Copy, Clone, Debug)]
96pub enum ComplexTableColumnWidth {
97 Simple(TableColumnWidth),
99 Limited(TableColumnWidth, TableColumnWidth, TableColumnWidth),
105}
106
107impl<W: Into<TableColumnWidth>> From<W> for ComplexTableColumnWidth {
108 fn from(tcw: W) -> Self {
109 let tcw: TableColumnWidth = tcw.into();
110 Self::simple(tcw)
111 }
112}
113
114impl<W: Into<TableColumnWidth>> From<(W, Range<f64>)> for ComplexTableColumnWidth {
115 fn from(data: (W, Range<f64>)) -> Self {
116 let tcw: TableColumnWidth = data.0.into();
117 let min = TableColumnWidth::Fixed(data.1.start);
118 let max = TableColumnWidth::Fixed(data.1.end);
119 Self::with_min_max(tcw, min, max)
120 }
121}
122
123impl<W1, W2> From<(W1, W2)> for ComplexTableColumnWidth
124where
125 W1: Into<TableColumnWidth>,
126 W2: Into<TableColumnWidth>,
127{
128 fn from(data: (W1, W2)) -> Self {
129 let tcw: TableColumnWidth = data.0.into();
130 let min: TableColumnWidth = data.1.into();
131 let max = TableColumnWidth::Fixed(0.0);
132 Self::with_min_max(tcw, min, max)
133 }
134}
135
136impl<W1, W2, W3> From<(W1, W2, W3)> for ComplexTableColumnWidth
137where
138 W1: Into<TableColumnWidth>,
139 W2: Into<TableColumnWidth>,
140 W3: Into<TableColumnWidth>,
141{
142 fn from(data: (W1, W2, W3)) -> Self {
143 let tcw: TableColumnWidth = data.0.into();
144 let min: TableColumnWidth = data.1.into();
145 let max: TableColumnWidth = data.2.into();
146 Self::with_min_max(tcw, min, max)
147 }
148}
149
150impl ComplexTableColumnWidth {
151 pub fn simple(tcw: TableColumnWidth) -> Self {
153 Self::Simple(tcw)
154 }
155
156 pub fn with_min<W1, W2>(tcw: W1, min: W2) -> Self
158 where
159 W1: Into<TableColumnWidth>,
160 W2: Into<TableColumnWidth>,
161 {
162 let tcw: TableColumnWidth = tcw.into();
163 let min: TableColumnWidth = min.into();
164 Self::with_min_max(tcw, min, TableColumnWidth::Fixed(0f64))
165 }
166
167 pub fn with_max<W1, W2>(tcw: W1, max: W2) -> Self
169 where
170 W1: Into<TableColumnWidth>,
171 W2: Into<TableColumnWidth>,
172 {
173 let tcw: TableColumnWidth = tcw.into();
174 let max: TableColumnWidth = max.into();
175 Self::with_min_max(tcw, TableColumnWidth::Fixed(0f64), max)
176 }
177
178 pub fn with_min_max<W1, W2, W3>(tcw: W1, min: W2, max: W3) -> Self
180 where
181 W1: Into<TableColumnWidth>,
182 W2: Into<TableColumnWidth>,
183 W3: Into<TableColumnWidth>,
184 {
185 let tcw: TableColumnWidth = tcw.into();
186 let min: TableColumnWidth = min.into();
187 let max: TableColumnWidth = max.into();
188 Self::Limited(tcw, min, max)
189 }
190
191 pub(crate) fn need_intrinsic_width(&self) -> bool {
192 match self {
193 Self::Simple(tcw) => tcw.need_intrinsic_width(),
194 Self::Limited(tcw, min, max) => {
195 tcw.need_intrinsic_width()
196 || min.need_intrinsic_width()
197 || max.need_intrinsic_width()
198 }
199 }
200 }
201
202 fn resolve_width(
203 &self,
204 total_width: f64,
205 intrinsic_width: f64,
206 px_per_flex: f64,
207 apply_limits: bool,
208 ) -> (bool, f64, f64, f64) {
209 match self {
210 Self::Simple(tcw) => {
211 let (col_width, col_fixed, col_flex) =
212 tcw.resolve_width(total_width, intrinsic_width, px_per_flex);
213 (false, col_width, col_fixed, col_flex)
214 }
215 Self::Limited(tcw, min, max) => {
216 let (col_width, col_fixed, col_flex) =
217 tcw.resolve_width(total_width, intrinsic_width, px_per_flex);
218
219 if apply_limits {
220 let (min_width, _min_fixed, _min_flex) =
221 min.resolve_width(total_width, intrinsic_width, px_per_flex);
222 let (max_width, _max_fixed, _max_flex) =
223 max.resolve_width(total_width, intrinsic_width, px_per_flex);
224
225 if (max_width > 0f64) && (col_width > max_width) {
226 return (true, max_width, max_width, 0f64);
227 }
228 if (min_width > 0f64) && (col_width < min_width) {
229 return (true, min_width, min_width, 0f64);
230 }
231 }
232 (false, col_width, col_fixed, col_flex)
233 }
234 }
235 }
236
237 pub(crate) fn compute_column_widths(
239 column_widths: &mut [ComplexTableColumnWidth],
240 intrinsic_widths: &[f64],
241 max_table_width: f64,
242 ) -> Vec<f64> {
243 let column_count = column_widths.len();
244 let mut col_widths = vec![0f64; column_count];
245
246 'outer: for _ in 0..column_count {
249 let px_per_flex = {
252 let mut fixed_width = 0f64;
253 let mut flex_sum = 0f64;
254
255 for col_num in 0..column_count {
256 let (_, _width, fixed_share, flex_share) = column_widths[col_num]
257 .resolve_width(max_table_width, intrinsic_widths[col_num], 0f64, false);
258 fixed_width += fixed_share;
259 flex_sum += flex_share;
260 }
261
262 let remaining = (max_table_width - fixed_width).max(0.0);
263 if flex_sum != 0f64 {
264 remaining / flex_sum
265 } else {
266 0f64
267 }
268 };
269
270 for col_num in 0..column_count {
273 let (limit, width, _fixed_share, _flex_share) = column_widths[col_num]
274 .resolve_width(
275 max_table_width,
276 intrinsic_widths[col_num],
277 px_per_flex,
278 true,
279 );
280 col_widths[col_num] = width;
281
282 if limit {
283 column_widths[col_num] = TableColumnWidth::Fixed(width).into();
285 continue 'outer;
286 }
287 }
288
289 break; }
291
292 col_widths
293 }
294}