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;