pub const DEFAULT_ROTARY_SCROLL_FACTOR_DP: f32 = 64.0;
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct RotaryScrollEvent {
pub vertical_scroll_pixels: f32,
pub horizontal_scroll_pixels: f32,
pub uptime_millis: u64,
}
impl RotaryScrollEvent {
pub const fn new(
vertical_scroll_pixels: f32,
horizontal_scroll_pixels: f32,
uptime_millis: u64,
) -> Self {
Self {
vertical_scroll_pixels,
horizontal_scroll_pixels,
uptime_millis,
}
}
pub fn from_detents(
detents: f32,
vertical_scroll_factor: f32,
horizontal_scroll_factor: f32,
uptime_millis: u64,
) -> Self {
let axis_value = -detents;
Self {
vertical_scroll_pixels: axis_value * vertical_scroll_factor,
horizontal_scroll_pixels: axis_value * horizontal_scroll_factor,
uptime_millis,
}
}
pub fn is_empty(&self) -> bool {
let vertical_dead =
!self.vertical_scroll_pixels.is_finite() || self.vertical_scroll_pixels == 0.0;
let horizontal_dead =
!self.horizontal_scroll_pixels.is_finite() || self.horizontal_scroll_pixels == 0.0;
vertical_dead && horizontal_dead
}
}
pub fn rotary_scroll_pixels_from_detents(detents: f32, scroll_factor: f32) -> f32 {
-detents * scroll_factor
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_stores_pixel_amounts_verbatim() {
let event = RotaryScrollEvent::new(-12.0, 3.5, 4_200);
assert_eq!(event.vertical_scroll_pixels, -12.0);
assert_eq!(event.horizontal_scroll_pixels, 3.5);
assert_eq!(event.uptime_millis, 4_200);
}
#[test]
fn positive_detents_become_negative_pixels() {
assert_eq!(rotary_scroll_pixels_from_detents(1.0, 64.0), -64.0);
assert_eq!(rotary_scroll_pixels_from_detents(-1.0, 64.0), 64.0);
assert_eq!(rotary_scroll_pixels_from_detents(0.0, 64.0), 0.0);
}
#[test]
fn from_detents_matches_compose_and_feeds_both_axes() {
let event = RotaryScrollEvent::from_detents(2.0, 64.0, 48.0, 9);
assert_eq!(event.vertical_scroll_pixels, -128.0);
assert_eq!(event.horizontal_scroll_pixels, -96.0);
assert_eq!(event.uptime_millis, 9);
}
#[test]
fn from_detents_is_sign_symmetric() {
let up = RotaryScrollEvent::from_detents(1.5, 64.0, 64.0, 0);
let down = RotaryScrollEvent::from_detents(-1.5, 64.0, 64.0, 0);
assert_eq!(up.vertical_scroll_pixels, -down.vertical_scroll_pixels);
assert!(up.vertical_scroll_pixels < 0.0);
assert!(down.vertical_scroll_pixels > 0.0);
}
#[test]
fn is_empty_rejects_zero_and_non_finite_amounts() {
assert!(RotaryScrollEvent::default().is_empty());
assert!(RotaryScrollEvent::new(0.0, 0.0, 1).is_empty());
assert!(RotaryScrollEvent::new(f32::NAN, f32::INFINITY, 1).is_empty());
assert!(!RotaryScrollEvent::new(-1.0, 0.0, 1).is_empty());
assert!(!RotaryScrollEvent::new(0.0, 2.0, 1).is_empty());
}
#[test]
fn event_is_copy_and_allocation_free() {
fn assert_copy<T: Copy>() {}
assert_copy::<RotaryScrollEvent>();
assert_eq!(
std::mem::size_of::<RotaryScrollEvent>(),
std::mem::size_of::<f32>() * 2 + std::mem::size_of::<u64>()
);
}
}