scroll_controller/
lib.rs

1//! A Piston library for handling scrolling areas.
2
3#![deny(missing_docs)]
4
5extern crate input;
6
7use input::GenericEvent;
8
9/// Stores scroll settings.
10#[derive(Clone, Debug)]
11pub struct ScrollSettings {
12    speed: f64,
13}
14
15impl ScrollSettings {
16    /// Creates a new `ScrollSettings` object.
17    pub fn new() -> ScrollSettings {
18        ScrollSettings {
19            speed: 1.0,
20        }
21    }
22
23    /// Sets scroll speed.
24    pub fn speed(mut self, val: f64) -> ScrollSettings {
25        self.speed = val;
26        self
27    }
28}
29
30/// Stores information for scrolling.
31#[derive(Clone, Debug)]
32pub struct ScrollController {
33    cursor: [f64; 2],
34    /// The offset of visible area.
35    pub offset: [f64; 2],
36    /// Visible bounds.
37    pub bounds: [f64; 4],
38    /// The size of the scrollable area.
39    pub area: [f64; 2],
40    /// The scroll speed.
41    pub speed: f64,
42}
43
44impl ScrollController {
45    /// Creates a new `ScrollController`.
46    pub fn new(bounds: [f64; 4], area: [f64; 2], settings: &ScrollSettings) -> ScrollController {
47        ScrollController {
48            cursor: [0.0; 2],
49            offset: [0.0; 2],
50            bounds: bounds,
51            area: area,
52            speed: settings.speed,
53        }
54    }
55
56    /// Returns the visible rectangle that intersects with the area.
57    /// This is used to find what part of the area the user is looking at.
58    pub fn visible_area_rect(&self) -> [f64; 4] {
59        [
60            -self.offset[0],
61            -self.offset[1],
62            self.bounds[2].min(self.area[0]),
63            self.bounds[3].min(self.area[1])
64        ]
65    }
66
67    /// Transform a rectangle from area coordinates to view.
68    pub fn rect_from_area_to_view(&self, rect: [f64; 4]) -> [f64; 4] {
69        [
70            self.bounds[0] - self.offset[0] - rect[0],
71            self.bounds[1] - self.offset[1] - rect[1],
72            rect[2],
73            rect[3]
74        ]
75    }
76
77    fn cursor_inside(&self) -> bool {
78        let c = self.cursor;
79        let b = self.bounds;
80        c[0] >= b[0] && c[1] >= b[1] && c[0] < b[0] + b[2] && c[1] < b[1] + b[3]
81    }
82
83    fn clamp_offset_to_scroll_area(&self, offset: [f64; 2]) -> [f64; 2] {
84        let b = self.bounds;
85        let a = self.area;
86        [
87            if offset[0] > 0.0 || b[2] > a[0] {
88                0.0
89            } else if offset[0] < -(a[0] - b[2]) {
90                -(a[0] - b[2])
91            } else {
92                offset[0]
93            },
94            if offset[1] > 0.0 || b[3] > a[1] {
95                0.0
96            } else if a[1] > b[3] && offset[1] < -(a[1] - b[3]) {
97                -(a[1] - b[3])
98            } else {
99                offset[1]
100            }
101        ]
102    }
103
104    /// Handles event.
105    pub fn event<E: GenericEvent>(&mut self, e: &E) {
106        if let Some(pos) = e.mouse_cursor_args() {
107            self.cursor = pos;
108        }
109        if let Some(pos) = e.mouse_scroll_args() {
110            if self.cursor_inside() {
111                let new_offset = [
112                    self.offset[0] - pos[0] * self.speed,
113                    self.offset[1] + pos[1] * self.speed
114                ];
115                self.offset = self.clamp_offset_to_scroll_area(new_offset);
116            }
117        }
118    }
119}