use super::id::PaneId;
use super::layout::{Layout, Rect};
use super::tree::PaneTree;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Direction {
Up,
Down,
Left,
Right,
}
const EPS: f32 = 0.001;
pub fn neighbor(layout: &Layout, from: PaneId, direction: Direction) -> Option<PaneId> {
let origin = layout.pane_rect(from)?;
let mut best: Option<(f32, f32, PaneId)> = None;
for (pane, rect) in &layout.panes {
if *pane == from {
continue;
}
let Some(gap) = edgegap(origin, *rect, direction) else {
continue;
};
let dist = centerdistance(origin, *rect, direction);
let closer = match best {
None => true,
Some((bgap, bdist, _)) => {
gap < bgap - EPS || ((gap - bgap).abs() <= EPS && dist < bdist)
}
};
if closer {
best = Some((gap, dist, *pane));
}
}
best.map(|(_, _, pane)| pane)
}
pub fn next(tree: &PaneTree, from: PaneId) -> Option<PaneId> {
let panes = tree.panes();
let i = panes.iter().position(|p| *p == from)?;
Some(panes[(i + 1) % panes.len()])
}
pub fn prev(tree: &PaneTree, from: PaneId) -> Option<PaneId> {
let panes = tree.panes();
let i = panes.iter().position(|p| *p == from)?;
Some(panes[(i + panes.len() - 1) % panes.len()])
}
fn edgegap(from: Rect, to: Rect, dir: Direction) -> Option<f32> {
let gap = match dir {
Direction::Left => from.x - (to.x + to.w),
Direction::Right => to.x - (from.x + from.w),
Direction::Up => from.y - (to.y + to.h),
Direction::Down => to.y - (from.y + from.h),
};
(gap >= -EPS).then(|| gap.max(0.0))
}
fn centerdistance(from: Rect, to: Rect, dir: Direction) -> f32 {
let (fx, fy) = from.center();
let (tx, ty) = to.center();
match dir {
Direction::Left | Direction::Right => (fy - ty).abs(),
Direction::Up | Direction::Down => (fx - tx).abs(),
}
}