flow_rs_core/graph/
drag_operations.rs

1//! Drag and drop operations for graph nodes
2
3use std::collections::HashSet;
4
5use crate::error::{FlowError, Result};
6use crate::types::{NodeId, Position, Rect};
7
8use super::{Graph, Node};
9
10impl<N, E> Graph<N, E> {
11    /// Apply drag operation to selected nodes
12    pub fn apply_node_drag(
13        &mut self,
14        selected_nodes: &HashSet<NodeId>,
15        delta: Position,
16    ) -> Result<()> {
17        self.apply_node_drag_with_transform(selected_nodes, delta, |pos, _| pos)
18    }
19
20    /// Apply drag operation with bounds constraint
21    pub fn apply_node_drag_with_bounds(
22        &mut self,
23        selected_nodes: &HashSet<NodeId>,
24        delta: Position,
25        bounds: Option<Rect>,
26    ) -> Result<()> {
27        self.apply_node_drag_with_transform(selected_nodes, delta, |new_pos, node| {
28            if let Some(bounds) = bounds {
29                Position::new(
30                    new_pos
31                        .x
32                        .max(bounds.x)
33                        .min(bounds.x + bounds.width - node.size.width),
34                    new_pos
35                        .y
36                        .max(bounds.y)
37                        .min(bounds.y + bounds.height - node.size.height),
38                )
39            } else {
40                new_pos
41            }
42        })
43    }
44
45    /// Apply drag operation with grid snapping
46    pub fn apply_node_drag_with_snap(
47        &mut self,
48        selected_nodes: &HashSet<NodeId>,
49        delta: Position,
50        grid_size: f64,
51    ) -> Result<()> {
52        self.apply_node_drag_with_transform(selected_nodes, delta, |new_pos, _| {
53            Position::new(
54                (new_pos.x / grid_size).round() * grid_size,
55                (new_pos.y / grid_size).round() * grid_size,
56            )
57        })
58    }
59
60    /// Apply drag operation with custom constraint function
61    pub fn apply_node_drag_with_constraint<F>(
62        &mut self,
63        selected_nodes: &HashSet<NodeId>,
64        delta: Position,
65        constraint: F,
66    ) -> Result<()>
67    where
68        F: Fn(Position) -> Position,
69    {
70        self.apply_node_drag_with_transform(selected_nodes, delta, |new_pos, _| constraint(new_pos))
71    }
72
73    /// Internal method for applying drag operations with position transformation
74    fn apply_node_drag_with_transform<F>(
75        &mut self,
76        selected_nodes: &HashSet<NodeId>,
77        delta: Position,
78        transform: F,
79    ) -> Result<()>
80    where
81        F: Fn(Position, &Node<N>) -> Position,
82    {
83        // Pre-validate all nodes exist to fail fast
84        for node_id in selected_nodes {
85            if !self.nodes.contains_key(node_id) {
86                return Err(FlowError::node_not_found(node_id.as_str()));
87            }
88        }
89
90        // Apply transformations
91        for node_id in selected_nodes {
92            if let Some(node) = self.get_node_mut(node_id) {
93                let new_pos = Position::new(node.position.x + delta.x, node.position.y + delta.y);
94                node.position = transform(new_pos, node);
95            }
96        }
97
98        Ok(())
99    }
100
101    /// Create a drag operation for undo/redo support
102    pub fn create_drag_operation(
103        &self,
104        selected_nodes: &HashSet<NodeId>,
105        delta: Position,
106    ) -> Result<crate::drag_operations::DragOperation> {
107        // Validate all nodes exist before creating operation
108        for node_id in selected_nodes {
109            if !self.nodes.contains_key(node_id) {
110                return Err(FlowError::node_not_found(node_id.as_str()));
111            }
112        }
113
114        Ok(crate::drag_operations::DragOperation::new(
115            selected_nodes.clone(),
116            delta,
117        ))
118    }
119}