1use crate::types::cpt_ids::EzCptIds;
2use crate::{Result, ViewError};
3use ratatui::buffer::Buffer;
4use ratatui::layout::{Layout, Rect};
5use std::collections::VecDeque;
6use std::fmt::Debug;
7use std::sync::atomic::{AtomicU32, Ordering};
8
9static LAYOUT_COUNT: AtomicU32 = AtomicU32::new(0);
10
11#[derive(Debug, Clone)]
13pub struct CustomLayout<CID>
14where
15 CID: EzCptIds,
16{
17 #[allow(dead_code)]
19 name: String,
20 layout: Layout,
22 items: VecDeque<LayoutItem<CID>>,
26}
27#[derive(Debug, Clone)]
29pub enum LayoutItem<CID>
30where
31 CID: EzCptIds,
32{
33 NestedLayout(CustomLayout<CID>),
35 Component(CID),
37}
38impl<CID> CustomLayout<CID>
39where
40 CID: EzCptIds,
41{
42 pub fn named<S: Into<String>>(
47 name: S,
48 layout: Layout,
49 items: VecDeque<LayoutItem<CID>>,
50 ) -> Result<Self> {
51 let name = name.into();
52 Self::check(name.clone(), &layout, &items)?;
53 LAYOUT_COUNT.fetch_add(1, Ordering::Relaxed);
54 Ok(Self {
55 name,
56 layout,
57 items,
58 })
59 }
60 fn check(name: String, layout: &Layout, items: &VecDeque<LayoutItem<CID>>) -> Result<()> {
61 let dummy_rect = Rect::new(0, 0, 0, 0);
62 let splited = layout.split(dummy_rect);
63 if splited.len() != items.len() {
64 return Err(
65 ViewError::MismatchConstraintsAndCpts(name, splited.len(), items.len()).into(),
66 );
67 }
68 Ok(())
69 }
70
71 #[must_use]
73 pub fn split(&self, area: Rect) -> Vec<(Rect, &LayoutItem<CID>)> {
74 let areas = self.layout.split(area);
75 areas.iter().copied().zip(&self.items).collect()
76 }
77
78 pub fn contains(&self, id: &CID) -> bool {
80 self.items.iter().any(|item| match item {
81 LayoutItem::Component(cpt_id) => cpt_id == id,
82 LayoutItem::NestedLayout(layout) => layout.contains(id),
83 })
84 }
85
86 pub(crate) fn draw<CALLBACK>(&self, area: Rect, buf: &mut Buffer, draw_id: &mut CALLBACK)
87 where
88 CALLBACK: for<'b> FnMut((CID, Rect, &'b mut Buffer)),
89 {
90 let tulples = self.split(area);
91 for (inner_area, item) in tulples {
92 match item {
93 LayoutItem::NestedLayout(custom_layout) => {
94 custom_layout.draw(inner_area, buf, draw_id);
95 }
96 LayoutItem::Component(cid) => {
97 draw_id((cid.clone(), inner_area, buf));
98 }
99 }
100 }
101 }
102}