ratatui_toolkit/primitives/split_layout/methods/
layout_panes.rs

1use ratatui::layout::Rect;
2
3use crate::primitives::split_layout::enums::layout_node::LayoutNode;
4use crate::primitives::split_layout::pane_layout::PaneLayout;
5use crate::primitives::split_layout::SplitAxis;
6use crate::primitives::split_layout::SplitLayout;
7
8impl SplitLayout {
9    /// Calculates pane rectangles for the current split tree.
10    ///
11    /// # Arguments
12    /// - `area`: The available rectangle to divide among panes.
13    ///
14    /// # Returns
15    /// A vector of `PaneLayout` values for each leaf pane.
16    ///
17    /// # Errors
18    /// - None.
19    ///
20    /// # Panics
21    /// - Does not panic.
22    ///
23    /// # Safety
24    /// - No safety requirements.
25    ///
26    /// # Performance
27    /// - O(n) where n is the number of nodes.
28    ///
29    /// # Example
30    /// ```rust
31    /// use ratatui::layout::Rect;
32    /// use ratatui_toolkit::primitives::split_layout::SplitLayout;
33    ///
34    /// let layout = SplitLayout::new(0);
35    /// let panes = layout.layout_panes(Rect::new(0, 0, 120, 40));
36    /// ```
37    pub fn layout_panes(&self, area: Rect) -> Vec<PaneLayout> {
38        let mut layouts = Vec::new();
39        let mut stack = vec![(self.root_index, area)];
40
41        while let Some((node_index, node_area)) = stack.pop() {
42            let Some(node) = self.nodes.get(node_index) else {
43                continue;
44            };
45
46            match node {
47                LayoutNode::Pane { id } => {
48                    layouts.push(PaneLayout::new(*id, node_area));
49                }
50                LayoutNode::Split {
51                    axis,
52                    ratio,
53                    first,
54                    second,
55                } => match axis {
56                    SplitAxis::Vertical => {
57                        let first_width = ((node_area.width as u32 * *ratio as u32) / 100) as u16;
58                        let second_width = node_area.width.saturating_sub(first_width);
59                        let first_area = Rect {
60                            x: node_area.x,
61                            y: node_area.y,
62                            width: first_width,
63                            height: node_area.height,
64                        };
65                        let second_area = Rect {
66                            x: node_area.x.saturating_add(first_width),
67                            y: node_area.y,
68                            width: second_width,
69                            height: node_area.height,
70                        };
71                        stack.push((*second, second_area));
72                        stack.push((*first, first_area));
73                    }
74                    SplitAxis::Horizontal => {
75                        let first_height = ((node_area.height as u32 * *ratio as u32) / 100) as u16;
76                        let second_height = node_area.height.saturating_sub(first_height);
77                        let first_area = Rect {
78                            x: node_area.x,
79                            y: node_area.y,
80                            width: node_area.width,
81                            height: first_height,
82                        };
83                        let second_area = Rect {
84                            x: node_area.x,
85                            y: node_area.y.saturating_add(first_height),
86                            width: node_area.width,
87                            height: second_height,
88                        };
89                        stack.push((*second, second_area));
90                        stack.push((*first, first_area));
91                    }
92                },
93            }
94        }
95
96        layouts
97    }
98}