Skip to content

Commit 412cac6

Browse files
committed
Merge pull request #684 from psFried/events
Switch to event-based input handling
2 parents 1f9ec7c + 1b566d1 commit 412cac6

17 files changed

+2208
-115
lines changed

src/events/global_input.rs

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
//! Handles all of the global input events and state.
2+
//! The core of this module is the `GlobalInput` struct. It is responsible for aggregating
3+
//! and interpreting raw input events into high-level semantic events.
4+
5+
use events::{InputState, UiEvent, MouseClick, MouseDrag, Scroll, InputProvider};
6+
use input::MouseButton;
7+
use position::{Point, Scalar};
8+
use widget::Index;
9+
10+
/// Global input event handler that also implements `InputProvider`. The `Ui` passes all events
11+
/// to it's `GlobalInput` instance, which aggregates and interprets the events to provide
12+
/// so-called 'high-level' events to widgets. This input gets reset after every update by the `Ui`.
13+
pub struct GlobalInput {
14+
/// The `InputState` as it was at the end of the last update cycle.
15+
pub start_state: InputState,
16+
/// The most recent `InputState`, with updates from handling all the events
17+
/// this update cycle
18+
pub current_state: InputState,
19+
events: Vec<UiEvent>,
20+
drag_threshold: Scalar,
21+
}
22+
23+
/// Iterator over global `UiEvent`s. Unlike the `WidgetInputEventIterator`, this will
24+
/// never filter out any events, and all coordinates will be reative to the (0,0) origin
25+
/// of the window.
26+
pub type GlobalInputEventIterator<'a> = ::std::slice::Iter<'a, UiEvent>;
27+
28+
impl <'a> InputProvider<'a, GlobalInputEventIterator<'a>> for GlobalInput {
29+
fn all_events(&'a self) -> GlobalInputEventIterator {
30+
self.events.iter()
31+
}
32+
33+
fn current_state(&'a self) -> &'a InputState {
34+
&self.current_state
35+
}
36+
37+
fn mouse_button_down(&self, button: MouseButton) -> Option<Point> {
38+
self.current_state().mouse_buttons.get(button).map(|_| {
39+
self.mouse_position()
40+
})
41+
}
42+
}
43+
44+
impl GlobalInput {
45+
46+
/// Returns a fresh new `GlobalInput`
47+
pub fn new(drag_threshold: Scalar) -> GlobalInput {
48+
GlobalInput{
49+
events: Vec::new(),
50+
drag_threshold: drag_threshold,
51+
start_state: InputState::new(),
52+
current_state: InputState::new(),
53+
}
54+
}
55+
56+
/// Adds a new event and updates the internal state.
57+
pub fn push_event(&mut self, event: UiEvent) {
58+
use input::Input::{Release, Move};
59+
use input::Motion::MouseRelative;
60+
use input::Motion::MouseScroll;
61+
use input::Button::Mouse;
62+
63+
let maybe_new_event = match event {
64+
UiEvent::Raw(Release(Mouse(button))) => self.handle_mouse_release(button),
65+
UiEvent::Raw(Move(MouseRelative(x, y))) => self.handle_mouse_move([x, y]),
66+
UiEvent::Raw(Move(MouseScroll(x, y))) => self.mouse_scroll(x, y),
67+
_ => None
68+
};
69+
70+
self.current_state.update(&event);
71+
self.events.push(event);
72+
if let Some(new_event) = maybe_new_event {
73+
self.push_event(new_event);
74+
}
75+
}
76+
77+
/// Called at the end of every update cycle in order to prepare the `GlobalInput` to
78+
/// handle events for the next one.
79+
pub fn reset(&mut self) {
80+
self.events.clear();
81+
self.start_state = self.current_state.clone();
82+
}
83+
84+
/// Returns the most up to date position of the mouse
85+
pub fn mouse_position(&self) -> Point {
86+
self.current_state.mouse_position
87+
}
88+
89+
/// Returns the input state as it was after the last update
90+
pub fn starting_state(&self) -> &InputState {
91+
&self.start_state
92+
}
93+
94+
/// Returns the most up to date info on which widget is capturing the mouse
95+
pub fn currently_capturing_mouse(&self) -> Option<Index> {
96+
self.current_state.widget_capturing_mouse
97+
}
98+
99+
/// Returns the most up to date info on which widget is capturing the keyboard
100+
pub fn currently_capturing_keyboard(&self) -> Option<Index> {
101+
self.current_state.widget_capturing_keyboard
102+
}
103+
104+
105+
fn mouse_scroll(&self, x: f64, y: f64) -> Option<UiEvent> {
106+
Some(UiEvent::Scroll(Scroll{
107+
x: x,
108+
y: y,
109+
modifiers: self.current_state.modifiers
110+
}))
111+
}
112+
113+
fn handle_mouse_move(&self, move_to: Point) -> Option<UiEvent> {
114+
self.current_state.mouse_buttons.pressed_button().and_then(|btn_and_point| {
115+
if self.is_drag(btn_and_point.1, move_to) {
116+
Some(UiEvent::MouseDrag(MouseDrag{
117+
button: btn_and_point.0,
118+
start: btn_and_point.1,
119+
end: move_to,
120+
in_progress: true,
121+
modifier: self.current_state.modifiers
122+
}))
123+
} else {
124+
None
125+
}
126+
})
127+
}
128+
129+
fn handle_mouse_release(&self, button: MouseButton) -> Option<UiEvent> {
130+
self.current_state.mouse_buttons.get(button).map(|point| {
131+
if self.is_drag(point, self.current_state.mouse_position) {
132+
UiEvent::MouseDrag(MouseDrag{
133+
button: button,
134+
start: point,
135+
end: self.current_state.mouse_position,
136+
modifier: self.current_state.modifiers,
137+
in_progress: false
138+
})
139+
} else {
140+
UiEvent::MouseClick(MouseClick {
141+
button: button,
142+
location: point,
143+
modifier: self.current_state.modifiers
144+
})
145+
}
146+
})
147+
}
148+
149+
fn is_drag(&self, a: Point, b: Point) -> bool {
150+
distance_between(a, b) > self.drag_threshold
151+
}
152+
}
153+
154+
fn distance_between(a: Point, b: Point) -> Scalar {
155+
let dx_2 = (a[0] - b[0]).powi(2);
156+
let dy_2 = (a[1] - b[1]).powi(2);
157+
(dx_2 + dy_2).abs().sqrt()
158+
}

0 commit comments

Comments
 (0)