Skip to content

Mouse input refactoring #663

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

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
a29255a
Ui now changes which widgets are capturing the keyboard and mouse whe…
psFried Dec 27, 2015
543b1f8
started refactoring handling of mouse input for text box
psFried Dec 29, 2015
e287fbf
rebased upstream master
psFried Dec 29, 2015
d51684c
started on simplified mouse api
psFried Dec 30, 2015
5f8c4cd
added time and position to mouse::ButtonPosition::Down
psFried Dec 31, 2015
1c36c9c
almost done with minimal mouse simple api, one drag test still failing
psFried Dec 31, 2015
0ad6ca0
changed mouse::ButtonPosition::Down to take a MouseButtonDown struct …
psFried Dec 31, 2015
c4bb4b8
mouse tests passing for click and drag events
psFried Dec 31, 2015
4190201
moved mouse module into a separate directory and separated new simple…
psFried Dec 31, 2015
3b6d9b2
added SimpleMouseEvent for scrolling
psFried Dec 31, 2015
4da7672
change ui to use new mouse methods, added reset method to mouse
psFried Jan 1, 2016
3e342d2
more changes to TextBox handling input
psFried Jan 3, 2016
152c142
added doc comments to MouseButtonMap
psFried Jan 3, 2016
ab04e2f
changed all widgets and examples to use mouse ButtonMap instead of mo…
psFried Jan 3, 2016
a2b22bc
added event iterator to Mouse
psFried Jan 5, 2016
920ebc5
renamed SimpleMouseEvent to MouseEvent and simple_events to events
psFried Jan 6, 2016
f8fa6d5
fixed highlight issue when text box takes focus
psFried Jan 6, 2016
7f3f7f3
added a mouse button down event
psFried Jan 6, 2016
e8ecf34
changed mouse down event to use a new struct instead of repurposing m…
psFried Jan 8, 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
11 changes: 7 additions & 4 deletions examples/custom_widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,19 +120,22 @@ mod circular_button {
/// over the button and the previous interaction state.
fn get_new_interaction(is_over: bool, prev: Interaction, mouse: Mouse) -> Interaction {
use conrod::MouseButtonPosition::{Down, Up};
use conrod::MouseButton;
use self::Interaction::{Normal, Highlighted, Clicked};
match (is_over, prev, mouse.left.position) {

let lmb_position = mouse.buttons.get(MouseButton::Left).position;
match (is_over, prev, lmb_position) {
// LMB is down over the button. But the button wasn't Highlighted last
// update. This means the user clicked somewhere outside the button and
// moved over the button holding LMB down. We do nothing in this case.
(true, Normal, Down) => Normal,
(true, Normal, Down(_)) => Normal,

// LMB is down over the button. The button was either Highlighted or Clicked
// last update. If it was highlighted before, that means the user clicked
// just now, and we transition to the Clicked state. If it was clicked
// before, that means the user is still holding LMB down from a previous
// click, in which case the state remains Clicked.
(true, _, Down) => Clicked,
(true, _, Down(_)) => Clicked,

// LMB is up. The mouse is hovering over the button. Regardless of what the
// state was last update, the state should definitely be Highlighted now.
Expand All @@ -141,7 +144,7 @@ mod circular_button {
// LMB is down, the mouse is not over the button, but the previous state was
// Clicked. That means the user clicked the button and then moved the mouse
// outside the button while holding LMB down. The button stays Clicked.
(false, Clicked, Down) => Clicked,
(false, Clicked, Down(_)) => Clicked,

// If none of the above applies, then nothing interesting is happening with
// this button.
Expand Down
11 changes: 7 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,13 @@ pub use frame::{Framing, Frameable};
pub use glyph_cache::{GlyphCache, LineBreak};
pub use graph::NodeIndex;
pub use label::{FontSize, Labelable};
pub use mouse::Mouse;

pub use mouse::{Mouse, MouseButton, NUM_MOUSE_BUTTONS};
pub use mouse::ButtonMap as MouseButtonMap;
pub use mouse::ButtonState as MouseButtonState;
pub use mouse::ButtonPosition as MouseButtonPosition;
pub use mouse::Scroll as MouseScroll;
pub use mouse::events::Scroll as MouseScroll;
pub use mouse::events as simple_mouse_events;
pub use position::{Align, Corner, Depth, Direction, Dimension, Dimensions, Edge, Margin, Padding,
Place, Point, Position, Positionable, Range, Rect, Scalar, Sizeable};
pub use position::Matrix as PositionMatrix;
Expand Down Expand Up @@ -108,10 +111,10 @@ mod widget;
/// This is the recommended way of generating `WidgetId`s as it greatly lessens the chances of
/// making errors when adding or removing widget ids.
///
/// Each Widget must have its own unique identifier so that the `Ui` can keep track of its state
/// Each Widget must have its own unique identifier so that the `Ui` can keep track of its state
/// between updates.
///
/// To make this easier, we provide the `widget_ids` macro, which generates a unique `WidgetId` for
/// To make this easier, we provide the `widget_ids` macro, which generates a unique `WidgetId` for
/// each identifier given in the list.
///
/// The `with n` syntax reserves `n` number of `WidgetId`s for that identifier rather than just one.
Expand Down
96 changes: 0 additions & 96 deletions src/mouse.rs

This file was deleted.

158 changes: 158 additions & 0 deletions src/mouse/button_map.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
use super::MouseButton;
use super::ButtonState;
use std::slice::{IterMut, Iter};
use position::Point;

/// The max total number of buttons on a mouse.
pub const NUM_MOUSE_BUTTONS: usize = 9;

/// A map of `conrod::MouseButton` to `conrod::MouseButtonState`.
/// Used by the `conrod::Mouse` to hold the state of all mouse buttons.
#[derive(Copy, Clone, Debug)]
pub struct ButtonMap {
button_states: [ButtonState; NUM_MOUSE_BUTTONS]
}

impl ButtonMap {
/// Returns a new button map with all states set to defaults.
pub fn new() -> ButtonMap {
ButtonMap{
button_states: [ButtonState::new(); NUM_MOUSE_BUTTONS]
}
}

/// Returns an immutable reference to the button State
/// for the given button.
///
/// # Example
/// ```
/// use conrod::{MouseButton, MouseButtonMap};
///
/// let map = MouseButtonMap::new();
/// let left_button_state = map.get(MouseButton::Left);
/// assert!(left_button_state.is_up());
/// ```
pub fn get(&self, button: MouseButton) -> &ButtonState {
&self.button_states[Self::button_idx(button)]
}

/// Returns a mutable reference to the button state for the given button.
///
/// # Example
/// ```
/// use conrod::{MouseButton, MouseButtonMap};
///
/// let mut map = MouseButtonMap::new();
/// {
/// let right_button_state = map.get_mut(MouseButton::Right);
/// right_button_state.was_just_pressed = true;
/// }
/// assert!(map.get(MouseButton::Right).was_just_pressed);
/// ```
pub fn get_mut(&mut self, button: MouseButton) -> &mut ButtonState {
&mut self.button_states[Self::button_idx(button)]
}

/// Simple way to update the ButtonState for a given MouseButton using a closure.
///
/// # Example
/// ```
/// use conrod::{MouseButton, MouseButtonState, MouseButtonMap};
///
/// let mut map = MouseButtonMap::new();
/// map.update(MouseButton::Right, |state| state.was_just_released = true);
/// assert!(map.get(MouseButton::Right).was_just_released);
/// ```
pub fn update<F: FnOnce(&mut ButtonState)>(&mut self, button: MouseButton, update_fn: F) {
let state = self.get_mut(button);
update_fn(state);
}

/// Returns a `Vec` containing all the button states in the map, combined
/// with their respective buttons.
///
/// # Example
/// ```
/// use conrod::{MouseButton, NUM_MOUSE_BUTTONS, MouseButtonState, MouseButtonMap};
///
/// let mut map = MouseButtonMap::new();
/// let button_states: Vec<(MouseButton, &MouseButtonState)> = map.all_buttons();
/// assert_eq!(NUM_MOUSE_BUTTONS, button_states.len());
/// ```
pub fn all_buttons(&self) -> Vec<(MouseButton, &ButtonState)> {
self.button_states.into_iter().enumerate()
.map(|idx_and_state| {
(MouseButton::from(idx_and_state.0 as u32), idx_and_state.1)
}).collect::<Vec<(MouseButton, &ButtonState)>>()
}

/// returns an iterator over mutable references to all the button states
pub fn iter_mut(&mut self) -> IterMut<ButtonState> {
self.button_states.iter_mut()
}

/// returns an iterator over immutable references to all the button states
pub fn iter(&self) -> Iter<ButtonState> {
self.button_states.iter()
}

/// Returns a new `ButtonMap` with all points relative to the
/// given `Point`.
pub fn relative_to(&self, xy: Point) -> ButtonMap {
let mut new_map = self.clone();
for mut state in new_map.button_states.iter_mut() {
state.event = state.event.map(|evt| evt.relative_to(xy));
}
new_map
}

fn button_idx(button: MouseButton) -> usize {
u32::from(button) as usize
}
}

#[test]
fn relative_to_changes_all_mouse_events_to_relative_positions() {
use input::MouseButton::*;
use super::events::*;
use super::events::MouseEvent::{Click, Drag};
use time::SteadyTime;

let mut map_a = ButtonMap::new();
map_a.update(Left, |state| state.event = Some(Click(MouseClick{
mouse_button: Left,
position: [10.0, 50.5]
})));
map_a.update(Button6, |state| state.event = Some(Drag(MouseDragEvent{
mouse_button: Button6,
start: MouseButtonDown{
time: SteadyTime::now(),
position: [20.2, 30.0]
},
current: MouseButtonDown{
time: SteadyTime::now(),
position: [40.4, 60.0]
},
button_released: true
})));

let map_b = map_a.relative_to([10.0, 10.0]);

let left_state = map_b.get(Left);
if let Some(Click(click)) = left_state.event {
assert_float_eq(0.0, click.position[0]);
assert_float_eq(40.5, click.position[1]);
} else {
panic!("Expected click event, got: {:?}", left_state.event);
}

let state_6 = map_b.get(Button6);
if let Some(Drag(drag)) = state_6.event {
assert_float_eq(10.2, drag.start.position[0]);
assert_float_eq(20.0, drag.start.position[1]);
assert_float_eq(30.4, drag.current.position[0]);
assert_float_eq(50.0, drag.current.position[1]);
} else {
panic!("Expected a drag event, but got: {:?}", state_6.event);
}
}
Loading