1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub struct Region {
7 pub x: usize,
8 pub y: usize,
9 pub width: usize,
10 pub height: usize,
11}
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum Direction {
16 Horizontal,
17 Vertical,
18}
19
20#[derive(Debug, Clone)]
22pub enum LayoutNode {
23 Split {
25 direction: Direction,
26 sizes: Vec<usize>, children: Vec<LayoutNode>,
28 },
29 Leaf {
31 name: String,
32 renderable: Option<String>, size: Option<usize>,
34 },
35}
36
37impl LayoutNode {
38 pub fn split(direction: Direction, children: Vec<LayoutNode>) -> Self {
40 let sizes = vec![1; children.len()];
41 Self::Split { direction, sizes, children }
42 }
43
44 pub fn sizes(mut self, sizes: Vec<usize>) -> Self {
46 if let Self::Split { sizes: ref mut s, .. } = self {
47 *s = sizes;
48 }
49 self
50 }
51}
52
53#[derive(Debug, Clone)]
55pub struct Layout {
56 pub root: LayoutNode,
57 pub visible: bool,
58 pub minimum_size: usize,
59}
60
61impl Layout {
62 pub fn new(root: LayoutNode) -> Self {
63 Self {
64 root,
65 visible: true,
66 minimum_size: 1,
67 }
68 }
69
70 pub fn compute(&self, total_width: usize, total_height: usize) -> Vec<(String, Region)> {
72 let mut regions = Vec::new();
73 let region = Region { x: 0, y: 0, width: total_width, height: total_height };
74 Self::layout_node(&self.root, region, &mut regions);
75 regions
76 }
77
78 fn layout_node(node: &LayoutNode, region: Region, out: &mut Vec<(String, Region)>) {
79 match node {
80 LayoutNode::Leaf { name, size, .. } => {
81 let mut r = region;
82 if let Some(s) = size {
83 r.width = r.width.min(*s);
84 r.height = r.height.min(*s);
85 } else {
86 r.width = r.width.max(2);
87 r.height = r.height.max(1);
88 }
89 out.push((name.clone(), r));
90 }
91 LayoutNode::Split { direction, sizes, children } => {
92 let total_size: usize = sizes.iter().sum();
93 let count = children.len();
94
95 match direction {
96 Direction::Horizontal => {
97 let mut x = region.x;
98 let total_spacing = count.saturating_sub(1);
99 let avail = region.width.saturating_sub(total_spacing);
100 for (i, child) in children.iter().enumerate() {
101 let ratio = sizes.get(i).copied().unwrap_or(1);
102 let child_w = (avail * ratio) / total_size;
103 let child_r = Region {
104 x,
105 y: region.y,
106 width: child_w.max(1),
107 height: region.height,
108 };
109 Self::layout_node(child, child_r, out);
110 x += child_w + 1; }
112 }
113 Direction::Vertical => {
114 let mut y = region.y;
115 for (i, child) in children.iter().enumerate() {
116 let ratio = sizes.get(i).copied().unwrap_or(1);
117 let child_h = (region.height * ratio) / total_size;
118 let child_r = Region {
119 x: region.x,
120 y,
121 width: region.width,
122 height: child_h.max(1),
123 };
124 Self::layout_node(child, child_r, out);
125 y += child_h;
126 }
127 }
128 }
129 }
130 }
131 }
132}