use crate::core::Point;
use crate::shortcut::Modifiers;
pub const WHEEL_DELTA_UNITS: i32 = 120;
pub const DEFAULT_LINES_PER_NOTCH: u32 = 3;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct WheelScroll {
pub lines_y: i32,
pub columns_x: i32,
pub modifiers: Modifiers,
}
impl WheelScroll {
pub fn from_event(delta: Point, modifiers: u32, lines_per_notch: u32) -> Self {
Self {
lines_y: lines_from_delta(delta.y, lines_per_notch),
columns_x: lines_from_delta(delta.x, lines_per_notch),
modifiers: Modifiers::from_event_bits(modifiers),
}
}
pub fn is_vertical(&self) -> bool {
self.lines_y != 0
}
}
fn lines_from_delta(value: i32, lines_per_notch: u32) -> i32 {
let value = value as i64;
let notches = if value.unsigned_abs() >= WHEEL_DELTA_UNITS as u64 {
value / WHEEL_DELTA_UNITS as i64
} else {
value
};
let lines = notches * lines_per_notch as i64;
lines.clamp(i32::MIN as i64, i32::MAX as i64) as i32
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_notch_moves_one_line_with_its_original_sign() {
let down = WheelScroll::from_event(Point::new(0, 1), 0, 1);
assert_eq!(down.lines_y, 1);
assert_eq!(down.columns_x, 0);
let up = WheelScroll::from_event(Point::new(0, -1), 0, 1);
assert_eq!(up.lines_y, -1);
}
#[test]
fn legacy_120_unit_ticks_become_one_notch() {
let down = WheelScroll::from_event(Point::new(0, WHEEL_DELTA_UNITS), 0, 1);
assert_eq!(down.lines_y, 1, "120 legacy units are one notch, one line");
let up = WheelScroll::from_event(Point::new(0, -WHEEL_DELTA_UNITS), 0, 1);
assert_eq!(up.lines_y, -1);
let two = WheelScroll::from_event(Point::new(0, 2 * WHEEL_DELTA_UNITS), 0, 1);
assert_eq!(two.lines_y, 2);
}
#[test]
fn deltas_below_120_are_already_notches() {
let event = WheelScroll::from_event(Point::new(3, -2), 0, 1);
assert_eq!(event.columns_x, 3);
assert_eq!(event.lines_y, -2);
}
#[test]
fn lines_per_notch_scales_both_axes() {
let event = WheelScroll::from_event(Point::new(-1, 2), 0, DEFAULT_LINES_PER_NOTCH);
assert_eq!(event.columns_x, -3);
assert_eq!(event.lines_y, 6);
}
#[test]
fn is_vertical_depends_only_on_the_vertical_component() {
assert!(WheelScroll::from_event(Point::new(0, 1), 0, 1).is_vertical());
assert!(!WheelScroll::from_event(Point::new(0, -5), 0, 0).is_vertical());
assert!(!WheelScroll::from_event(Point::new(4, 0), 0, 1).is_vertical());
assert!(!WheelScroll::from_event(Point::new(0, 0), 0, 1).is_vertical());
}
#[test]
fn modifiers_are_decoded_from_event_bits() {
let event = WheelScroll::from_event(Point::new(0, 1), 0b0111, 1);
assert!(event.modifiers.contains(Modifiers::SHIFT));
assert!(event.modifiers.contains(Modifiers::CTRL));
assert!(event.modifiers.contains(Modifiers::ALT));
assert!(!event.modifiers.contains(Modifiers::PRIMARY));
}
#[test]
fn the_command_bit_maps_to_primary() {
let event = WheelScroll::from_event(Point::new(0, 1), 0b1000, 1);
assert!(event.modifiers.contains(Modifiers::PRIMARY));
}
}