Skip to main content

fret_interaction/
drag.rs

1//! Drag gesture helpers (thresholding and deltas).
2//!
3//! This is a deliberately small primitive that can be reused by:
4//! - in-window floating windows (`imui`),
5//! - canvas-space node graph interactions,
6//! - docking/multi-window drag choreography.
7
8use fret_core::{Point, Px};
9
10#[derive(Debug, Clone, Copy, PartialEq)]
11pub struct DragThreshold {
12    pub px: Px,
13}
14
15impl DragThreshold {
16    pub fn new(px: Px) -> Self {
17        Self { px }
18    }
19
20    pub fn distance_sq_exceeded(self, start: Point, current: Point) -> bool {
21        let dx = current.x.0 - start.x.0;
22        let dy = current.y.0 - start.y.0;
23        if !dx.is_finite() || !dy.is_finite() {
24            return false;
25        }
26
27        let dist2 = dx * dx + dy * dy;
28        let threshold = self.px.0;
29        if !threshold.is_finite() || threshold <= 0.0 {
30            return dist2 > 0.0;
31        }
32
33        dist2 >= threshold * threshold
34    }
35}
36
37impl Default for DragThreshold {
38    fn default() -> Self {
39        Self { px: Px(6.0) }
40    }
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46
47    #[test]
48    fn default_threshold_matches_imgui_default() {
49        assert_eq!(DragThreshold::default().px, Px(6.0));
50    }
51
52    #[test]
53    fn negative_threshold_is_treated_as_zero() {
54        let t = DragThreshold::new(Px(-10.0));
55        let start = Point::new(Px(0.0), Px(0.0));
56        let current = Point::new(Px(0.0), Px(0.0));
57        assert!(!t.distance_sq_exceeded(start, current));
58
59        let moved = Point::new(Px(1.0), Px(0.0));
60        assert!(t.distance_sq_exceeded(start, moved));
61    }
62
63    #[test]
64    fn threshold_exceeded_is_inclusive() {
65        let t = DragThreshold::new(Px(2.0));
66        let start = Point::new(Px(0.0), Px(0.0));
67        let current = Point::new(Px(2.0), Px(0.0));
68        assert!(t.distance_sq_exceeded(start, current));
69    }
70}