Skip to main content

hadrone_core/
collision.rs

1//! Pluggable collision resolution when an item overlaps others after move/resize.
2
3use crate::LayoutItem;
4
5/// Resolves overlaps after the focused item has been placed.
6pub trait CollisionResolver: Send + Sync {
7    fn resolve_collisions(&self, layout: &mut Vec<LayoutItem>, moved_id: &str);
8}
9
10/// Default RGL-style behavior: overlapping non-static items are pushed downward.
11#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
12pub struct PushDownResolver;
13
14impl CollisionResolver for PushDownResolver {
15    fn resolve_collisions(&self, layout: &mut Vec<LayoutItem>, moved_id: &str) {
16        let Some(moved_item) = layout.iter().find(|i| i.id == moved_id).cloned() else {
17            return;
18        };
19
20        let mut items_to_move = Vec::new();
21        for item in layout.iter() {
22            if item.id != moved_id && crate::collides(&moved_item, item) {
23                items_to_move.push(item.id.clone());
24            }
25        }
26
27        for id in items_to_move {
28            if let Some(index) = layout.iter().position(|i| i.id == id)
29                && !layout[index].is_static
30            {
31                layout[index].y = moved_item.y + moved_item.h;
32                let next_id = layout[index].id.clone();
33                self.resolve_collisions(layout, &next_id);
34            }
35        }
36    }
37}
38
39/// No automatic displacement; overlaps remain until the next compaction.
40#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
41pub struct NoopCollisionResolver;
42
43impl CollisionResolver for NoopCollisionResolver {
44    fn resolve_collisions(&self, _layout: &mut Vec<LayoutItem>, _moved_id: &str) {}
45}
46
47/// Built-in strategies for [`crate::LayoutEngine`].
48#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
49pub enum CollisionStrategy {
50    #[default]
51    PushDown,
52    /// Skip collision resolution (may leave overlaps until compaction).
53    None,
54}
55
56impl CollisionStrategy {
57    pub fn build(self) -> Box<dyn CollisionResolver> {
58        match self {
59            CollisionStrategy::PushDown => Box::new(PushDownResolver),
60            CollisionStrategy::None => Box::new(NoopCollisionResolver),
61        }
62    }
63}