1use super::id::PaneId;
4use super::layout::{Layout, Rect};
5use super::tree::PaneTree;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8pub enum Direction {
9 Up,
10 Down,
11 Left,
12 Right,
13}
14
15const EPS: f32 = 0.001;
16
17pub fn neighbor(layout: &Layout, from: PaneId, direction: Direction) -> Option<PaneId> {
21 let origin = layout.pane_rect(from)?;
22 let mut best: Option<(f32, f32, PaneId)> = None;
23 for (pane, rect) in &layout.panes {
24 if *pane == from {
25 continue;
26 }
27 let Some(gap) = edgegap(origin, *rect, direction) else {
28 continue;
29 };
30 let dist = centerdistance(origin, *rect, direction);
31 let closer = match best {
32 None => true,
33 Some((bgap, bdist, _)) => {
34 gap < bgap - EPS || ((gap - bgap).abs() <= EPS && dist < bdist)
35 }
36 };
37 if closer {
38 best = Some((gap, dist, *pane));
39 }
40 }
41 best.map(|(_, _, pane)| pane)
42}
43
44pub fn next(tree: &PaneTree, from: PaneId) -> Option<PaneId> {
46 let panes = tree.panes();
47 let i = panes.iter().position(|p| *p == from)?;
48 Some(panes[(i + 1) % panes.len()])
49}
50
51pub fn prev(tree: &PaneTree, from: PaneId) -> Option<PaneId> {
53 let panes = tree.panes();
54 let i = panes.iter().position(|p| *p == from)?;
55 Some(panes[(i + panes.len() - 1) % panes.len()])
56}
57
58fn edgegap(from: Rect, to: Rect, dir: Direction) -> Option<f32> {
61 let gap = match dir {
62 Direction::Left => from.x - (to.x + to.w),
63 Direction::Right => to.x - (from.x + from.w),
64 Direction::Up => from.y - (to.y + to.h),
65 Direction::Down => to.y - (from.y + from.h),
66 };
67 (gap >= -EPS).then(|| gap.max(0.0))
68}
69
70fn centerdistance(from: Rect, to: Rect, dir: Direction) -> f32 {
71 let (fx, fy) = from.center();
72 let (tx, ty) = to.center();
73 match dir {
74 Direction::Left | Direction::Right => (fy - ty).abs(),
75 Direction::Up | Direction::Down => (fx - tx).abs(),
76 }
77}