1mod tabs;
14mod tree;
15
16pub use tabs::Tabs;
17pub use tree::{Pane, Tree};
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
25pub enum Axis {
26 Columns,
28 Rows,
30}
31
32impl Axis {
33 pub fn other(self) -> Self {
34 match self {
35 Axis::Columns => Axis::Rows,
36 Axis::Rows => Axis::Columns,
37 }
38 }
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
43pub enum Direction {
44 Left,
45 Right,
46 Up,
47 Down,
48}
49
50impl Direction {
51 pub fn axis(self) -> Axis {
53 match self {
54 Direction::Left | Direction::Right => Axis::Columns,
55 Direction::Up | Direction::Down => Axis::Rows,
56 }
57 }
58
59 pub fn is_forward(self) -> bool {
61 matches!(self, Direction::Right | Direction::Down)
62 }
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
68pub struct ViewId(pub u64);
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
72pub struct Rect {
73 pub x: u16,
74 pub y: u16,
75 pub width: u16,
76 pub height: u16,
77}
78
79impl Rect {
80 pub fn new(x: u16, y: u16, width: u16, height: u16) -> Self {
81 Self {
82 x,
83 y,
84 width,
85 height,
86 }
87 }
88
89 pub fn right(self) -> u16 {
90 self.x.saturating_add(self.width)
91 }
92
93 pub fn bottom(self) -> u16 {
94 self.y.saturating_add(self.height)
95 }
96
97 pub fn is_empty(self) -> bool {
98 self.width == 0 || self.height == 0
99 }
100
101 pub fn extent(self, axis: Axis) -> u16 {
103 match axis {
104 Axis::Columns => self.width,
105 Axis::Rows => self.height,
106 }
107 }
108
109 pub fn overlaps_across(self, other: Rect, axis: Axis) -> bool {
114 match axis {
115 Axis::Columns => self.y < other.bottom() && other.y < self.bottom(),
116 Axis::Rows => self.x < other.right() && other.x < self.right(),
117 }
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn an_axis_has_an_opposite() {
127 assert_eq!(Axis::Columns.other(), Axis::Rows);
128 assert_eq!(Axis::Rows.other(), Axis::Columns);
129 }
130
131 #[test]
132 fn a_direction_knows_which_split_it_can_move_within() {
133 assert_eq!(Direction::Left.axis(), Axis::Columns);
134 assert_eq!(Direction::Right.axis(), Axis::Columns);
135 assert_eq!(Direction::Up.axis(), Axis::Rows);
136 assert_eq!(Direction::Down.axis(), Axis::Rows);
137 assert!(Direction::Right.is_forward() && Direction::Down.is_forward());
138 assert!(!Direction::Left.is_forward() && !Direction::Up.is_forward());
139 }
140
141 #[test]
142 fn rect_edges_saturate_rather_than_wrapping() {
143 let r = Rect::new(u16::MAX - 1, u16::MAX - 1, 10, 10);
144 assert_eq!(r.right(), u16::MAX);
145 assert_eq!(r.bottom(), u16::MAX);
146 }
147
148 #[test]
149 fn overlap_is_measured_across_the_axis_not_along_it() {
150 let left = Rect::new(0, 0, 10, 20);
152 let right = Rect::new(10, 0, 10, 20);
153 assert!(left.overlaps_across(right, Axis::Columns));
154
155 assert!(!left.overlaps_across(right, Axis::Rows));
157 }
158
159 #[test]
160 fn touching_edges_do_not_count_as_overlapping() {
161 let top = Rect::new(0, 0, 10, 10);
163 let bottom = Rect::new(0, 10, 10, 10);
164 assert!(!top.overlaps_across(bottom, Axis::Columns));
165 assert!(
166 top.overlaps_across(bottom, Axis::Rows),
167 "they share columns"
168 );
169 }
170
171 #[test]
172 fn an_empty_rect_is_one_with_no_area() {
173 assert!(Rect::new(0, 0, 0, 5).is_empty());
174 assert!(Rect::new(0, 0, 5, 0).is_empty());
175 assert!(!Rect::new(0, 0, 1, 1).is_empty());
176 }
177
178 #[test]
179 fn extent_reads_the_axis_it_is_asked_for() {
180 let r = Rect::new(0, 0, 30, 10);
181 assert_eq!(r.extent(Axis::Columns), 30);
182 assert_eq!(r.extent(Axis::Rows), 10);
183 }
184}