Skip to main content

cranpose_app_shell/
wheel.rs

1use cranpose_foundation::Modifiers;
2use cranpose_ui_graphics::Point;
3
4const NOTCH_LOGICAL_PX: f32 = 40.0;
5const ZOOM_PER_NOTCH: f32 = 1.2;
6
7/// A mouse-wheel or trackpad scroll sample ready for
8/// [`AppShell::wheel_scrolled`](crate::AppShell::wheel_scrolled).
9#[derive(Clone, Copy, Debug, PartialEq)]
10pub struct WheelScroll {
11    /// Scroll amount in logical pixels; positive moves content down and right
12    /// (see the module docs — this is winit's sign, not the DOM's).
13    pub delta: Point,
14    /// Keyboard modifiers held during the sample. `ctrl` makes it a zoom
15    /// gesture, `alt` turns a vertical wheel into a horizontal scroll.
16    pub modifiers: Modifiers,
17    /// Monotonic milliseconds, for the rotary event's velocity tracking. Only
18    /// differences between samples are meaningful.
19    pub uptime_millis: u64,
20}
21
22impl WheelScroll {
23    /// A sample with no modifiers held.
24    pub fn new(delta: Point, uptime_millis: u64) -> Self {
25        Self {
26            delta,
27            modifiers: Modifiers::NONE,
28            uptime_millis,
29        }
30    }
31
32    /// This sample with `modifiers` held.
33    pub fn with_modifiers(mut self, modifiers: Modifiers) -> Self {
34        self.modifiers = modifiers;
35        self
36    }
37
38    /// Whether this sample is the zoom gesture (ctrl+wheel, which is also how
39    /// trackpad pinches arrive in a browser) rather than a scroll.
40    pub fn is_zoom(&self) -> bool {
41        self.modifiers.ctrl
42    }
43
44    /// The multiplicative zoom step for a ctrl+wheel sample: one notch up
45    /// (positive delta) zooms in by `ZOOM_PER_NOTCH`.
46    pub fn zoom_factor(&self) -> f32 {
47        ZOOM_PER_NOTCH.powf(self.delta.y / NOTCH_LOGICAL_PX)
48    }
49
50    /// The delta the hovered scrollable should see: with alt held, a vertical
51    /// wheel drives the horizontal axis instead (the shift-less way to scroll
52    /// a row on a wheel that only has a vertical axis).
53    pub fn scroll_delta(&self) -> Point {
54        if !self.modifiers.alt {
55            return self.delta;
56        }
57        let x = if self.delta.x.abs() <= f32::EPSILON {
58            self.delta.y
59        } else {
60            self.delta.x
61        };
62        Point { x, y: 0.0 }
63    }
64}
65
66#[cfg(test)]
67#[path = "tests/wheel_tests.rs"]
68mod tests;