1#![deny(missing_docs)]
4
5extern crate vecmath;
6extern crate input;
7
8use std::time::Instant;
9
10use math::{Matrix2d, Rectangle};
11use input::GenericEvent;
12
13pub mod math;
14
15#[derive(Clone, Copy, PartialEq, Debug)]
17pub enum ButtonEvent {
18 MouseEnter,
20 MouseLeave,
22 Press,
24 Click,
26 Cancel,
28}
29
30pub struct ButtonController {
32 pub mouse_cursor_inside: bool,
34 pub was_inside: bool,
36 pub pressed: bool,
38 pub pressed_instant: Option<Instant>,
40 pub events: Vec<ButtonEvent>,
42}
43
44impl ButtonController {
45 pub fn new() -> ButtonController {
47 ButtonController {
48 mouse_cursor_inside: false,
49 was_inside: false,
50 pressed: false,
51 pressed_instant: None,
52 events: vec![],
53 }
54 }
55
56 pub fn event<E: GenericEvent>(&mut self, layout: Rectangle, transform: Matrix2d, e: &E) {
58 use math::is_inside;
59 use input::MouseButton;
60
61 self.events.clear();
62 if let Some(pos) = e.mouse_cursor_args() {
63 let inside = is_inside(pos, transform, layout);
64 if inside {
65 if !self.mouse_cursor_inside {
66 self.mouse_cursor_inside = true;
67 self.events.push(ButtonEvent::MouseEnter);
68 }
69 } else {
70 if self.mouse_cursor_inside {
71 self.mouse_cursor_inside = false;
72 self.events.push(ButtonEvent::MouseLeave);
73 }
74 }
75 }
76
77 if let Some(input::Button::Mouse(MouseButton::Left)) = e.press_args() {
78 if self.mouse_cursor_inside {
79 self.pressed = true;
80 self.was_inside = true;
81 self.events.push(ButtonEvent::Press);
82 self.pressed_instant = Some(Instant::now());
83 }
84 }
85 if let Some(input::Button::Mouse(MouseButton::Left)) = e.release_args() {
86 self.pressed = false;
87 if self.mouse_cursor_inside {
88 self.events.push(ButtonEvent::Click);
89 } else if self.was_inside {
90 self.events.push(ButtonEvent::Cancel);
91 }
92 self.was_inside = false;
93 }
94 }
95
96 pub fn touch_event<E: GenericEvent, S: Into<[u32; 2]>>(&mut self,
98 layout: Rectangle,
99 transform: Matrix2d,
100 window_size: S,
101 e: &E) {
102 use input::Touch;
103 use math::is_inside;
104
105 self.events.clear();
106 let window_size = window_size.into();
107 if let Some(args) = e.touch_args() {
108 let pos = args.position();
109 let pos = [pos[0] * window_size[0] as f64, pos[1] * window_size[1] as f64];
110 let inside = is_inside(pos, transform, layout);
111 match args.touch {
112 Touch::Start => {
113 if inside {
114 self.pressed = true;
115 self.was_inside = true;
116 self.mouse_cursor_inside = true;
117 self.events.push(ButtonEvent::Press);
118 self.pressed_instant = Some(Instant::now());
119 }
120 }
121 Touch::Move => {
122 if inside {
123 if !self.mouse_cursor_inside {
124 self.mouse_cursor_inside = true;
125 self.events.push(ButtonEvent::MouseEnter);
126 }
127 } else {
128 if self.mouse_cursor_inside {
129 self.mouse_cursor_inside = false;
130 self.events.push(ButtonEvent::MouseLeave);
131 }
132 }
133 }
134 Touch::End => {
135 self.pressed = false;
136 if self.mouse_cursor_inside {
137 self.events.push(ButtonEvent::Click);
138 } else if self.was_inside {
139 self.events.push(ButtonEvent::Cancel);
140 }
141 self.was_inside = false;
142 self.mouse_cursor_inside = false;
143 }
144 Touch::Cancel => {}
145 }
146 }
147 }
148
149 pub fn appear_pressed(&self, pressed_duration_secs: f64) -> bool {
151 if let Some(ref instant) = self.pressed_instant {
152 if math::duration_to_secs(&instant.elapsed()) < pressed_duration_secs {
153 true
154 } else {
155 self.pressed
156 }
157 } else {
158 self.pressed
159 }
160 }
161
162 pub fn state(&self, pressed_duration_secs: f64) -> ButtonState {
164 ButtonVisual {
165 appear_pressed: self.appear_pressed(pressed_duration_secs),
166 mouse_cursor_inside: self.mouse_cursor_inside,
167 }.state()
168 }
169}
170
171#[derive(Clone, Copy, PartialEq, Debug)]
173pub enum ButtonState {
174 Inactive,
176 Hover,
178 Press,
180 Cancel,
182}
183
184#[derive(Clone, Copy, PartialEq, Debug)]
186pub struct ButtonVisual {
187 pub appear_pressed: bool,
189 pub mouse_cursor_inside: bool,
191}
192
193impl ButtonVisual {
194 pub fn state(&self) -> ButtonState {
196 use ButtonState as S;
197
198 match (self.appear_pressed, self.mouse_cursor_inside) {
199 (true, true) => S::Press,
200 (true, false) => S::Cancel,
201 (false, false) => S::Inactive,
202 (false, true) => S::Hover,
203 }
204 }
205}