1use ratatui::layout::Rect;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub struct ZoneId(u32);
12
13impl ZoneId {
14 #[must_use]
16 pub const fn new(id: u32) -> Self {
17 Self(id)
18 }
19
20 #[must_use]
22 pub const fn raw(self) -> u32 {
23 self.0
24 }
25}
26
27impl std::fmt::Display for ZoneId {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 write!(f, "zone:{}", self.0)
30 }
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub enum ZoneHint {
40 Tab,
42 Sidebar,
44 Control,
46 Overlay,
48 StatusBar,
50}
51
52#[derive(Debug, Clone)]
57pub struct ZoneRequest {
58 pub name: String,
60 pub hint: ZoneHint,
62 pub label: String,
64 pub preferred_height: u16,
66 pub preferred_width: u16,
68 pub min_terminal_width: u16,
70 pub order: u8,
72}
73
74impl ZoneRequest {
75 #[must_use]
77 pub fn tab(name: impl Into<String>, label: impl Into<String>) -> Self {
78 Self {
79 name: name.into(),
80 hint: ZoneHint::Tab,
81 label: label.into(),
82 preferred_height: 0,
83 preferred_width: 0,
84 min_terminal_width: 0,
85 order: 128,
86 }
87 }
88
89 #[must_use]
91 pub fn sidebar(name: impl Into<String>, label: impl Into<String>) -> Self {
92 Self {
93 name: name.into(),
94 hint: ZoneHint::Sidebar,
95 label: label.into(),
96 preferred_height: 0,
97 preferred_width: 0,
98 min_terminal_width: 120,
99 order: 128,
100 }
101 }
102
103 #[must_use]
105 pub fn overlay(
106 name: impl Into<String>,
107 label: impl Into<String>,
108 width: u16,
109 height: u16,
110 ) -> Self {
111 Self {
112 name: name.into(),
113 hint: ZoneHint::Overlay,
114 label: label.into(),
115 preferred_height: height,
116 preferred_width: width,
117 min_terminal_width: 0,
118 order: 0,
119 }
120 }
121
122 #[must_use]
124 pub fn with_order(mut self, order: u8) -> Self {
125 self.order = order;
126 self
127 }
128
129 #[must_use]
131 pub fn with_min_width(mut self, width: u16) -> Self {
132 self.min_terminal_width = width;
133 self
134 }
135}
136
137#[derive(Debug, Clone)]
142pub struct ZoneSpec {
143 pub id: ZoneId,
145 pub name: String,
147 pub label: String,
149 pub hint: ZoneHint,
151 pub area: Rect,
153 pub visible: bool,
155 pub order: u8,
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 #[test]
164 fn zone_id_display() {
165 assert_eq!(ZoneId::new(42).to_string(), "zone:42");
166 }
167
168 #[test]
169 fn zone_id_equality() {
170 assert_eq!(ZoneId::new(1), ZoneId::new(1));
171 assert_ne!(ZoneId::new(1), ZoneId::new(2));
172 }
173
174 #[test]
175 fn zone_request_tab() {
176 let req = ZoneRequest::tab("bmad.sprint", "Sprint");
177 assert_eq!(req.hint, ZoneHint::Tab);
178 assert_eq!(req.name, "bmad.sprint");
179 assert_eq!(req.label, "Sprint");
180 }
181
182 #[test]
183 fn zone_request_sidebar() {
184 let req = ZoneRequest::sidebar("github.pr", "Pull Requests");
185 assert_eq!(req.hint, ZoneHint::Sidebar);
186 assert_eq!(req.min_terminal_width, 120);
187 }
188
189 #[test]
190 fn zone_request_overlay() {
191 let req = ZoneRequest::overlay("finder.search", "Search", 60, 20);
192 assert_eq!(req.hint, ZoneHint::Overlay);
193 assert_eq!(req.preferred_width, 60);
194 assert_eq!(req.preferred_height, 20);
195 }
196
197 #[test]
198 fn zone_request_builder() {
199 let req = ZoneRequest::tab("test.tab", "Test")
200 .with_order(10)
201 .with_min_width(140);
202 assert_eq!(req.order, 10);
203 assert_eq!(req.min_terminal_width, 140);
204 }
205}