Skip to main content

guise/panegroup/
layout.rs

1//! Pure rect math: turn a [`PaneTree`] into pane and divider rects.
2
3use crate::SplitDirection;
4use super::id::{PaneId, SplitId};
5use super::tree::{Node, PaneTree};
6
7#[derive(Debug, Clone, Copy, PartialEq)]
8pub struct Rect {
9    pub x: f32,
10    pub y: f32,
11    pub w: f32,
12    pub h: f32,
13}
14
15impl Rect {
16    pub fn new(x: f32, y: f32, w: f32, h: f32) -> Self {
17        Self { x, y, w, h }
18    }
19
20    pub fn center(&self) -> (f32, f32) {
21        (self.x + self.w / 2.0, self.y + self.h / 2.0)
22    }
23}
24
25/// Computed pane and divider rects, both in layout order.
26#[derive(Debug, Clone, PartialEq)]
27pub struct Layout {
28    pub panes: Vec<(PaneId, Rect)>,
29    pub dividers: Vec<(SplitId, Rect, SplitDirection)>,
30}
31
32impl Layout {
33    pub fn pane_rect(&self, pane: PaneId) -> Option<Rect> {
34        self.panes.iter().find(|(p, _)| *p == pane).map(|(_, r)| *r)
35    }
36}
37
38/// Lay out `tree` inside `rect`, reserving `divider` thickness per split.
39pub fn compute_layout(tree: &PaneTree, rect: Rect, divider: f32) -> Layout {
40    let mut layout = Layout {
41        panes: Vec::new(),
42        dividers: Vec::new(),
43    };
44    walk(tree.root(), rect, divider, &mut layout);
45    layout
46}
47
48fn walk(node: &Node, rect: Rect, divider: f32, out: &mut Layout) {
49    match node {
50        Node::Leaf(pane) => out.panes.push((*pane, rect)),
51        Node::Split {
52            id,
53            axis,
54            ratio,
55            first,
56            second,
57        } => {
58            let (frect, drect, srect) = match axis {
59                SplitDirection::Horizontal => {
60                    let avail = (rect.w - divider).max(0.0);
61                    let fw = avail * ratio;
62                    (
63                        Rect::new(rect.x, rect.y, fw, rect.h),
64                        Rect::new(rect.x + fw, rect.y, divider, rect.h),
65                        Rect::new(rect.x + fw + divider, rect.y, avail - fw, rect.h),
66                    )
67                }
68                SplitDirection::Vertical => {
69                    let avail = (rect.h - divider).max(0.0);
70                    let fh = avail * ratio;
71                    (
72                        Rect::new(rect.x, rect.y, rect.w, fh),
73                        Rect::new(rect.x, rect.y + fh, rect.w, divider),
74                        Rect::new(rect.x, rect.y + fh + divider, rect.w, avail - fh),
75                    )
76                }
77            };
78            out.dividers.push((*id, drect, *axis));
79            walk(first, frect, divider, out);
80            walk(second, srect, divider, out);
81        }
82    }
83}