1#[macro_use]
2extern crate bitflags;
3
4use std::cmp::max;
5use std::collections::BTreeMap;
6use std::f32;
7
8#[derive(Default, Clone, Copy)]
10pub struct Rectangle {
11 pub top: f32,
12 pub left: f32,
13 pub bottom: f32,
14 pub right: f32,
15}
16
17#[derive(Clone)]
19pub struct Size {
20 pub width: f32,
21 pub height: f32,
22}
23
24impl Size {
25 pub fn join_max(a: &Size, b: &Size) -> Self {
26 Size {
27 width: f32::max(a.width, b.width),
28 height: f32::max(a.height, b.height),
29 }
30 }
31
32 pub fn join_min(a: &Size, b: &Size) -> Self {
33 Size {
34 width: f32::min(a.width, b.width),
35 height: f32::min(a.height, b.height),
36 }
37 }
38
39 pub fn spread(&self, divisions: f32) -> Self {
42 Size {
43 width: self.width / divisions,
44 height: self.height / divisions,
45 }
46 }
47
48 pub fn padded(&self, padding: Rectangle) -> Self {
50 Size {
51 width: self.width + padding.left + padding.right,
52 height: self.height + padding.top + padding.bottom,
53 }
54 }
55
56 pub fn within(&self, other: &Size) -> bool {
58 other.width > self.width && other.height > self.height
59 }
60}
61
62#[derive(Clone)]
64pub struct SizeGrouping {
65 pub minimum: Size,
66 pub maximum: Size,
67 pub preferred: Size,
68}
69
70impl Default for SizeGrouping {
71 fn default() -> Self {
72 SizeGrouping {
73 minimum: Size {
74 width: 0.0,
75 height: 0.0,
76 },
77 preferred: Size {
78 width: 0.0,
79 height: 0.0,
80 },
81 maximum: Size {
82 width: f32::MAX,
83 height: f32::MAX,
84 },
85 }
86 }
87}
88
89impl SizeGrouping {
90 pub fn join(a: &SizeGrouping, b: &SizeGrouping) -> SizeGrouping {
91 SizeGrouping {
92 minimum: Size::join_max(&a.minimum, &b.minimum),
93 preferred: Size::join_max(&a.preferred, &b.preferred),
94 maximum: Size::join_min(&a.maximum, &b.maximum),
95 }
96 }
97
98 pub fn spread(&self, divisions: f32) -> SizeGrouping {
99 SizeGrouping {
100 minimum: self.minimum.spread(divisions),
101 preferred: self.preferred.spread(divisions),
102 maximum: self.maximum.spread(divisions),
103 }
104 }
105
106 pub fn padded(&self, padding: Rectangle) -> SizeGrouping {
107 SizeGrouping {
108 minimum: self.minimum.padded(padding),
109 preferred: self.preferred.padded(padding),
110 maximum: self.maximum.padded(padding),
111 }
112 }
113
114 pub fn box_fit(&self, area: &Size, prop: &CellProperties) -> (f32, f32, f32, f32) {
118 let pad_width = prop.padding.left + prop.padding.right;
119 let pad_height = prop.padding.top + prop.padding.bottom;
120
121 let w = if prop.flags.contains(CellFlags::FillHorizontal) {
123 f32::min(self.maximum.width, area.width - pad_width)
124 } else {
125 f32::min(self.preferred.width, area.width - pad_width)
126 };
127
128 let h = if prop.flags.contains(CellFlags::FillVertical) {
130 f32::min(self.maximum.height, area.height - pad_height)
131 } else {
132 f32::min(self.preferred.height, area.height - pad_height)
133 };
134
135 let x = if prop.flags.contains(CellFlags::AnchorRight) {
137 area.width - prop.padding.right - w
139 } else if prop.flags.contains(CellFlags::AnchorHorizontalCenter) {
140 (area.width / 2.0) - (w / 2.0)
143 } else {
144 prop.padding.left
146 };
147
148 let y = if prop.flags.contains(CellFlags::AnchorBottom) {
150 area.height - prop.padding.bottom - h
152 } else if prop.flags.contains(CellFlags::AnchorHorizontalCenter) {
153 (area.height / 2.0) - (h / 2.0)
156 } else {
157 prop.padding.top
159 };
160
161 (x, y, w, h)
162 }
163}
164
165bitflags! {
166 pub struct CellFlags: u16 {
167 const None = 0b0000_0000_0000_0000;
168 const ExpandHorizontal = 0b0000_0000_0000_0001;
170 const ExpandVertical = 0b0000_0000_0000_0010;
172 const FillHorizontal = 0b0000_0000_0000_0100;
174 const FillVertical = 0b0000_0000_0000_1000;
176 const AnchorTop = 0b0000_0000_0001_0000;
178 const AnchorBottom = 0b0000_0000_0010_0000;
180 const AnchorLeft = 0b0000_0000_0100_0000;
182 const AnchorRight = 0b0000_0000_1000_0000;
184 const AnchorHorizontalCenter = 0b0000_0001_0000_0000;
186 const AnchorVerticalCenter = 0b0000_0010_0000_0000;
188 const Uniform = 0b0000_0100_0000_0000;
190 }
191}
192
193pub type PositioningFn = FnMut(f32, f32, f32, f32);
197
198pub struct CellProperties {
200 pub size: SizeGrouping,
202 pub flags: CellFlags,
204 pub colspan: u8,
206 pub padding: Rectangle,
208 pub callback: Option<Box<PositioningFn>>,
212}
213
214impl Default for CellProperties {
215 fn default() -> Self {
216 CellProperties {
217 size: Default::default(),
218 flags: CellFlags::None,
219 padding: Default::default(),
220 colspan: 1,
221 callback: None,
222 }
223 }
224}
225
226impl Clone for CellProperties {
227 fn clone(&self) -> Self {
228 CellProperties {
229 size: self.size.clone(),
230 flags: self.flags,
231 padding: self.padding,
232 colspan: self.colspan,
233 callback: None,
234 }
235 }
236}
237
238pub enum LayoutOp {
239 Cell(CellProperties),
241 Row,
243}
244
245#[derive(Default)]
246pub struct TableLayout {
247 pub cell_defaults: CellProperties,
248 pub row_defaults: BTreeMap<u8, CellProperties>,
249 pub column_defaults: BTreeMap<u8, CellProperties>,
250 pub opcodes: Vec<LayoutOp>,
251
252 pub row: u8,
253 pub column: u8,
254}
255
256impl CellProperties {
257 pub fn new() -> Self {
258 Default::default()
259 }
260
261 pub fn with_defaults(layout: &TableLayout) -> Self {
269 let column_value = layout.column_defaults.get(&layout.column);
271 if column_value.is_some() {
272 return (*column_value.unwrap()).clone();
273 }
274
275 let row_value = layout.row_defaults.get(&layout.row);
277 if row_value.is_some() {
278 return (*row_value.unwrap()).clone();
279 }
280
281 CellProperties {
283 ..layout.cell_defaults.clone()
284 }
285 }
286
287 pub fn minimum_size(mut self, minimum: Size) -> Self {
288 self.size.minimum = minimum;
289 self
290 }
291
292 pub fn maximum_size(mut self, maximum: Size) -> Self {
293 self.size.maximum = maximum;
294 self
295 }
296
297 pub fn preferred_size(mut self, preferred: Size) -> Self {
298 self.size.preferred = preferred;
299 self
300 }
301
302 pub fn expand(mut self) -> Self {
303 self.flags |= CellFlags::ExpandHorizontal | CellFlags::ExpandVertical;
304 self
305 }
306
307 pub fn expand_horizontal(mut self) -> Self {
308 self.flags |= CellFlags::ExpandHorizontal;
309 self
310 }
311
312 pub fn expand_vertical(mut self) -> Self {
313 self.flags |= CellFlags::ExpandVertical;
314 self
315 }
316
317 pub fn fill(mut self) -> Self {
318 self.flags |= CellFlags::FillHorizontal | CellFlags::FillVertical;
319 self
320 }
321
322 pub fn fill_horizontal(mut self) -> Self {
323 self.flags |= CellFlags::FillHorizontal;
324 self
325 }
326
327 pub fn fill_vertical(mut self) -> Self {
328 self.flags |= CellFlags::FillVertical;
329 self
330 }
331
332 pub fn anchor_top(mut self) -> Self {
333 self.flags |= CellFlags::AnchorTop;
334 self
335 }
336
337 pub fn anchor_bottom(mut self) -> Self {
338 self.flags |= CellFlags::AnchorBottom;
339 self
340 }
341
342 pub fn anchor_left(mut self) -> Self {
343 self.flags |= CellFlags::AnchorLeft;
344 self
345 }
346
347 pub fn anchor_right(mut self) -> Self {
348 self.flags |= CellFlags::AnchorRight;
349 self
350 }
351
352 pub fn anchor_center(mut self) -> Self {
353 self.flags |= CellFlags::AnchorHorizontalCenter | CellFlags::AnchorVerticalCenter;
354 self
355 }
356
357 pub fn anchor_horizontal_center(mut self) -> Self {
358 self.flags |= CellFlags::AnchorHorizontalCenter;
359 self
360 }
361
362 pub fn anchor_vertical_center(mut self) -> Self {
363 self.flags |= CellFlags::AnchorVerticalCenter;
364 self
365 }
366
367 pub fn uniform(mut self) -> Self {
368 self.flags |= CellFlags::Uniform;
369 self
370 }
371
372 pub fn colspan(mut self, span: u8) -> Self {
373 self.colspan = span;
374 self
375 }
376
377 pub fn callback(mut self, fun: Box<PositioningFn>) -> Self {
378 self.callback = Option::Some(fun);
379 self
380 }
381
382 pub fn padding(mut self, pad: &Rectangle) -> Self {
385 self.padding = *pad;
386 self
387 }
388
389 pub fn padding_all(mut self, pad: f32) -> Self {
390 self.padding.top = pad;
391 self.padding.left = pad;
392 self.padding.bottom = pad;
393 self.padding.right = pad;
394 self
395 }
396
397 pub fn padding_top(mut self, pad: f32) -> Self {
399 self.padding.top = pad;
400 self
401 }
402
403 pub fn padding_left(mut self, pad: f32) -> Self {
405 self.padding.left = pad;
406 self
407 }
408
409 pub fn padding_bottom(mut self, pad: f32) -> Self {
411 self.padding.bottom = pad;
412 self
413 }
414
415 pub fn padding_right(mut self, pad: f32) -> Self {
417 self.padding.right = pad;
418 self
419 }
420}
421
422impl TableLayout {
423 pub fn new() -> TableLayout {
424 Default::default()
425 }
426
427 pub fn get_rows_cols(&self) -> (u8, u8) {
429 let mut cols = 0;
430 let mut colcur = 0;
431 let mut rows = 0;
432
433 for op in &self.opcodes {
434 match op {
435 LayoutOp::Cell(cp) => colcur += cp.colspan,
436 LayoutOp::Row => {
437 cols = max(cols, colcur);
438 colcur = 0;
439 rows += 1
440 }
441 }
442 }
443
444 if colcur > 0 {
445 cols = max(cols, colcur);
446 rows += 1;
447 }
448
449 (rows, cols)
450 }
451
452 pub fn clear(&mut self) {
454 self.row = 0;
455 self.column = 0;
456 self.opcodes.clear()
457 }
458
459 pub fn full_clear(&mut self) {
461 self.clear();
462 self.row_defaults.clear();
463 self.column_defaults.clear();
464 self.cell_defaults = Default::default()
465 }
466
467 pub fn with_row(&mut self) -> &mut Self {
469 self.opcodes.push(LayoutOp::Row);
470 self.row += 1;
471 self.column = 0;
472 self
473 }
474
475 pub fn with_cell(&mut self, properties: CellProperties) -> &mut Self {
477 self.column += properties.colspan;
478 self.opcodes.push(LayoutOp::Cell(properties));
479 self
480 }
481
482 pub fn impose(&mut self, width: f32, height: f32) {
483 let mut row: u8 = 0;
484 let mut col: u8 = 0;
485
486 let (total_rows, total_cols) = self.get_rows_cols();
487 if total_cols == 0 {
488 return;
489 } let mut col_sizes: Vec<SizeGrouping> = Vec::with_capacity(total_cols as usize);
492 for _i in 0..total_cols {
494 col_sizes.push(Default::default());
495 }
496
497 let mut row_sizes: Vec<SizeGrouping> = Vec::with_capacity(total_cols as usize);
499 for _i in 0..total_rows {
500 row_sizes.push(Default::default());
501 }
502
503 let mut has_xexpand: Vec<bool> = Vec::with_capacity(total_cols as usize);
504 for _i in 0..total_cols {
505 has_xexpand.push(false);
506 }
507
508 let mut has_yexpand: Vec<bool> = Vec::with_capacity(total_rows as usize);
509 for _i in 0..total_rows {
510 has_yexpand.push(false);
511 }
512
513 for op in &self.opcodes {
515 match op {
516 LayoutOp::Cell(cp) => {
517 match cp.colspan {
518 0 => {}
520 _ => {
521 let midget = cp.size.padded(cp.padding).spread(f32::from(cp.colspan));
522 row_sizes[row as usize] =
523 SizeGrouping::join(&row_sizes[row as usize], &cp.size);
524 if cp.flags.contains(CellFlags::ExpandVertical) {
525 has_yexpand[row as usize] = true
526 }
527 for _i in 0..cp.colspan {
528 if cp.flags.contains(CellFlags::ExpandHorizontal) {
529 has_xexpand[col as usize] = true
530 }
531 col_sizes[col as usize] =
532 SizeGrouping::join(&col_sizes[col as usize], &midget);
533 col += 1;
534 }
535 }
536 }
537 }
538 LayoutOp::Row => {
540 row += 1;
541 col = 0;
542 }
543 }
544 }
545
546 let mut slack: Vec<f32> = Vec::new();
547
548 let mut error = width;
550 for c in &col_sizes {
551 error -= c.preferred.width;
553 }
554
555 if error > 0.0 {
556 let expansions = has_xexpand.iter().filter(|x| **x).count();
559 if expansions > 0 {
560 let amount = error / expansions as f32;
561 for (i, e) in has_xexpand.iter().enumerate() {
562 if *e {
563 col_sizes[i].preferred.width += amount;
564 }
565 }
566 }
567 } else if error < 0.0 {
568 let error = -error;
570 let mut total_slack: f32 = 0.0;
572 slack.clear();
573 slack.resize(total_cols as usize, 0.0);
574 for (i, x) in col_sizes
575 .iter()
576 .map(|x| x.preferred.width - x.minimum.width)
577 .enumerate()
578 {
579 slack[i] = x;
580 total_slack += x;
581 }
582
583 for mut s in &mut slack {
586 let norm = *s / total_slack;
587 let error_over_slack = error * norm;
588 *s -= error_over_slack
589 }
590
591 for (i, x) in slack.iter().enumerate() {
593 col_sizes[i].preferred.width = f32::max(col_sizes[i].minimum.width + *x, 0.0);
594 }
595 }
596
597 let mut error = height;
599 for c in &row_sizes {
600 error -= c.preferred.height;
602 }
603
604 if error > 0.0 {
605 let expansions = has_yexpand.iter().filter(|y| **y).count();
608 if expansions > 0 {
609 let amount = error / expansions as f32;
610 for (i, e) in has_yexpand.iter().enumerate() {
611 if *e {
612 row_sizes[i].preferred.height += amount;
613 }
614 }
615 }
616 } else if error < 0.0 {
617 let error = -error;
619 let mut total_slack: f32 = 0.0;
621 slack.clear();
622 slack.resize(total_rows as usize, 0.0);
623 for (i, y) in row_sizes
624 .iter()
625 .map(|y| y.preferred.height - y.minimum.height)
626 .enumerate()
627 {
628 slack[i] = y;
629 total_slack += y;
630 }
631
632 for mut s in &mut slack {
635 let norm = *s / total_slack;
636 let error_over_slack = error * norm;
637 *s -= error_over_slack
638 }
639
640 for (i, y) in slack.iter().enumerate() {
642 row_sizes[i].preferred.height = f32::max(row_sizes[i].minimum.height + *y, 0.0);
643 }
644 }
645
646 let mut x = 0.0;
648 let mut y = 0.0;
649 row = 0;
650 col = 0;
651 for mut op in &mut self.opcodes {
652 let height = row_sizes[row as usize].preferred.height;
654 match op {
655 LayoutOp::Cell(cp) => match &cp.colspan {
657 0 => {} _ => {
659 let mut width: f32 = 0.0;
660 for _i in 0..cp.colspan {
661 width += col_sizes[col as usize].preferred.width;
662 col += 1;
663 }
664 let s = Size { width, height };
665 let (bx, by, bw, bh) = cp.size.box_fit(&s, &cp);
666
667 match &mut cp.callback {
669 Some(cb) => {
670 (*cb)(x + bx, y + by, bw, bh);
671 }
672 None => {}
673 }
674
675 x += width;
676 }
677 },
678 LayoutOp::Row => {
680 x = 0.0;
681 y += height;
682 row += 1;
683 col = 0;
684 }
685 }
686 }
687 }
688}
689
690#[cfg(test)]
691mod test;