summaryrefslogtreecommitdiffstats
path: root/src/tuine/constraints.rs
blob: a05462bed2f2908c3a739f22203ff6695946bf76 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
use tui::layout::{Direction, Rect};

use crate::canvas::LayoutConstraint;

pub(super) fn get_constraints(
    direction: Direction, constraints: &[LayoutConstraint], area: Rect,
) -> Vec<Rect> {
    // Order of operations:
    // - Ratios first + canvas-handled (which is just zero)
    // - Then any flex-grows to take up remaining space; divide amongst remaining
    //   hand out any remaining space

    #[derive(Debug, Default, Clone, Copy)]
    struct Size {
        width: u16,
        height: u16,
    }

    impl Size {
        fn shrink_width(&mut self, amount: u16) {
            self.width -= amount;
        }

        fn shrink_height(&mut self, amount: u16) {
            self.height -= amount;
        }
    }

    let mut bounds = Size {
        width: area.width,
        height: area.height,
    };
    let mut sizes = vec![Size::default(); constraints.len()];
    let mut grow = vec![];
    let mut num_non_ch = 0;

    for (itx, (constraint, size)) in constraints.iter().zip(sizes.iter_mut()).enumerate() {
        match constraint {
            LayoutConstraint::Ratio(a, b) => {
                match direction {
                    Direction::Horizontal => {
                        let amount = (((area.width as u32) * (*a)) / (*b)) as u16;
                        bounds.shrink_width(amount);
                        size.width = amount;
                        size.height = area.height;
                    }
                    Direction::Vertical => {
                        let amount = (((area.height as u32) * (*a)) / (*b)) as u16;
                        bounds.shrink_height(amount);
                        size.width = area.width;
                        size.height = amount;
                    }
                }
                num_non_ch += 1;
            }
            LayoutConstraint::Grow => {
                // Mark it as grow in the vector and handle in second pass.
                grow.push(itx);
                num_non_ch += 1;
            }
            LayoutConstraint::CanvasHandled => {
                // Do nothing in this case. It's already 0.
            }
        }
    }

    if !grow.is_empty() {
        match direction {
            Direction::Horizontal => {
                let width = bounds.width / grow.len() as u16;
                bounds.shrink_width(width * grow.len() as u16);
                for g in grow {
                    sizes[g] = Size {
                        width,
                        height: area.height,
                    };
                }
            }
            Direction::Vertical => {
                let height = bounds.height / grow.len() as u16;
                bounds.shrink_height(height * grow.len() as u16);
                for g in grow {
                    sizes[g] = Size {
                        width: area.width,
                        height,
                    };
                }
            }
        }
    }

    if num_non_ch > 0 {
        match direction {
            Direction::Horizontal => {
                let per_item = bounds.width / num_non_ch;
                let mut remaining_width = bounds.width % num_non_ch;
                for (size, constraint) in sizes.iter_mut().zip(constraints) {
                    match constraint {
                        LayoutConstraint::CanvasHandled => {}
                        LayoutConstraint::Grow | LayoutConstraint::Ratio(_, _) => {
                            if remaining_width > 0 {
                                size.width += per_item + 1;
                                remaining_width -= 1;
                            } else {
                                size.width += per_item;
                            }
                        }
                    }
                }
            }
            Direction::Vertical => {
                let per_item = bounds.height / num_non_ch;
                let mut remaining_height = bounds.height % num_non_ch;
                for (size, constraint) in sizes.iter_mut().zip(constraints) {
                    match constraint {
                        LayoutConstraint::CanvasHandled => {}
                        LayoutConstraint::Grow | LayoutConstraint::Ratio(_, _) => {
                            if remaining_height > 0 {
                                size.height += per_item + 1;
                                remaining_height -= 1;
                            } else {
                                size.height += per_item;
                            }
                        }
                    }
                }
            }
        }
    }

    let mut curr_x = area.x;
    let mut curr_y = area.y;
    sizes
        .into_iter()
        .map(|size| {
            let rect = Rect::new(curr_x, curr_y, size.width, size.height);
            match direction {
                Direction::Horizontal => {
                    curr_x += size.width;
                }
                Direction::Vertical => {
                    curr_y += size.height;
                }
            }

            rect
        })
        .collect()
}

#[cfg(test)]
mod test {
    // TODO: Add some tests.
}