1#[derive(Clone, Copy, Debug, PartialEq)]
10pub enum ScrollDelta {
11 Pixels {
13 x: f64,
15 y: f64,
17 },
18 WheelTicks {
21 x: f64,
23 y: f64,
25 },
26}
27
28impl ScrollDelta {
29 #[must_use]
31 pub const fn pixels(x: f64, y: f64) -> Self {
32 Self::Pixels { x, y }
33 }
34
35 #[must_use]
37 pub const fn wheel_ticks(x: f64, y: f64) -> Self {
38 Self::WheelTicks { x, y }
39 }
40
41 #[must_use]
43 pub const fn x(self) -> f64 {
44 match self {
45 Self::Pixels { x, .. } | Self::WheelTicks { x, .. } => x,
46 }
47 }
48
49 #[must_use]
51 pub const fn y(self) -> f64 {
52 match self {
53 Self::Pixels { y, .. } | Self::WheelTicks { y, .. } => y,
54 }
55 }
56
57 #[must_use]
59 pub fn is_finite(self) -> bool {
60 self.x().is_finite() && self.y().is_finite()
61 }
62
63 #[must_use]
66 pub const fn negate_y(self) -> Self {
67 match self {
68 Self::Pixels { x, y } => Self::Pixels { x, y: -y },
69 Self::WheelTicks { x, y } => Self::WheelTicks { x, y: -y },
70 }
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::ScrollDelta;
77
78 #[test]
79 fn units_remain_distinct() {
80 assert_ne!(
81 ScrollDelta::pixels(1.0, -2.0),
82 ScrollDelta::wheel_ticks(1.0, -2.0)
83 );
84 }
85
86 #[test]
87 fn rejects_non_finite_components() {
88 assert!(!ScrollDelta::pixels(f64::NAN, 0.0).is_finite());
89 assert!(!ScrollDelta::wheel_ticks(0.0, f64::INFINITY).is_finite());
90 assert!(ScrollDelta::wheel_ticks(0.25, -1.0).is_finite());
91 }
92
93 #[test]
94 fn negate_y_flips_only_the_vertical_component() {
95 assert_eq!(
96 ScrollDelta::wheel_ticks(3.0, 1.0).negate_y(),
97 ScrollDelta::wheel_ticks(3.0, -1.0)
98 );
99 assert_eq!(
100 ScrollDelta::pixels(-2.0, -5.0).negate_y(),
101 ScrollDelta::pixels(-2.0, 5.0)
102 );
103 }
104}