use std::collections::HashMap;
use super::{
DockPanel, DockingTree, Branch, PanelNode, LeafId, PanelRect,
DropZone, Separator, SeparatorOrientation, SeparatorLevel,
CornerHandle,
};
pub fn compute_leaf_rects<P: DockPanel>(
tree: &DockingTree<P>,
area: PanelRect,
) -> HashMap<LeafId, PanelRect> {
let mut rects = HashMap::new();
collect_leaf_rects_from_branch(tree.root(), area, &mut rects);
rects
}
pub fn collect_leaf_rects_from_branch<P: DockPanel>(
branch: &Branch<P>,
branch_rect: PanelRect,
out: &mut HashMap<LeafId, PanelRect>,
) {
let child_rects = DockingTree::<P>::compute_child_rects(branch, branch_rect);
for (child, rect) in branch.children.iter().zip(child_rects.iter()) {
match child {
PanelNode::Leaf(leaf) => {
if !leaf.hidden {
out.insert(leaf.id, *rect);
}
}
PanelNode::Branch(b) => {
collect_leaf_rects_from_branch(b, *rect, out);
}
}
}
}
pub fn generate_separators<P: DockPanel>(
branch: &Branch<P>,
branch_rect: PanelRect,
out: &mut Vec<Separator>,
) {
let child_rects = DockingTree::<P>::compute_child_rects(branch, branch_rect);
if child_rects.len() >= 2 {
let child_panel_rects: Vec<(u64, PanelRect)> = branch.children.iter()
.zip(child_rects.iter())
.filter(|(node, _)| !node.is_hidden())
.map(|(node, wr)| (node.raw_id(), *wr))
.collect();
for i in 0..child_panel_rects.len() {
for j in (i + 1)..child_panel_rects.len() {
let (_, r1) = child_panel_rects[i];
let (_, r2) = child_panel_rects[j];
let h_overlap = r1.y.max(r2.y) < (r1.y + r1.height).min(r2.y + r2.height) - 1.0;
let v_overlap = r1.x.max(r2.x) < (r1.x + r1.width).min(r2.x + r2.width) - 1.0;
if h_overlap {
let left = if r1.x < r2.x { r1 } else { r2 };
let right = if r1.x < r2.x { r2 } else { r1 };
let gap = right.x - (left.x + left.width);
if gap <= 15.0 {
let sep_x = left.x + left.width + gap / 2.0;
let sep_y = r1.y.max(r2.y);
let sep_h = (r1.y + r1.height).min(r2.y + r2.height) - sep_y;
let (ca, cb) = if child_panel_rects[i].1.x < child_panel_rects[j].1.x {
(child_panel_rects[i].0, child_panel_rects[j].0)
} else {
(child_panel_rects[j].0, child_panel_rects[i].0)
};
out.push(Separator::new(
SeparatorOrientation::Vertical,
sep_x, sep_y, sep_h,
SeparatorLevel::Node { parent_id: branch.id, child_a: ca, child_b: cb },
));
}
} else if v_overlap {
let top = if r1.y < r2.y { r1 } else { r2 };
let bottom = if r1.y < r2.y { r2 } else { r1 };
let gap = bottom.y - (top.y + top.height);
if gap <= 15.0 {
let sep_y = top.y + top.height + gap / 2.0;
let sep_x = r1.x.max(r2.x);
let sep_w = (r1.x + r1.width).min(r2.x + r2.width) - sep_x;
let (ca, cb) = if child_panel_rects[i].1.y < child_panel_rects[j].1.y {
(child_panel_rects[i].0, child_panel_rects[j].0)
} else {
(child_panel_rects[j].0, child_panel_rects[i].0)
};
out.push(Separator::new(
SeparatorOrientation::Horizontal,
sep_y, sep_x, sep_w,
SeparatorLevel::Node { parent_id: branch.id, child_a: ca, child_b: cb },
));
}
}
}
}
}
for (child, rect) in branch.children.iter().zip(child_rects.iter()) {
if let PanelNode::Branch(b) = child {
generate_separators(b, *rect, out);
}
}
}
pub fn detect_corners(separators: &[Separator]) -> Vec<CornerHandle> {
let mut out = Vec::new();
for (vi, v_sep) in separators.iter().enumerate() {
if v_sep.orientation != SeparatorOrientation::Vertical { continue; }
for (hi, h_sep) in separators.iter().enumerate() {
if h_sep.orientation != SeparatorOrientation::Horizontal { continue; }
out.push(CornerHandle {
v_separator_idx: vi,
h_separator_idx: hi,
x: v_sep.position,
y: h_sep.position,
});
}
}
out
}
pub fn detect_drop_zone(x: f32, y: f32, width: f32, height: f32) -> DropZone {
let center_margin = 0.20;
let cx = width * center_margin;
let cy = height * center_margin;
if x > cx && x < width - cx && y > cy && y < height - cy {
return DropZone::Center;
}
let dist_left = x;
let dist_right = width - x;
let dist_top = y;
let dist_bottom = height - y;
let min_dist = dist_left.min(dist_right).min(dist_top).min(dist_bottom);
if min_dist == dist_left {
DropZone::Left
} else if min_dist == dist_right {
DropZone::Right
} else if min_dist == dist_top {
DropZone::Up
} else {
DropZone::Down
}
}