Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to event-based input handling #684

Merged
merged 39 commits into from
Feb 18, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
fef589d
started on a class to create high-level events from raw input
psFried Jan 18, 2016
0500d1e
basic clicks and drags working
psFried Jan 19, 2016
ca02069
added basic support for modifier keys
psFried Jan 19, 2016
b743fb1
finished support for modifier keys, added reset method to conrod even…
psFried Jan 20, 2016
767136d
added event filter to aggregate Input::Text events
psFried Jan 20, 2016
6f1dcfb
removed separate event handler trait in favor of a struct with all pr…
psFried Jan 20, 2016
d245910
extracted event traits in preparation for filtered and relative events
psFried Jan 21, 2016
ce294ed
organized event modules
psFried Jan 21, 2016
4e615fc
implemented new RelativePosition trait for ConrodEvent to make them r…
psFried Jan 21, 2016
80845fe
cleaned up unused imports
psFried Jan 21, 2016
b9e7e7a
added cepturing events to ConrodEvent
psFried Jan 22, 2016
dd88277
added capturing/uncapturing events, added tests for Ui event handling
psFried Jan 25, 2016
69103d2
removed EventAggregator trait and renamed EventProvider trait to Widg…
psFried Jan 25, 2016
3c5a49f
added methods to determine if an event is from the keyboard or mouse
psFried Jan 27, 2016
5920aeb
refactored GlobalInput to have a current and starting state
psFried Jan 27, 2016
8a31fe4
renamed mouse_button_map to input_state
psFried Jan 27, 2016
50f983a
added WidgetInput to filter events and provide them in relative coord…
psFried Jan 28, 2016
46053f6
made a separate Scroll event that contains modifier keys
psFried Jan 28, 2016
9de23ac
added tests for scroll event filtering, refactored widget_input and m…
psFried Jan 28, 2016
e960b5b
renamed WidgetEvents to EventProvider, moved it into its own module, …
psFried Jan 28, 2016
dce29fe
started adding doc comments
psFried Jan 29, 2016
849715e
finished adding doc comments to events module
Jan 29, 2016
6a2ff7e
renamed EventProvider trait to InputProvider
psFried Feb 3, 2016
ac7aaf7
added current_state method to InputProvider trait, cleaned up the res…
psFried Feb 3, 2016
1cfec73
added convenience methods to InputProvider for getting current input …
psFried Feb 3, 2016
16a152e
reset global input after setting widgets
psFried Feb 3, 2016
067dd60
refactored button widget to use new WidgetInput
psFried Feb 3, 2016
eb180e5
removed unused import in button.rs
psFried Feb 3, 2016
a74bd01
refactored DropDownList to use new InputProvider instead of UserInput
psFried Feb 4, 2016
f3eb207
moved tests into their own module
psFried Feb 5, 2016
7d11f14
removed impl IntoIter on GlobalInput
psFried Feb 5, 2016
856dde1
changed InputProvider::all_events method to return an Iterator of &Co…
psFried Feb 6, 2016
d4d917e
changed WidgetInput to just borrow from GlobalInput to avoid addition…
psFried Feb 7, 2016
c06b6d6
renamed ConrodEvent to UiEvent, added missing doc comments to event i…
psFried Feb 14, 2016
f3372e6
Removed the RelativePosition trait
psFried Feb 14, 2016
a40c48b
changed all of the InputProvider::*just_pressed/released methods to r…
psFried Feb 15, 2016
ec0e5b5
moved mouse drag distance threshold into Theme so that it can be spec…
psFried Feb 15, 2016
1374455
changed mouse_button_currently_pressed to return an Option<Point> ins…
psFried Feb 17, 2016
1b566d1
renamed InputProvider::mouse_button_currently_pressed methods to mous…
psFried Feb 17, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 158 additions & 0 deletions src/events/global_input.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
//! Handles all of the global input events and state.
//! The core of this module is the `GlobalInput` struct. It is responsible for aggregating
//! and interpreting raw input events into high-level semantic events.

use events::{InputState, UiEvent, MouseClick, MouseDrag, Scroll, InputProvider};
use input::MouseButton;
use position::{Point, Scalar};
use widget::Index;

/// Global input event handler that also implements `InputProvider`. The `Ui` passes all events
/// to it's `GlobalInput` instance, which aggregates and interprets the events to provide
/// so-called 'high-level' events to widgets. This input gets reset after every update by the `Ui`.
pub struct GlobalInput {
/// The `InputState` as it was at the end of the last update cycle.
pub start_state: InputState,
/// The most recent `InputState`, with updates from handling all the events
/// this update cycle
pub current_state: InputState,
events: Vec<UiEvent>,
drag_threshold: Scalar,
}

/// Iterator over global `UiEvent`s. Unlike the `WidgetInputEventIterator`, this will
/// never filter out any events, and all coordinates will be reative to the (0,0) origin
/// of the window.
pub type GlobalInputEventIterator<'a> = ::std::slice::Iter<'a, UiEvent>;

impl <'a> InputProvider<'a, GlobalInputEventIterator<'a>> for GlobalInput {
fn all_events(&'a self) -> GlobalInputEventIterator {
self.events.iter()
}

fn current_state(&'a self) -> &'a InputState {
&self.current_state
}

fn mouse_button_down(&self, button: MouseButton) -> Option<Point> {
self.current_state().mouse_buttons.get(button).map(|_| {
self.mouse_position()
})
}
}

impl GlobalInput {

/// Returns a fresh new `GlobalInput`
pub fn new(drag_threshold: Scalar) -> GlobalInput {
GlobalInput{
events: Vec::new(),
drag_threshold: drag_threshold,
start_state: InputState::new(),
current_state: InputState::new(),
}
}

/// Adds a new event and updates the internal state.
pub fn push_event(&mut self, event: UiEvent) {
use input::Input::{Release, Move};
use input::Motion::MouseRelative;
use input::Motion::MouseScroll;
use input::Button::Mouse;

let maybe_new_event = match event {
UiEvent::Raw(Release(Mouse(button))) => self.handle_mouse_release(button),
UiEvent::Raw(Move(MouseRelative(x, y))) => self.handle_mouse_move([x, y]),
UiEvent::Raw(Move(MouseScroll(x, y))) => self.mouse_scroll(x, y),
_ => None
};

self.current_state.update(&event);
self.events.push(event);
if let Some(new_event) = maybe_new_event {
self.push_event(new_event);
}
}

/// Called at the end of every update cycle in order to prepare the `GlobalInput` to
/// handle events for the next one.
pub fn reset(&mut self) {
self.events.clear();
self.start_state = self.current_state.clone();
}

/// Returns the most up to date position of the mouse
pub fn mouse_position(&self) -> Point {
self.current_state.mouse_position
}

/// Returns the input state as it was after the last update
pub fn starting_state(&self) -> &InputState {
&self.start_state
}

/// Returns the most up to date info on which widget is capturing the mouse
pub fn currently_capturing_mouse(&self) -> Option<Index> {
self.current_state.widget_capturing_mouse
}

/// Returns the most up to date info on which widget is capturing the keyboard
pub fn currently_capturing_keyboard(&self) -> Option<Index> {
self.current_state.widget_capturing_keyboard
}


fn mouse_scroll(&self, x: f64, y: f64) -> Option<UiEvent> {
Some(UiEvent::Scroll(Scroll{
x: x,
y: y,
modifiers: self.current_state.modifiers
}))
}

fn handle_mouse_move(&self, move_to: Point) -> Option<UiEvent> {
self.current_state.mouse_buttons.pressed_button().and_then(|btn_and_point| {
if self.is_drag(btn_and_point.1, move_to) {
Some(UiEvent::MouseDrag(MouseDrag{
button: btn_and_point.0,
start: btn_and_point.1,
end: move_to,
in_progress: true,
modifier: self.current_state.modifiers
}))
} else {
None
}
})
}

fn handle_mouse_release(&self, button: MouseButton) -> Option<UiEvent> {
self.current_state.mouse_buttons.get(button).map(|point| {
if self.is_drag(point, self.current_state.mouse_position) {
UiEvent::MouseDrag(MouseDrag{
button: button,
start: point,
end: self.current_state.mouse_position,
modifier: self.current_state.modifiers,
in_progress: false
})
} else {
UiEvent::MouseClick(MouseClick {
button: button,
location: point,
modifier: self.current_state.modifiers
})
}
})
}

fn is_drag(&self, a: Point, b: Point) -> bool {
distance_between(a, b) > self.drag_threshold
}
}

fn distance_between(a: Point, b: Point) -> Scalar {
let dx_2 = (a[0] - b[0]).powi(2);
let dy_2 = (a[1] - b[1]).powi(2);
(dx_2 + dy_2).abs().sqrt()
}
Loading