1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//! A Piston library for handling scrolling areas.

#![deny(missing_docs)]

extern crate input;

use input::GenericEvent;

/// Stores scroll settings.
#[derive(Clone, Debug)]
pub struct ScrollSettings {
    speed: f64,
}

impl ScrollSettings {
    /// Creates a new `ScrollSettings` object.
    pub fn new() -> ScrollSettings {
        ScrollSettings {
            speed: 1.0,
        }
    }

    /// Sets scroll speed.
    pub fn speed(mut self, val: f64) -> ScrollSettings {
        self.speed = val;
        self
    }
}

/// Stores information for scrolling.
#[derive(Clone, Debug)]
pub struct ScrollController {
    cursor: [f64; 2],
    /// The offset of visible area.
    pub offset: [f64; 2],
    /// Visible bounds.
    pub bounds: [f64; 4],
    /// The size of the scrollable area.
    pub area: [f64; 2],
    /// The scroll speed.
    pub speed: f64,
}

impl ScrollController {
    /// Creates a new `ScrollController`.
    pub fn new(bounds: [f64; 4], area: [f64; 2], settings: &ScrollSettings) -> ScrollController {
        ScrollController {
            cursor: [0.0; 2],
            offset: [0.0; 2],
            bounds: bounds,
            area: area,
            speed: settings.speed,
        }
    }

    /// Returns the visible rectangle that intersects with the area.
    /// This is used to find what part of the area the user is looking at.
    pub fn visible_area_rect(&self) -> [f64; 4] {
        [
            -self.offset[0],
            -self.offset[1],
            self.bounds[2].min(self.area[0]),
            self.bounds[3].min(self.area[1])
        ]
    }

    /// Transform a rectangle from area coordinates to view.
    pub fn rect_from_area_to_view(&self, rect: [f64; 4]) -> [f64; 4] {
        [
            self.bounds[0] - self.offset[0] - rect[0],
            self.bounds[1] - self.offset[1] - rect[1],
            rect[2],
            rect[3]
        ]
    }

    fn cursor_inside(&self) -> bool {
        let c = self.cursor;
        let b = self.bounds;
        c[0] >= b[0] && c[1] >= b[1] && c[0] < b[0] + b[2] && c[1] < b[1] + b[3]
    }

    fn clamp_offset_to_scroll_area(&self, offset: [f64; 2]) -> [f64; 2] {
        let b = self.bounds;
        let a = self.area;
        [
            if offset[0] > 0.0 || b[2] > a[0] {
                0.0
            } else if offset[0] < -(a[0] - b[2]) {
                -(a[0] - b[2])
            } else {
                offset[0]
            },
            if offset[1] > 0.0 || b[3] > a[1] {
                0.0
            } else if a[1] > b[3] && offset[1] < -(a[1] - b[3]) {
                -(a[1] - b[3])
            } else {
                offset[1]
            }
        ]
    }

    /// Handles event.
    pub fn event<E: GenericEvent>(&mut self, e: &E) {
        if let Some(pos) = e.mouse_cursor_args() {
            self.cursor = pos;
        }
        if let Some(pos) = e.mouse_scroll_args() {
            if self.cursor_inside() {
                let new_offset = [
                    self.offset[0] - pos[0] * self.speed,
                    self.offset[1] + pos[1] * self.speed
                ];
                self.offset = self.clamp_offset_to_scroll_area(new_offset);
            }
        }
    }
}