use crate::primitives::resizable_grid::types::{
LayoutNode, PaneId, PaneInfo, ResizableGrid, SplitAreas, SplitAxis, SplitDividerLayout,
};
use ratatui::layout::Rect;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PaneLayout {
pane_id: PaneId,
area: Rect,
}
impl PaneLayout {
pub(crate) fn new(pane_id: PaneId, area: Rect) -> Self {
Self { pane_id, area }
}
pub fn area(&self) -> Rect {
self.area
}
pub fn pane_id(&self) -> PaneId {
self.pane_id
}
}
impl ResizableGrid {
pub fn layout_panes(&self, area: Rect) -> Vec<PaneLayout> {
let mut layouts = Vec::new();
let mut stack = vec![(self.root_index, area)];
while let Some((node_index, node_area)) = stack.pop() {
let Some(node) = self.nodes.get(node_index) else {
continue;
};
match node {
LayoutNode::Pane { id } => {
layouts.push(PaneLayout::new(*id, node_area));
}
LayoutNode::Split {
axis,
ratio,
first,
second,
} => match axis {
SplitAxis::Vertical => {
let first_width = ((node_area.width as u32 * *ratio as u32) / 100) as u16;
let second_width = node_area.width.saturating_sub(first_width);
let first_area = Rect {
x: node_area.x,
y: node_area.y,
width: first_width,
height: node_area.height,
};
let second_area = Rect {
x: node_area.x.saturating_add(first_width),
y: node_area.y,
width: second_width,
height: node_area.height,
};
stack.push((*second, second_area));
stack.push((*first, first_area));
}
SplitAxis::Horizontal => {
let first_height = ((node_area.height as u32 * *ratio as u32) / 100) as u16;
let second_height = node_area.height.saturating_sub(first_height);
let first_area = Rect {
x: node_area.x,
y: node_area.y,
width: node_area.width,
height: first_height,
};
let second_area = Rect {
x: node_area.x,
y: node_area.y.saturating_add(first_height),
width: node_area.width,
height: second_height,
};
stack.push((*second, second_area));
stack.push((*first, first_area));
}
},
}
}
layouts
}
pub fn layout_dividers(&self, area: Rect) -> Vec<SplitDividerLayout> {
let mut dividers = Vec::new();
let mut stack = vec![(self.root_index, area)];
while let Some((node_index, node_area)) = stack.pop() {
let Some(node) = self.nodes.get(node_index) else {
continue;
};
if let LayoutNode::Split {
axis,
ratio,
first,
second,
} = node
{
dividers.push(SplitDividerLayout {
split_index: node_index,
axis: *axis,
area: node_area,
ratio: *ratio,
});
match axis {
SplitAxis::Vertical => {
let first_width = ((node_area.width as u32 * *ratio as u32) / 100) as u16;
let second_width = node_area.width.saturating_sub(first_width);
let first_area = Rect {
x: node_area.x,
y: node_area.y,
width: first_width,
height: node_area.height,
};
let second_area = Rect {
x: node_area.x.saturating_add(first_width),
y: node_area.y,
width: second_width,
height: node_area.height,
};
stack.push((*second, second_area));
stack.push((*first, first_area));
}
SplitAxis::Horizontal => {
let first_height = ((node_area.height as u32 * *ratio as u32) / 100) as u16;
let second_height = node_area.height.saturating_sub(first_height);
let first_area = Rect {
x: node_area.x,
y: node_area.y,
width: node_area.width,
height: first_height,
};
let second_area = Rect {
x: node_area.x,
y: node_area.y.saturating_add(first_height),
width: node_area.width,
height: second_height,
};
stack.push((*second, second_area));
stack.push((*first, first_area));
}
}
}
}
dividers
}
pub fn calculate_split_area(&self, area: Rect, split_percent: u16) -> SplitAreas {
let left_width = (area.width as u32 * split_percent as u32 / 100) as u16;
let left = Rect {
x: area.x,
y: area.y,
width: left_width,
height: area.height,
};
let right = Rect {
x: area.x + left_width,
y: area.y,
width: area.width.saturating_sub(left_width),
height: area.height,
};
SplitAreas { left, right }
}
pub fn get_panes(&self, area: Rect) -> Vec<PaneInfo> {
self.layout_panes(area)
.into_iter()
.map(|layout| PaneInfo {
id: layout.pane_id(),
area: layout.area(),
})
.collect()
}
}