1use super::{ObjectError, ObjectHandle, ObjectRegistry, ObjectType, Result};
4use crate::status_bar::{RenderItem, StatusBarSide};
5
6#[derive(Debug, Clone)]
8pub struct WindowProxy {
9 handle: ObjectHandle,
10 pending_status: std::sync::Arc<std::sync::Mutex<Vec<(StatusBarSide, Vec<RenderItem>)>>>,
12}
13
14impl WindowProxy {
15 pub fn new(handle: ObjectHandle) -> Result<Self> {
16 if handle.object_type() != ObjectType::Window {
17 return Err(ObjectError::type_mismatch(
18 handle,
19 ObjectType::Window,
20 handle.object_type(),
21 ));
22 }
23 Ok(Self {
24 handle,
25 pending_status: std::sync::Arc::new(std::sync::Mutex::new(Vec::new())),
26 })
27 }
28
29 pub fn handle(&self) -> ObjectHandle {
30 self.handle
31 }
32
33 pub fn id(&self) -> u64 {
34 self.handle.id()
35 }
36
37 pub fn active_pane<T>(&self, registry: &impl ObjectRegistry<T>) -> Result<PaneProxy> {
53 let tab = self.active_tab(registry)?;
54 tab.active_pane(registry)
55 }
56
57 pub fn active_tab<T>(&self, registry: &impl ObjectRegistry<T>) -> Result<TabProxy> {
71 let tabs = self.tabs(registry)?;
72 tabs.into_iter()
73 .next()
74 .ok_or_else(|| ObjectError::method_not_found(self.handle, "active_tab: no tabs"))
75 }
76
77 pub fn tabs<T>(&self, registry: &impl ObjectRegistry<T>) -> Result<Vec<TabProxy>> {
88 let child_handles = registry.get_children(&self.handle)?;
89
90 child_handles
91 .into_iter()
92 .map(|handle| {
93 if handle.object_type() != ObjectType::Tab {
94 return Err(ObjectError::type_mismatch(
95 handle,
96 ObjectType::Tab,
97 handle.object_type(),
98 ));
99 }
100 TabProxy::new(handle)
101 })
102 .collect()
103 }
104
105 pub fn get_dimensions(&self) -> Result<(u32, u32)> {
107 Err(ObjectError::method_not_found(self.handle, "get_dimensions"))
108 }
109
110 pub fn is_focused(&self) -> Result<bool> {
112 Err(ObjectError::method_not_found(self.handle, "is_focused"))
113 }
114
115 pub fn set_right_status(&self, items: Vec<RenderItem>) -> Result<()> {
129 let mut pending = self.pending_status.lock().map_err(|_| {
130 ObjectError::method_not_found(self.handle, "set_right_status: lock poisoned")
131 })?;
132 pending.push((StatusBarSide::Right, items));
133 Ok(())
134 }
135
136 pub fn set_left_status(&self, items: Vec<RenderItem>) -> Result<()> {
150 let mut pending = self.pending_status.lock().map_err(|_| {
151 ObjectError::method_not_found(self.handle, "set_left_status: lock poisoned")
152 })?;
153 pending.push((StatusBarSide::Left, items));
154 Ok(())
155 }
156
157 pub fn clear_status(&self) -> Result<()> {
166 let mut pending = self.pending_status.lock().map_err(|_| {
167 ObjectError::method_not_found(self.handle, "clear_status: lock poisoned")
168 })?;
169 pending.push((StatusBarSide::Left, vec![]));
170 pending.push((StatusBarSide::Right, vec![]));
171 Ok(())
172 }
173
174 pub fn drain_pending_status(&self) -> Vec<(StatusBarSide, Vec<RenderItem>)> {
183 self.pending_status
184 .lock()
185 .map(|mut pending| pending.drain(..).collect())
186 .unwrap_or_default()
187 }
188
189 pub fn toast_notification(&self, _title: &str, _message: &str) -> Result<()> {
191 Err(ObjectError::method_not_found(
192 self.handle,
193 "toast_notification",
194 ))
195 }
196}
197
198use super::PaneProxy;
201use super::TabProxy;
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206
207 #[test]
208 fn test_window_proxy_creation() {
209 let handle = ObjectHandle::new(ObjectType::Window, 1, 0);
210 let proxy = WindowProxy::new(handle);
211 assert!(proxy.is_ok());
212
213 let proxy = proxy.unwrap();
214 assert_eq!(proxy.id(), 1);
215 assert_eq!(proxy.handle(), handle);
216 }
217
218 #[test]
219 fn test_window_proxy_type_validation() {
220 let pane_handle = ObjectHandle::new(ObjectType::Pane, 1, 0);
221 let result = WindowProxy::new(pane_handle);
222
223 assert!(result.is_err());
224 match result {
225 Err(ObjectError::TypeMismatch {
226 expected, actual, ..
227 }) => {
228 assert_eq!(expected, ObjectType::Window);
229 assert_eq!(actual, ObjectType::Pane);
230 }
231 _ => panic!("Expected TypeMismatch error"),
232 }
233 }
234
235 #[test]
236 fn test_window_proxy_methods_not_implemented() {
237 let handle = ObjectHandle::new(ObjectType::Window, 1, 0);
238 let proxy = WindowProxy::new(handle).unwrap();
239
240 assert!(matches!(
242 proxy.get_dimensions(),
243 Err(ObjectError::MethodNotFound { .. })
244 ));
245 assert!(matches!(
246 proxy.is_focused(),
247 Err(ObjectError::MethodNotFound { .. })
248 ));
249 assert!(matches!(
250 proxy.toast_notification("title", "message"),
251 Err(ObjectError::MethodNotFound { .. })
252 ));
253
254 assert!(proxy.set_right_status(vec![]).is_ok());
256 assert!(proxy.set_left_status(vec![]).is_ok());
257 assert!(proxy.clear_status().is_ok());
258 }
259
260 #[test]
261 fn test_window_proxy_clone() {
262 let handle = ObjectHandle::new(ObjectType::Window, 1, 0);
263 let proxy1 = WindowProxy::new(handle).unwrap();
264 let proxy2 = proxy1.clone();
265
266 assert_eq!(proxy1.handle(), proxy2.handle());
267 assert_eq!(proxy1.id(), proxy2.id());
268 }
269
270 #[test]
271 fn test_status_bar_methods() {
272 let handle = ObjectHandle::new(ObjectType::Window, 1, 0);
273 let proxy = WindowProxy::new(handle).unwrap();
274
275 let left_items = vec![RenderItem::Text("Left".to_string())];
277 assert!(proxy.set_left_status(left_items).is_ok());
278
279 let right_items = vec![RenderItem::Text("Right".to_string())];
281 assert!(proxy.set_right_status(right_items).is_ok());
282
283 let pending = proxy.drain_pending_status();
285 assert_eq!(pending.len(), 2);
286
287 let pending2 = proxy.drain_pending_status();
289 assert_eq!(pending2.len(), 0);
290 }
291
292 #[test]
293 fn test_status_bar_clear() {
294 let handle = ObjectHandle::new(ObjectType::Window, 1, 0);
295 let proxy = WindowProxy::new(handle).unwrap();
296
297 assert!(proxy.clear_status().is_ok());
299
300 let pending = proxy.drain_pending_status();
301 assert_eq!(pending.len(), 2);
302
303 for (_, items) in pending {
305 assert!(items.is_empty());
306 }
307 }
308
309 use super::super::registry::{ObjectRegistry, RegistryEntry};
311 use std::collections::HashMap;
312
313 #[derive(Clone)]
314 struct TestObject;
315
316 struct TestRegistry {
317 objects: HashMap<u64, RegistryEntry<TestObject>>,
318 parents: HashMap<u64, ObjectHandle>,
319 children: HashMap<u64, Vec<ObjectHandle>>,
320 generations: HashMap<u64, u32>,
321 }
322
323 impl TestRegistry {
324 fn new() -> Self {
325 Self {
326 objects: HashMap::new(),
327 parents: HashMap::new(),
328 children: HashMap::new(),
329 generations: HashMap::new(),
330 }
331 }
332
333 fn set_parent(&mut self, child: ObjectHandle, parent: ObjectHandle) {
334 self.parents.insert(child.id(), parent);
335 self.children
336 .entry(parent.id())
337 .or_insert_with(Vec::new)
338 .push(child);
339 }
340 }
341
342 impl ObjectRegistry<TestObject> for TestRegistry {
343 fn register(&mut self, object: TestObject) -> ObjectHandle {
344 let id = self.objects.len() as u64 + 1;
345 let generation = self.current_generation(id);
346 let handle = ObjectHandle::new(ObjectType::Window, id, generation);
347 self.objects.insert(id, RegistryEntry::new(handle, object));
348 handle
349 }
350
351 fn unregister(&mut self, handle: ObjectHandle) -> Result<TestObject> {
352 self.increment_generation(handle.id());
353 self.objects
354 .remove(&handle.id())
355 .map(|entry| entry.into_object())
356 .ok_or_else(|| ObjectError::not_found(handle))
357 }
358
359 fn get(&self, handle: ObjectHandle) -> Result<&TestObject> {
360 let current_gen = self.current_generation(handle.id());
361 if !handle.is_valid(current_gen) {
362 return Err(ObjectError::stale_handle(handle, current_gen));
363 }
364 self.objects
365 .get(&handle.id())
366 .map(|entry| entry.object())
367 .ok_or_else(|| ObjectError::not_found(handle))
368 }
369
370 fn get_mut(&mut self, handle: ObjectHandle) -> Result<&mut TestObject> {
371 let current_gen = self.current_generation(handle.id());
372 if !handle.is_valid(current_gen) {
373 return Err(ObjectError::stale_handle(handle, current_gen));
374 }
375 self.objects
376 .get_mut(&handle.id())
377 .map(|entry| entry.object_mut())
378 .ok_or_else(|| ObjectError::not_found(handle))
379 }
380
381 fn get_by_id(&self, id: u64) -> Option<&TestObject> {
382 self.objects.get(&id).map(|entry| entry.object())
383 }
384
385 fn next_id(&mut self) -> u64 {
386 self.objects.len() as u64 + 1
387 }
388
389 fn increment_generation(&mut self, id: u64) {
390 let gen = self.generations.entry(id).or_insert(0);
391 *gen = gen.wrapping_add(1);
392 }
393
394 fn current_generation(&self, id: u64) -> u32 {
395 self.generations.get(&id).copied().unwrap_or(0)
396 }
397
398 fn all_ids(&self) -> Vec<u64> {
399 self.objects.keys().copied().collect()
400 }
401
402 fn len(&self) -> usize {
403 self.objects.len()
404 }
405
406 fn get_parent(&self, handle: &ObjectHandle) -> Result<Option<ObjectHandle>> {
407 let current_gen = self.current_generation(handle.id());
408 if !handle.is_valid(current_gen) {
409 return Err(ObjectError::stale_handle(*handle, current_gen));
410 }
411 if !self.objects.contains_key(&handle.id()) {
412 return Err(ObjectError::not_found(*handle));
413 }
414 Ok(self.parents.get(&handle.id()).copied())
415 }
416
417 fn get_children(&self, handle: &ObjectHandle) -> Result<Vec<ObjectHandle>> {
418 let current_gen = self.current_generation(handle.id());
419 if !handle.is_valid(current_gen) {
420 return Err(ObjectError::stale_handle(*handle, current_gen));
421 }
422 if !self.objects.contains_key(&handle.id()) {
423 return Err(ObjectError::not_found(*handle));
424 }
425 Ok(self
426 .children
427 .get(&handle.id())
428 .cloned()
429 .unwrap_or_else(Vec::new))
430 }
431 }
432
433 #[test]
434 fn test_window_navigation_to_tabs() {
435 let mut registry = TestRegistry::new();
436
437 let window_handle = ObjectHandle::new(ObjectType::Window, 1, 0);
439 let tab1_handle = ObjectHandle::new(ObjectType::Tab, 2, 0);
440 let tab2_handle = ObjectHandle::new(ObjectType::Tab, 3, 0);
441
442 registry
443 .objects
444 .insert(1, RegistryEntry::new(window_handle, TestObject));
445 registry
446 .objects
447 .insert(2, RegistryEntry::new(tab1_handle, TestObject));
448 registry
449 .objects
450 .insert(3, RegistryEntry::new(tab2_handle, TestObject));
451
452 registry.set_parent(tab1_handle, window_handle);
454 registry.set_parent(tab2_handle, window_handle);
455
456 let window_proxy = WindowProxy::new(window_handle).unwrap();
457 let tabs = window_proxy.tabs(®istry).unwrap();
458
459 assert_eq!(tabs.len(), 2);
460 assert!(tabs.iter().any(|t| t.id() == 2));
461 assert!(tabs.iter().any(|t| t.id() == 3));
462 }
463
464 #[test]
465 fn test_window_navigation_active_tab() {
466 let mut registry = TestRegistry::new();
467
468 let window_handle = ObjectHandle::new(ObjectType::Window, 1, 0);
470 let tab1_handle = ObjectHandle::new(ObjectType::Tab, 2, 0);
471 let tab2_handle = ObjectHandle::new(ObjectType::Tab, 3, 0);
472
473 registry
474 .objects
475 .insert(1, RegistryEntry::new(window_handle, TestObject));
476 registry
477 .objects
478 .insert(2, RegistryEntry::new(tab1_handle, TestObject));
479 registry
480 .objects
481 .insert(3, RegistryEntry::new(tab2_handle, TestObject));
482
483 registry.set_parent(tab1_handle, window_handle);
485 registry.set_parent(tab2_handle, window_handle);
486
487 let window_proxy = WindowProxy::new(window_handle).unwrap();
488 let active_tab = window_proxy.active_tab(®istry).unwrap();
489
490 assert!(active_tab.id() == 2 || active_tab.id() == 3);
492 }
493
494 #[test]
495 fn test_window_navigation_active_pane() {
496 let mut registry = TestRegistry::new();
497
498 let window_handle = ObjectHandle::new(ObjectType::Window, 1, 0);
500 let tab_handle = ObjectHandle::new(ObjectType::Tab, 2, 0);
501 let pane_handle = ObjectHandle::new(ObjectType::Pane, 3, 0);
502
503 registry
504 .objects
505 .insert(1, RegistryEntry::new(window_handle, TestObject));
506 registry
507 .objects
508 .insert(2, RegistryEntry::new(tab_handle, TestObject));
509 registry
510 .objects
511 .insert(3, RegistryEntry::new(pane_handle, TestObject));
512
513 registry.set_parent(tab_handle, window_handle);
515 registry.set_parent(pane_handle, tab_handle);
516
517 let window_proxy = WindowProxy::new(window_handle).unwrap();
518 let active_pane = window_proxy.active_pane(®istry).unwrap();
519
520 assert_eq!(active_pane.id(), 3);
521 }
522
523 #[test]
524 fn test_window_navigation_no_tabs() {
525 let mut registry = TestRegistry::new();
526
527 let window_handle = ObjectHandle::new(ObjectType::Window, 1, 0);
528 registry
529 .objects
530 .insert(1, RegistryEntry::new(window_handle, TestObject));
531
532 let window_proxy = WindowProxy::new(window_handle).unwrap();
533
534 let tabs = window_proxy.tabs(®istry).unwrap();
536 assert!(tabs.is_empty());
537
538 let result = window_proxy.active_tab(®istry);
540 assert!(result.is_err());
541
542 let result = window_proxy.active_pane(®istry);
544 assert!(result.is_err());
545 }
546
547 #[test]
548 fn test_window_navigation_wrong_child_type() {
549 let mut registry = TestRegistry::new();
550
551 let window_handle = ObjectHandle::new(ObjectType::Window, 1, 0);
553 let pane_handle = ObjectHandle::new(ObjectType::Pane, 2, 0);
554
555 registry
556 .objects
557 .insert(1, RegistryEntry::new(window_handle, TestObject));
558 registry
559 .objects
560 .insert(2, RegistryEntry::new(pane_handle, TestObject));
561
562 registry.set_parent(pane_handle, window_handle);
564
565 let window_proxy = WindowProxy::new(window_handle).unwrap();
566 let result = window_proxy.tabs(®istry);
567
568 assert!(matches!(result, Err(ObjectError::TypeMismatch { .. })));
570 }
571
572 #[test]
573 fn test_window_navigation_complete_hierarchy() {
574 let mut registry = TestRegistry::new();
575
576 let window_handle = ObjectHandle::new(ObjectType::Window, 1, 0);
578 let tab1_handle = ObjectHandle::new(ObjectType::Tab, 2, 0);
579 let tab2_handle = ObjectHandle::new(ObjectType::Tab, 3, 0);
580 let pane1_handle = ObjectHandle::new(ObjectType::Pane, 4, 0);
581 let pane2_handle = ObjectHandle::new(ObjectType::Pane, 5, 0);
582
583 registry
584 .objects
585 .insert(1, RegistryEntry::new(window_handle, TestObject));
586 registry
587 .objects
588 .insert(2, RegistryEntry::new(tab1_handle, TestObject));
589 registry
590 .objects
591 .insert(3, RegistryEntry::new(tab2_handle, TestObject));
592 registry
593 .objects
594 .insert(4, RegistryEntry::new(pane1_handle, TestObject));
595 registry
596 .objects
597 .insert(5, RegistryEntry::new(pane2_handle, TestObject));
598
599 registry.set_parent(tab1_handle, window_handle);
601 registry.set_parent(tab2_handle, window_handle);
602 registry.set_parent(pane1_handle, tab1_handle);
603 registry.set_parent(pane2_handle, tab2_handle);
604
605 let window_proxy = WindowProxy::new(window_handle).unwrap();
606
607 let tabs = window_proxy.tabs(®istry).unwrap();
609 assert_eq!(tabs.len(), 2);
610
611 let active_tab = window_proxy.active_tab(®istry).unwrap();
613 assert!(active_tab.id() == 2 || active_tab.id() == 3);
614
615 let active_pane = window_proxy.active_pane(®istry).unwrap();
617 assert!(active_pane.id() == 4 || active_pane.id() == 5);
618 }
619}