From a29255a8b28f614f4eb6865a8509f12ae1fc9088 Mon Sep 17 00:00:00 2001 From: pfried Date: Sun, 27 Dec 2015 12:57:49 -0500 Subject: [PATCH 01/19] Ui now changes which widgets are capturing the keyboard and mouse when mouse is clicked, started refactoring TextBox to use new Ui methods --- src/ui.rs | 30 ++++++++++++++++++++++++++---- src/widget/mod.rs | 8 ++++++++ src/widget/text_box.rs | 34 ++++++++++++++++++++++++---------- 3 files changed, 58 insertions(+), 14 deletions(-) diff --git a/src/ui.rs b/src/ui.rs index c6cd14de3..cab137f5c 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -168,6 +168,7 @@ impl Ui { } } +<<<<<<< 935ee46b45fd33a68ae43cb87b1b83da7e6e51e4 /// The **Rect** for the widget at the given index. /// /// Returns `None` if there is no widget for the given index. @@ -183,6 +184,14 @@ impl Ui { self.rect_of(idx).map(|rect| rect.w()) } + pub fn is_capturing_keyboard(&self, id: widget::Index) -> bool { + self.maybe_captured_keyboard == Some(Capturing::Captured(id)) + } + + pub fn is_capturing_mouse(&self, id: widget::Index) -> bool { + self.maybe_captured_mouse == Some(Capturing::Captured(id)) + } + /// The absolute height of the widget at the given index. /// /// Returns `None` if there is no widget for the given index. @@ -248,6 +257,7 @@ impl Ui { match button_type { Button::Mouse(button) => { + self.focus_widget_under_mouse(); let mouse_button = match button { Left => &mut self.mouse.left, Right => &mut self.mouse.right, @@ -267,6 +277,7 @@ impl Ui { use input::MouseButton::{Left, Middle, Right}; match button_type { Button::Mouse(button) => { + self.focus_widget_under_mouse(); let mouse_button = match button { Left => &mut self.mouse.left, Right => &mut self.mouse.right, @@ -286,6 +297,18 @@ impl Ui { }); } + fn focus_widget_under_mouse(&mut self) { + if let Some(widget_index) = self.maybe_widget_under_mouse { + self.change_focus_to(widget_index); + } + } + + pub fn change_focus_to(&mut self, widget_index: widget::Index) { + self.maybe_captured_mouse = Some(Capturing::Captured(widget_index)); + self.maybe_captured_keyboard = Some(Capturing::Captured(widget_index)); + } + + /// Get the centred xy coords for some given `Dimension`s, `Position` and alignment. /// @@ -624,7 +647,7 @@ pub fn infer_parent_from_position(ui: &Ui, x_pos: Position, y_pos: Positio /// Attempts to infer the parent of a widget from its *x*/*y* `Position`s and the current state of /// the `Ui`. -/// +/// /// If no parent can be inferred via the `Position`s, the `maybe_current_parent_idx` will be used. /// /// If `maybe_current_parent_idx` is `None`, the `Ui`'s `window` widget will be used. @@ -688,7 +711,7 @@ pub fn get_mouse_state(ui: &Ui, idx: widget::Index) -> Option { Some(Capturing::Captured(captured_idx)) => if idx == captured_idx { Some(ui.mouse) } else { None }, _ => - if Some(idx) == ui.maybe_widget_under_mouse + if Some(idx) == ui.maybe_widget_under_mouse || Some(idx) == ui.maybe_top_scrollable_widget_under_mouse { Some(ui.mouse) } else { @@ -702,7 +725,7 @@ pub fn get_mouse_state(ui: &Ui, idx: widget::Index) -> Option { /// Indicate that the widget with the given widget::Index has captured the mouse. /// /// Returns true if the mouse was successfully captured. -/// +/// /// Returns false if the mouse was already captured. pub fn mouse_captured_by(ui: &mut Ui, idx: widget::Index) -> bool { // If the mouse isn't already captured, set idx as the capturing widget. @@ -819,4 +842,3 @@ pub fn post_update_cache(ui: &mut Ui, widget: widget::PostUpdateCache(ui: &mut Ui, color: Color) { ui.maybe_background_color = Some(color); } - diff --git a/src/widget/mod.rs b/src/widget/mod.rs index 4a13ca653..73d56e44b 100644 --- a/src/widget/mod.rs +++ b/src/widget/mod.rs @@ -1056,6 +1056,14 @@ impl<'a, C> UiCell<'a, C> { ui::keyboard_uncaptured_by(self.ui, self.idx) } + pub fn is_capturing_keyboard(&self) -> bool { + self.ui.is_capturing_keyboard(self.idx) + } + + pub fn is_capturing_mouse(&self) -> bool { + self.ui.is_capturing_mouse(self.idx) + } + /// Generate a new, unique NodeIndex into a Placeholder node within the `Ui`'s widget graph. /// This should only be called once for each unique widget needed to avoid unnecessary bloat /// within the `Ui`'s widget graph. diff --git a/src/widget/text_box.rs b/src/widget/text_box.rs index 2cac7d6d4..251296c95 100644 --- a/src/widget/text_box.rs +++ b/src/widget/text_box.rs @@ -22,7 +22,7 @@ use { }; use input::keyboard::Key::{Backspace, Left, Right, Return, A, E, LCtrl, RCtrl}; use vecmath::vec2_sub; -use widget::{self, KidArea}; +use widget::{self, Widget, KidArea, UpdateArgs, UiCell}; pub type Idx = usize; @@ -338,6 +338,27 @@ impl<'a, F> TextBox<'a, F> { self } + +} + +fn get_new_interaction_2(args: &UpdateArgs, C>) -> Interaction + where C: CharacterCache, + F: FnMut(&mut String) { + let capturing = args.ui.is_capturing_mouse() || args.ui.is_capturing_keyboard(); + if capturing { + let state: &widget::State = args.state; + let v: &State = state.view(); + let prev_interaction: Interaction = v.interaction; + let new_view = match prev_interaction { + Interaction::Captured(view) => view, + _ => View{ cursor: Cursor::from_range(0, 999), offset: 0f64 } + }; + Interaction::Captured(new_view) + } else { + args.ui.input().maybe_mouse.map(|mouse| { + Interaction::Uncaptured(Uncaptured::Highlighted) + }).unwrap_or(Interaction::Uncaptured(Uncaptured::Normal)) + } } impl<'a, F> Widget for TextBox<'a, F> where F: FnMut(&mut String) { @@ -381,8 +402,10 @@ impl<'a, F> Widget for TextBox<'a, F> where F: FnMut(&mut String) { } } + /// Update the state of the TextBox. fn update(mut self, args: widget::UpdateArgs) { + let mut new_interaction = get_new_interaction_2(&args); let widget::UpdateArgs { idx, state, rect, style, mut ui, .. } = args; let (xy, dim) = rect.xy_dim(); @@ -395,14 +418,6 @@ impl<'a, F> Widget for TextBox<'a, F> where F: FnMut(&mut String) { let text_x = text_w / 2.0 - pad_dim[0] / 2.0 + TEXT_PADDING; let text_start_x = text_x - text_w / 2.0; let mut new_control_pressed = state.view().control_pressed; - let mut new_interaction = match (self.enabled, maybe_mouse) { - (false, _) | (true, None) => Interaction::Uncaptured(Uncaptured::Normal), - (true, Some(mouse)) => { - let over_elem = over_elem(ui.glyph_cache(), mouse.xy, dim, pad_dim, text_start_x, - text_w, font_size, &self.text); - get_new_interaction(over_elem, state.view().interaction, mouse) - }, - }; // Check cursor validity (and update new_interaction if necessary). if let Interaction::Captured(view) = new_interaction { @@ -674,4 +689,3 @@ impl<'a, F> Frameable for TextBox<'a, F> { self } } - From 543b1f8f34444a515e6330c8dbb4396139852dae Mon Sep 17 00:00:00 2001 From: pfried Date: Tue, 29 Dec 2015 10:30:11 -0500 Subject: [PATCH 02/19] started refactoring handling of mouse input for text box --- src/ui.rs | 40 +++++++++-------------------- src/widget/text_box.rs | 58 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 69 insertions(+), 29 deletions(-) diff --git a/src/ui.rs b/src/ui.rs index cab137f5c..d7017be17 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -257,7 +257,7 @@ impl Ui { match button_type { Button::Mouse(button) => { - self.focus_widget_under_mouse(); + self.widget_under_mouse_captures_keyboard(); let mouse_button = match button { Left => &mut self.mouse.left, Right => &mut self.mouse.right, @@ -277,7 +277,7 @@ impl Ui { use input::MouseButton::{Left, Middle, Right}; match button_type { Button::Mouse(button) => { - self.focus_widget_under_mouse(); + self.widget_under_mouse_captures_keyboard(); let mouse_button = match button { Left => &mut self.mouse.left, Right => &mut self.mouse.right, @@ -297,19 +297,12 @@ impl Ui { }); } - fn focus_widget_under_mouse(&mut self) { - if let Some(widget_index) = self.maybe_widget_under_mouse { - self.change_focus_to(widget_index); - } - } - - pub fn change_focus_to(&mut self, widget_index: widget::Index) { - self.maybe_captured_mouse = Some(Capturing::Captured(widget_index)); - self.maybe_captured_keyboard = Some(Capturing::Captured(widget_index)); + fn widget_under_mouse_captures_keyboard(&mut self) { + self.maybe_widget_under_mouse.map(|widget_index| { + self.maybe_captured_keyboard = Some(Capturing::Captured(widget_index)); + }); } - - /// Get the centred xy coords for some given `Dimension`s, `Position` and alignment. /// /// If getting the xy for a specific widget, its `widget::Index` should be specified so that we @@ -703,21 +696,12 @@ pub fn user_input<'a, C>(ui: &'a Ui, idx: widget::Index) -> UserInput<'a> { /// If the Ui has been captured and the given id doesn't match the captured id, return None. pub fn get_mouse_state(ui: &Ui, idx: widget::Index) -> Option { match ui.maybe_captured_mouse { - Some(Capturing::Captured(captured_idx)) => - if idx == captured_idx { Some(ui.mouse) } else { None }, - Some(Capturing::JustReleased) => - None, - None => match ui.maybe_captured_keyboard { - Some(Capturing::Captured(captured_idx)) => - if idx == captured_idx { Some(ui.mouse) } else { None }, - _ => - if Some(idx) == ui.maybe_widget_under_mouse - || Some(idx) == ui.maybe_top_scrollable_widget_under_mouse { - Some(ui.mouse) - } else { - None - }, - }, + Some(Capturing::Captured(captured_idx)) if idx == captured_idx => + Some(ui.mouse), + None if ui.maybe_widget_under_mouse == Some(idx) => + Some(ui.mouse), + _ => + None } } diff --git a/src/widget/text_box.rs b/src/widget/text_box.rs index 251296c95..1f5edd741 100644 --- a/src/widget/text_box.rs +++ b/src/widget/text_box.rs @@ -23,6 +23,7 @@ use { use input::keyboard::Key::{Backspace, Left, Right, Return, A, E, LCtrl, RCtrl}; use vecmath::vec2_sub; use widget::{self, Widget, KidArea, UpdateArgs, UiCell}; +use widget::{self, Widget, UpdateArgs}; pub type Idx = usize; @@ -339,8 +340,55 @@ impl<'a, F> TextBox<'a, F> { } + +} + +fn get_clicked_elem(text: &str, args: &UpdateArgs, C>) -> Elem + where C: CharacterCache, + F: FnMut(&mut String) { + let maybe_elem_under_mouse: Option = args.ui.input().maybe_mouse.iter() + .filter(|mouse| mouse.left.was_just_pressed) + .map(|mouse| { + let style = args.style; + let theme = args.ui.theme(); + let font_size = style.font_size(theme); + let (xy, widget_dimension) = args.rect.xy_dim(); + let inner_dimension = get_inner_dimensions(widget_dimension, style, theme); + let relative_mouse = mouse.relative_to(xy); + let glyph_cache = args.ui.glyph_cache(); + let text_w = glyph_cache.width(font_size, text); + let text_x = position::align_left_of(inner_dimension[0], text_w) + TEXT_PADDING; + let text_start_x = text_x - text_w / 2.0; + over_elem(args.ui.glyph_cache(), + relative_mouse.xy, + widget_dimension, + inner_dimension, + text_start_x, + text_w, + font_size, + text) + }).next(); + maybe_elem_under_mouse.unwrap_or(Elem::Nill) } +fn get_new_interaction_2(text: &str, args: &UpdateArgs, C>) -> Interaction + where C: CharacterCache, + F: FnMut(&mut String) { + let prev_state: &State = args.state.view(); + let clicked_elem = get_clicked_elem(text, args); + match clicked_elem { + Elem::Char(char_index) => + Interaction::Captured(View{cursor: Cursor::from_index(char_index), offset: 0f64 }), + Elem::Rect => + Interaction::Captured(View{cursor: Cursor::from_range(0, text.chars().count()), offset: 0f64}), + Elem::Nill if args.ui.is_capturing_keyboard() => { + if let Interaction::Captured(view) = prev_state.interaction { + Interaction::Captured(view) + } else { + Interaction::Captured(View{cursor: Cursor::from_range(0, text.chars().count()), offset: 0f64}) + } + }, + _ => args.ui.input().maybe_mouse.map(|_mouse| { fn get_new_interaction_2(args: &UpdateArgs, C>) -> Interaction where C: CharacterCache, F: FnMut(&mut String) { @@ -361,6 +409,12 @@ fn get_new_interaction_2(args: &UpdateArgs, C>) -> Interaction } } +fn get_inner_dimensions(outer_rect: Dimensions, style: &Style, theme: &Theme) -> Dimensions { + let frame_width = style.frame(theme); + vec2_sub(outer_rect, [frame_width * 2.0; 2]) +} + + impl<'a, F> Widget for TextBox<'a, F> where F: FnMut(&mut String) { type State = State; type Style = Style; @@ -405,11 +459,13 @@ impl<'a, F> Widget for TextBox<'a, F> where F: FnMut(&mut String) { /// Update the state of the TextBox. fn update(mut self, args: widget::UpdateArgs) { + let mut new_interaction = get_new_interaction_2(&self.text, &args); + let widget::UpdateArgs { state, rect, style, mut ui, .. } = args; let mut new_interaction = get_new_interaction_2(&args); let widget::UpdateArgs { idx, state, rect, style, mut ui, .. } = args; let (xy, dim) = rect.xy_dim(); - let maybe_mouse = ui.input().maybe_mouse.map(|mouse| mouse.relative_to(xy)); + // let maybe_mouse = ui.input().maybe_mouse.map(|mouse| mouse.relative_to(xy)); let frame = style.frame(ui.theme()); let inner_rect = rect.pad(frame); let font_size = style.font_size(ui.theme()); From e287fbf3a5c87a46900b73cb4fa7b64393da2567 Mon Sep 17 00:00:00 2001 From: pfried Date: Tue, 29 Dec 2015 14:13:24 -0500 Subject: [PATCH 03/19] rebased upstream master --- src/ui.rs | 1 - src/widget/text_box.rs | 25 +++---------------------- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/src/ui.rs b/src/ui.rs index d7017be17..2f9bd7830 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -168,7 +168,6 @@ impl Ui { } } -<<<<<<< 935ee46b45fd33a68ae43cb87b1b83da7e6e51e4 /// The **Rect** for the widget at the given index. /// /// Returns `None` if there is no widget for the given index. diff --git a/src/widget/text_box.rs b/src/widget/text_box.rs index 1f5edd741..f05fe746a 100644 --- a/src/widget/text_box.rs +++ b/src/widget/text_box.rs @@ -18,12 +18,11 @@ use { Scalar, Text, Theme, - Widget, }; + use input::keyboard::Key::{Backspace, Left, Right, Return, A, E, LCtrl, RCtrl}; use vecmath::vec2_sub; -use widget::{self, Widget, KidArea, UpdateArgs, UiCell}; -use widget::{self, Widget, UpdateArgs}; +use widget::{self, Widget, KidArea, UpdateArgs}; pub type Idx = usize; @@ -357,7 +356,7 @@ fn get_clicked_elem(text: &str, args: &UpdateArgs, C>) -> Elem let relative_mouse = mouse.relative_to(xy); let glyph_cache = args.ui.glyph_cache(); let text_w = glyph_cache.width(font_size, text); - let text_x = position::align_left_of(inner_dimension[0], text_w) + TEXT_PADDING; + let text_x = text_w / 2.0 - inner_dimension[0] / 2.0 + TEXT_PADDING; let text_start_x = text_x - text_w / 2.0; over_elem(args.ui.glyph_cache(), relative_mouse.xy, @@ -389,21 +388,6 @@ fn get_new_interaction_2(text: &str, args: &UpdateArgs, C>) -> } }, _ => args.ui.input().maybe_mouse.map(|_mouse| { -fn get_new_interaction_2(args: &UpdateArgs, C>) -> Interaction - where C: CharacterCache, - F: FnMut(&mut String) { - let capturing = args.ui.is_capturing_mouse() || args.ui.is_capturing_keyboard(); - if capturing { - let state: &widget::State = args.state; - let v: &State = state.view(); - let prev_interaction: Interaction = v.interaction; - let new_view = match prev_interaction { - Interaction::Captured(view) => view, - _ => View{ cursor: Cursor::from_range(0, 999), offset: 0f64 } - }; - Interaction::Captured(new_view) - } else { - args.ui.input().maybe_mouse.map(|mouse| { Interaction::Uncaptured(Uncaptured::Highlighted) }).unwrap_or(Interaction::Uncaptured(Uncaptured::Normal)) } @@ -460,12 +444,9 @@ impl<'a, F> Widget for TextBox<'a, F> where F: FnMut(&mut String) { /// Update the state of the TextBox. fn update(mut self, args: widget::UpdateArgs) { let mut new_interaction = get_new_interaction_2(&self.text, &args); - let widget::UpdateArgs { state, rect, style, mut ui, .. } = args; - let mut new_interaction = get_new_interaction_2(&args); let widget::UpdateArgs { idx, state, rect, style, mut ui, .. } = args; let (xy, dim) = rect.xy_dim(); - // let maybe_mouse = ui.input().maybe_mouse.map(|mouse| mouse.relative_to(xy)); let frame = style.frame(ui.theme()); let inner_rect = rect.pad(frame); let font_size = style.font_size(ui.theme()); From d51684c1cd6c7bb02afcd22852878c853baa1e4d Mon Sep 17 00:00:00 2001 From: pfried Date: Wed, 30 Dec 2015 18:52:07 -0500 Subject: [PATCH 04/19] started on simplified mouse api --- src/mouse.rs | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 180 insertions(+), 1 deletion(-) diff --git a/src/mouse.rs b/src/mouse.rs index f28d69fdc..79dabb957 100644 --- a/src/mouse.rs +++ b/src/mouse.rs @@ -1,10 +1,14 @@ -//! +//! //! A module for describing Mouse state. //! //! The `Ui` will continuously maintain the latest Mouse state, necessary for widget logic. //! +pub use input::MouseButton; +pub use graphics::math::Scalar; use position::Point; +use time::{SteadyTime, Duration}; +use std::collections::HashMap; /// The current state of a Mouse button. #[derive(Copy, Clone, Debug)] @@ -41,6 +45,37 @@ pub struct Mouse { pub unknown: ButtonState, /// Amount that the mouse has scrolled since the last render. pub scroll: Scroll, + /// Movements less than this threshold will not be considered drags + pub drag_distance_threshold: Scalar, + last_button_down_events: [Option; 9], +} + + +/// Used for simplified mouse event handling. Most widgets can probably +/// just use these events +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum SimpleMouseEvent { + Click(MouseClick), + Drag(MouseDragEvent), +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct MouseClick { + mouse_button: MouseButton, + position: Point +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct MouseDragEvent { + mouse_button: MouseButton, + button_down_position: Point, + current_position: Point +} + +#[derive(Copy, Clone, Debug, PartialEq)] +struct MouseButtonDown { + time: SteadyTime, + position: Point } /// The amount of scrolling that has occurred since the last render event. @@ -84,6 +119,8 @@ impl Mouse { right: ButtonState::new(), unknown: ButtonState::new(), scroll: Scroll { x: 0.0, y: 0.0 }, + drag_distance_threshold: 2.0, + last_button_down_events: [None; 9], } } @@ -92,5 +129,147 @@ impl Mouse { Mouse { xy: ::vecmath::vec2_sub(self.xy, xy), ..self } } + /// sets the new position of the mouse + pub fn move_to(&mut self, xy: Point) { + self.xy = xy; + } + + /// Sends the mouse a button down event + pub fn button_down(&mut self, button: MouseButton) { + use input::MouseButton::*; + + let button_down = MouseButtonDown { + time: SteadyTime::now(), + position: self.xy.clone() + }; + self.set_last_button_down_time(button, Some(button_down)); + + let button_state = match button { + Left => &mut self.left, + Right => &mut self.right, + Middle => &mut self.middle, + _ => &mut self.unknown + }; + button_state.position = ButtonPosition::Down; + button_state.was_just_pressed = true; + } + + pub fn button_up(&mut self, button: MouseButton) { + use input::MouseButton::*; + + self.set_last_button_down_time(button, None); + let button_state = match button { + Left => &mut self.left, + Right => &mut self.right, + Middle => &mut self.middle, + _ => &mut self.unknown + }; + button_state.position = ButtonPosition::Up; + button_state.was_just_released = true; + } + + pub fn was_clicked(&self, button: MouseButton) -> bool { + use input::MouseButton::*; + let button_state = match button { + Left => self.left, + Right => self.right, + Middle => self.middle, + _ => self.unknown + }; + button_state.was_just_released + } + + pub fn get_simple_event(&self) -> Option { + use input::MouseButton::{Left, Right, Middle}; + None + } + + fn get_last_button_down(&self, button: MouseButton) -> Option { + let index: u32 = button.into(); + self.last_button_down_events[index as usize] + } + + fn set_last_button_down_time(&mut self, button: MouseButton, maybe_last_down: Option) { + let index: u32 = button.into(); + self.last_button_down_events[index as usize] = maybe_last_down; + } + +} + +#[test] +fn move_to_sets_new_mouse_position() { + let mut mouse = Mouse::new(); + + let new_position = [2.0, 5.0]; + mouse.move_to(new_position); + assert_eq!(new_position, mouse.xy); +} + +#[test] +fn button_down_sets_last_button_down_info_and_button_up_removes_it() { + use input::MouseButton::Left; + + let mut mouse = Mouse::new(); + assert!(mouse.get_last_button_down(Left).is_none()); + + mouse.button_down(Left); + assert!(mouse.get_last_button_down(Left).is_some()); + + mouse.button_up(Left); + assert!(mouse.get_last_button_down(Left).is_none()); +} + +#[test] +fn button_down_sets_button_state_to_down() { + let mut mouse = Mouse::new(); + mouse.left.position = ButtonPosition::Up; + + mouse.button_down(MouseButton::Left); + + assert_eq!(ButtonPosition::Down, mouse.left.position); +} + +#[test] +fn button_up_sets_button_state_to_up() { + let mut mouse = Mouse::new(); + mouse.left.position = ButtonPosition::Down; + + mouse.button_up(MouseButton::Left); + assert_eq!(ButtonPosition::Up, mouse.left.position); } +#[test] +fn get_simple_event_returns_click_if_button_goes_down_then_up_in_same_position() { + use input::MouseButton::Right; + let mut mouse = Mouse::new(); + mouse.button_down(Right); + mouse.button_up(Right); + let expected_event = SimpleMouseEvent::Click(MouseClick{ + mouse_button: Right, + position: mouse.xy.clone() + }); + + let actual_event = mouse.get_simple_event(); + assert!(actual_event.is_some()); + assert_eq!(expected_event, actual_event.unwrap()); +} + +#[test] +fn get_simple_event_returns_drag_event_if_mouse_was_dragged() { + use input::MouseButton::Left; + let mut mouse = Mouse::new(); + let new_position = [0.0, mouse.drag_distance_threshold + 1.0]; + mouse.button_down(Left); + mouse.move_to(new_position); + mouse.button_up(Left); + + let expected_event = SimpleMouseEvent::Drag(MouseDragEvent{ + mouse_button: Left, + button_down_position: [0.0, 0.0], + current_position: new_position, + }); + + let actual_event = mouse.get_simple_event(); + assert!(actual_event.is_some()); + assert_eq!(expected_event, actual_event.unwrap()); +} From 5f8c4cd1f8df7f11fae415b4dd4186b8106d5bcd Mon Sep 17 00:00:00 2001 From: pfried Date: Wed, 30 Dec 2015 19:15:41 -0500 Subject: [PATCH 05/19] added time and position to mouse::ButtonPosition::Down --- examples/custom_widget.rs | 6 +++--- src/mouse.rs | 28 +++++++++++++++++++++++----- src/ui.rs | 4 +++- src/widget/button.rs | 6 +++--- src/widget/drag.rs | 11 ++++------- src/widget/envelope_editor.rs | 12 ++++++------ src/widget/mod.rs | 6 +++--- src/widget/number_dialer.rs | 9 ++++----- src/widget/scroll.rs | 15 +++++++-------- src/widget/slider.rs | 6 +++--- src/widget/text_box.rs | 4 ++-- src/widget/title_bar.rs | 7 +++---- src/widget/toggle.rs | 7 +++---- src/widget/xy_pad.rs | 7 +++---- 14 files changed, 70 insertions(+), 58 deletions(-) diff --git a/examples/custom_widget.rs b/examples/custom_widget.rs index 1d7fb9de0..3418339db 100644 --- a/examples/custom_widget.rs +++ b/examples/custom_widget.rs @@ -125,14 +125,14 @@ mod circular_button { // 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. @@ -141,7 +141,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. diff --git a/src/mouse.rs b/src/mouse.rs index 79dabb957..724331d97 100644 --- a/src/mouse.rs +++ b/src/mouse.rs @@ -22,12 +22,12 @@ pub struct ButtonState { } /// Represents the current state of a mouse button. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum ButtonPosition { /// The mouse button is currently up. Up, /// The mouse button is currently down (pressed). - Down, + Down(SteadyTime, Point), } /// Represents the current state of the Mouse. @@ -105,6 +105,19 @@ impl ButtonState { self.was_just_pressed = false; } + pub fn is_down(&self) -> bool { + match self.position { + ButtonPosition::Up => false, + ButtonPosition::Down(_, _) => true + } + } + + pub fn is_up(&self) -> bool { + match self.position { + ButtonPosition::Up => true, + ButtonPosition::Down(_, _) => false + } + } } @@ -143,6 +156,7 @@ impl Mouse { position: self.xy.clone() }; self.set_last_button_down_time(button, Some(button_down)); + let mouse_position = self.xy.clone(); let button_state = match button { Left => &mut self.left, @@ -150,7 +164,7 @@ impl Mouse { Middle => &mut self.middle, _ => &mut self.unknown }; - button_state.position = ButtonPosition::Down; + button_state.position = ButtonPosition::Down(SteadyTime::now(), mouse_position); button_state.was_just_pressed = true; } @@ -226,13 +240,17 @@ fn button_down_sets_button_state_to_down() { mouse.button_down(MouseButton::Left); - assert_eq!(ButtonPosition::Down, mouse.left.position); + let is_down = match mouse.left.position { + ButtonPosition::Down(_, _) => true, + _ => false + }; + assert!(is_down); } #[test] fn button_up_sets_button_state_to_up() { let mut mouse = Mouse::new(); - mouse.left.position = ButtonPosition::Down; + mouse.left.position = ButtonPosition::Down(SteadyTime::now(), [0.0, 0.0]); mouse.button_up(MouseButton::Left); assert_eq!(ButtonPosition::Up, mouse.left.position); diff --git a/src/ui.rs b/src/ui.rs index 2f9bd7830..7d022efb0 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -20,6 +20,7 @@ use std::collections::HashSet; use std::io::Write; use theme::Theme; use widget::{self, Widget}; +use time::SteadyTime; /// Indicates whether or not the Mouse has been captured by a widget. @@ -254,6 +255,7 @@ impl Ui { use input::Button; use input::MouseButton::{Left, Middle, Right}; + let mouse_position = self.mouse.xy.clone(); match button_type { Button::Mouse(button) => { self.widget_under_mouse_captures_keyboard(); @@ -263,7 +265,7 @@ impl Ui { Middle => &mut self.mouse.middle, _ => &mut self.mouse.unknown, }; - mouse_button.position = mouse::ButtonPosition::Down; + mouse_button.position = mouse::ButtonPosition::Down(SteadyTime::now(), mouse_position); mouse_button.was_just_pressed = true; }, Button::Keyboard(key) => self.keys_just_pressed.push(key), diff --git a/src/widget/button.rs b/src/widget/button.rs index e5e8a32ec..e156fd214 100644 --- a/src/widget/button.rs +++ b/src/widget/button.rs @@ -79,10 +79,10 @@ fn get_new_interaction(is_over: bool, prev: Interaction, mouse: Mouse) -> Intera use mouse::ButtonPosition::{Down, Up}; use self::Interaction::{Normal, Highlighted, Clicked}; match (is_over, prev, mouse.left.position) { - (true, Normal, Down) => Normal, - (true, _, Down) => Clicked, + (true, Normal, Down(_, _)) => Normal, + (true, _, Down(_, _)) => Clicked, (true, _, Up) => Highlighted, - (false, Clicked, Down) => Clicked, + (false, Clicked, Down(_, _)) => Clicked, _ => Normal, } } diff --git a/src/widget/drag.rs b/src/widget/drag.rs index e8eefb68e..f97cfabb5 100644 --- a/src/widget/drag.rs +++ b/src/widget/drag.rs @@ -1,4 +1,4 @@ -//! +//! //! Types and functionality related to the dragging behaviour of Widgets. //! @@ -32,10 +32,10 @@ pub fn drag_widget(xy: Point, rel_rect: Rect, state: State, mouse: Mouse) -> (Po // Determine the new drag state. let new_state = match (is_over, state, mouse.left.position) { - (true, Normal, Down) => Normal, + (true, Normal, Down(_, _)) => Normal, (true, _, Up) => Highlighted, - (true, _, Down) | - (false, Clicked(_), Down) => Clicked(mouse.xy), + (true, _, Down(_, _)) | + (false, Clicked(_), Down(_, _)) => Clicked(mouse.xy), _ => Normal, }; @@ -48,6 +48,3 @@ pub fn drag_widget(xy: Point, rel_rect: Rect, state: State, mouse: Mouse) -> (Po (new_xy, new_state) } - - - diff --git a/src/widget/envelope_editor.rs b/src/widget/envelope_editor.rs index 054f364ae..6a87b9ffd 100644 --- a/src/widget/envelope_editor.rs +++ b/src/widget/envelope_editor.rs @@ -178,11 +178,11 @@ fn get_new_interaction(is_over_elem: Option, use self::MouseButton::{Left, Right}; use self::Interaction::{Normal, Highlighted, Clicked}; match (is_over_elem, prev, mouse.left.position, mouse.right.position) { - (Some(_), Normal, Down, Up) => Normal, + (Some(_), Normal, Down(_, _), Up) => Normal, (Some(elem), _, Up, Up) => Highlighted(elem), - (Some(elem), Highlighted(_), Down, Up) => Clicked(elem, Left), - (Some(_), Clicked(p_elem, m_button), Down, Up) | - (Some(_), Clicked(p_elem, m_button), Up, Down) => { + (Some(elem), Highlighted(_), Down(_, _), Up) => Clicked(elem, Left), + (Some(_), Clicked(p_elem, m_button), Down(_, _), Up) | + (Some(_), Clicked(p_elem, m_button), Up, Down(_, _)) => { match p_elem { EnvPoint(idx, _) => Clicked(EnvPoint(idx, (mouse.xy[0], mouse.xy[1])), m_button), // CurvePoint(idx, _) => @@ -190,7 +190,7 @@ fn get_new_interaction(is_over_elem: Option, _ => Clicked(p_elem, m_button), } }, - (None, Clicked(p_elem, m_button), Down, Up) => { + (None, Clicked(p_elem, m_button), Down(_, _), Up) => { match (p_elem, m_button) { (EnvPoint(idx, _), Left) => Clicked(EnvPoint(idx, (mouse.xy[0], mouse.xy[1])), Left), @@ -199,7 +199,7 @@ fn get_new_interaction(is_over_elem: Option, _ => Clicked(p_elem, Left), } }, - (Some(_), Highlighted(p_elem), Up, Down) => { + (Some(_), Highlighted(p_elem), Up, Down(_, _)) => { match p_elem { EnvPoint(idx, _) => Clicked(EnvPoint(idx, (mouse.xy[0], mouse.xy[1])), Right), // CurvePoint(idx, _) => Clicked(CurvePoint(idx, (mouse.xy[0], mouse.xy[1])), Right), diff --git a/src/widget/mod.rs b/src/widget/mod.rs index 73d56e44b..735eaa3b3 100644 --- a/src/widget/mod.rs +++ b/src/widget/mod.rs @@ -695,7 +695,7 @@ pub trait Widget: Sized { /// For all following occasions, the pre-existing cached state will be compared and updated. /// /// Note that this is a very imperative, mutation oriented segment of code. We try to move as much -/// imperativeness and mutation out of the users hands and into this function as possible, so that +/// imperativeness and mutation out of the users hands and into this function as possible, so that /// users have a clear, consise, purely functional `Widget` API. As a result, we try to keep this /// as verbosely annotated as possible. If anything is unclear, feel free to post an issue or PR /// with concerns/improvements to the github repo. @@ -834,7 +834,7 @@ fn set_widget<'a, C, W>(widget: W, idx: Index, ui: &mut Ui) where let maybe_mouse = ui::get_mouse_state(ui, idx); match (prev.maybe_floating, maybe_mouse) { (Some(prev_floating), Some(mouse)) => { - if mouse.left.position == ::mouse::ButtonPosition::Down { + if mouse.left.is_down() { Some(new_floating()) } else { Some(prev_floating) @@ -1266,7 +1266,7 @@ impl Sizeable for W where W: Widget { // } // }; // } -// +// // style_retrieval! { // fn_name: color, // member: maybe_color, diff --git a/src/widget/number_dialer.rs b/src/widget/number_dialer.rs index bd4d9192c..b20ed5670 100644 --- a/src/widget/number_dialer.rs +++ b/src/widget/number_dialer.rs @@ -189,16 +189,16 @@ fn get_new_interaction(is_over_elem: Option, prev: Interaction, mouse: Mou use self::Elem::ValueGlyph; use self::Interaction::{Normal, Highlighted, Clicked}; match (is_over_elem, prev, mouse.left.position) { - (Some(_), Normal, Down) => Normal, + (Some(_), Normal, Down(_, _)) => Normal, (Some(elem), _, Up) => Highlighted(elem), - (Some(elem), Highlighted(_), Down) => Clicked(elem), - (Some(_), Clicked(p_elem), Down) => { + (Some(elem), Highlighted(_), Down(_, _)) => Clicked(elem), + (Some(_), Clicked(p_elem), Down(_, _)) => { match p_elem { ValueGlyph(idx, _) => Clicked(ValueGlyph(idx, mouse.xy[1])), _ => Clicked(p_elem), } }, - (None, Clicked(p_elem), Down) => { + (None, Clicked(p_elem), Down(_, _)) => { match p_elem { ValueGlyph(idx, _) => Clicked(ValueGlyph(idx, mouse.xy[1])), _ => Clicked(p_elem), @@ -569,4 +569,3 @@ impl<'a, T, F> Labelable<'a> for NumberDialer<'a, T, F> self } } - diff --git a/src/widget/scroll.rs b/src/widget/scroll.rs index a39c8a2c8..3926f604b 100644 --- a/src/widget/scroll.rs +++ b/src/widget/scroll.rs @@ -1,4 +1,4 @@ -//! +//! //! Types and functionality related to the scrolling behaviour of widgets. //! @@ -76,7 +76,7 @@ pub struct Bar { } -/// The current interaction with the +/// The current interaction with the #[derive(Copy, Clone, Debug, PartialEq)] pub enum Interaction { /// No interaction with the Scrollbar. @@ -155,7 +155,7 @@ impl State { /// Construct a new State. /// The `visible` rect corresponds to a Widget's `kid_area` aka the viewable container. /// The `kids` rect is the area *actually occupied* by the children widgets. - pub fn new(scrolling: Scrolling, + pub fn new(scrolling: Scrolling, visible: Rect, kids: Rect, theme: &Theme, @@ -476,11 +476,11 @@ fn new_interaction(bar: &Bar, use self::Elem::Handle; use mouse::ButtonPosition::{Down, Up}; match (is_over_elem, bar.interaction, mouse.left.position) { - (Some(_), Normal, Down) => Normal, + (Some(_), Normal, Down(_, _)) => Normal, (Some(elem), _, Up) => Highlighted(elem), - (Some(_), Highlighted(_), Down) | - (_, Clicked(Handle(_)), Down) => Clicked(Handle(mouse_scalar)), - (_, Clicked(elem), Down) => Clicked(elem), + (Some(_), Highlighted(_), Down(_, _)) | + (_, Clicked(Handle(_)), Down(_, _)) => Clicked(Handle(mouse_scalar)), + (_, Clicked(elem), Down(_, _)) => Clicked(elem), _ => Normal, } } @@ -551,4 +551,3 @@ fn test_bar_new_no_scroll_rev_range() { let maybe_bar = Bar::new_if_scrollable(visible, kids, None); assert_eq!(maybe_bar, None); } - diff --git a/src/widget/slider.rs b/src/widget/slider.rs index f576657d5..4b1f02df6 100644 --- a/src/widget/slider.rs +++ b/src/widget/slider.rs @@ -95,10 +95,10 @@ fn get_new_interaction(is_over: bool, prev: Interaction, mouse: Mouse) -> Intera use mouse::ButtonPosition::{Down, Up}; use self::Interaction::{Normal, Highlighted, Clicked}; match (is_over, prev, mouse.left.position) { - (true, Normal, Down) => Normal, - (true, _, Down) => Clicked, + (true, Normal, Down(_, _)) => Normal, + (true, _, Down(_, _)) => Clicked, (true, _, Up) => Highlighted, - (false, Clicked, Down) => Clicked, + (false, Clicked, Down(_, _)) => Clicked, _ => Normal, } } diff --git a/src/widget/text_box.rs b/src/widget/text_box.rs index f05fe746a..60828b1ca 100644 --- a/src/widget/text_box.rs +++ b/src/widget/text_box.rs @@ -250,7 +250,7 @@ fn get_new_interaction(over_elem: Elem, prev_interaction: Interaction, mouse: Mo match prev_interaction { Interaction::Captured(mut prev) => match mouse.left.position { - Down => match over_elem { + Down(_, _) => match over_elem { Elem::Nill => if prev.cursor.anchor == Anchor::None { Uncaptured(Normal) } else { @@ -278,7 +278,7 @@ fn get_new_interaction(over_elem: Elem, prev_interaction: Interaction, mouse: Mo }, Interaction::Uncaptured(prev) => match mouse.left.position { - Down => match over_elem { + Down(_, _) => match over_elem { Elem::Nill => Uncaptured(Normal), Elem::Rect => match prev { Normal => prev_interaction, diff --git a/src/widget/title_bar.rs b/src/widget/title_bar.rs index a957d266d..73677d57b 100644 --- a/src/widget/title_bar.rs +++ b/src/widget/title_bar.rs @@ -84,10 +84,10 @@ fn get_new_interaction(is_over: bool, prev: Interaction, mouse: Mouse) -> Intera use mouse::ButtonPosition::{Down, Up}; use self::Interaction::{Normal, Highlighted, Clicked}; match (is_over, prev, mouse.left.position) { - (true, Normal, Down) => Normal, - (true, _, Down) => Clicked, + (true, Normal, Down(_, _)) => Normal, + (true, _, Down(_, _)) => Clicked, (true, _, Up) => Highlighted, - (false, Clicked, Down) => Clicked, + (false, Clicked, Down(_, _)) => Clicked, _ => Normal, } } @@ -249,4 +249,3 @@ impl<'a, F> Labelable<'a> for TitleBar<'a, F> { self } } - diff --git a/src/widget/toggle.rs b/src/widget/toggle.rs index c096d86bc..39a0f493e 100644 --- a/src/widget/toggle.rs +++ b/src/widget/toggle.rs @@ -89,10 +89,10 @@ fn get_new_interaction(is_over: bool, use mouse::ButtonPosition::{Down, Up}; use self::Interaction::{Normal, Highlighted, Clicked}; match (is_over, prev, mouse.left.position) { - (true, Normal, Down) => Normal, - (true, _, Down) => Clicked, + (true, Normal, Down(_, _)) => Normal, + (true, _, Down(_, _)) => Clicked, (true, _, Up) => Highlighted, - (false, Clicked, Down) => Clicked, + (false, Clicked, Down(_, _)) => Clicked, _ => Normal, } } @@ -321,4 +321,3 @@ impl<'a, F> Labelable<'a> for Toggle<'a, F> { self } } - diff --git a/src/widget/xy_pad.rs b/src/widget/xy_pad.rs index 6b49fccdf..16d7b4512 100644 --- a/src/widget/xy_pad.rs +++ b/src/widget/xy_pad.rs @@ -98,10 +98,10 @@ fn get_new_interaction(is_over: bool, use mouse::ButtonPosition::{Down, Up}; use self::Interaction::{Normal, Highlighted, Clicked}; match (is_over, prev, mouse.left.position) { - (true, Normal, Down) => Normal, - (true, _, Down) => Clicked, + (true, Normal, Down(_, _)) => Normal, + (true, _, Down(_, _)) => Clicked, (true, _, Up) => Highlighted, - (false, Clicked, Down) => Clicked, + (false, Clicked, Down(_, _)) => Clicked, _ => Normal, } } @@ -458,4 +458,3 @@ impl<'a, X, Y, F> Labelable<'a> for XYPad<'a, X, Y, F> self } } - From 1c36c9c13800f9db81226942a1afd2715ed3974e Mon Sep 17 00:00:00 2001 From: pfried Date: Wed, 30 Dec 2015 21:43:52 -0500 Subject: [PATCH 06/19] almost done with minimal mouse simple api, one drag test still failing --- src/mouse.rs | 234 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 180 insertions(+), 54 deletions(-) diff --git a/src/mouse.rs b/src/mouse.rs index 724331d97..1b17b8ebc 100644 --- a/src/mouse.rs +++ b/src/mouse.rs @@ -7,8 +7,7 @@ pub use input::MouseButton; pub use graphics::math::Scalar; use position::Point; -use time::{SteadyTime, Duration}; -use std::collections::HashMap; +use time::{SteadyTime}; /// The current state of a Mouse button. #[derive(Copy, Clone, Debug)] @@ -47,7 +46,8 @@ pub struct Mouse { pub scroll: Scroll, /// Movements less than this threshold will not be considered drags pub drag_distance_threshold: Scalar, - last_button_down_events: [Option; 9], + /// Simple mouse event that is waiting to be consumed + pub simple_event: Option, } @@ -59,17 +59,52 @@ pub enum SimpleMouseEvent { Drag(MouseDragEvent), } +impl SimpleMouseEvent { + + pub fn relative_to(&self, xy: Point) -> Self { + use self::SimpleMouseEvent::*; + + match self { + &Click(mouse_click) => Click(mouse_click.relative_to(xy)), + &Drag(mouse_drag) => Drag(mouse_drag.relative_to(xy)) + } + } +} + #[derive(Copy, Clone, Debug, PartialEq)] pub struct MouseClick { mouse_button: MouseButton, position: Point } +impl MouseClick { + + pub fn relative_to(&self, xy: Point) -> MouseClick { + use ::vecmath::vec2_sub; + + MouseClick{ + position: vec2_sub(self.position, xy), + ..*self + } + } +} + #[derive(Copy, Clone, Debug, PartialEq)] pub struct MouseDragEvent { mouse_button: MouseButton, - button_down_position: Point, - current_position: Point + start: MouseButtonDown, + current: MouseButtonDown, +} + +impl MouseDragEvent { + + pub fn relative_to(&self, xy: Point) -> MouseDragEvent { + MouseDragEvent{ + start: self.start.relative_to(xy), + current: self.current.relative_to(xy), + ..*self + } + } } #[derive(Copy, Clone, Debug, PartialEq)] @@ -78,6 +113,18 @@ struct MouseButtonDown { position: Point } +impl MouseButtonDown { + + pub fn relative_to(&self, xy: Point) -> MouseButtonDown { + use ::vecmath::vec2_sub; + + MouseButtonDown{ + position: vec2_sub(self.position, xy), + ..*self + } + } +} + /// The amount of scrolling that has occurred since the last render event. #[derive(Copy, Clone, Debug)] pub struct Scroll { @@ -105,17 +152,19 @@ impl ButtonState { self.was_just_pressed = false; } + /// returns true if the mouse button is currently Down, otherwise false pub fn is_down(&self) -> bool { match self.position { - ButtonPosition::Up => false, - ButtonPosition::Down(_, _) => true + ButtonPosition::Down(_, _) => true, + _ => false } } + /// Returns true if the mouse button is currently Up, otherwise false pub fn is_up(&self) -> bool { match self.position { ButtonPosition::Up => true, - ButtonPosition::Down(_, _) => false + _ => false } } } @@ -133,17 +182,23 @@ impl Mouse { unknown: ButtonState::new(), scroll: Scroll { x: 0.0, y: 0.0 }, drag_distance_threshold: 2.0, - last_button_down_events: [None; 9], + simple_event: None } } /// Return the mouse state with its position relative to the given position. pub fn relative_to(self, xy: Point) -> Mouse { - Mouse { xy: ::vecmath::vec2_sub(self.xy, xy), ..self } + use ::vecmath::vec2_sub; + + let relative_simple_event = self.simple_event.map(|mouse_event| mouse_event.relative_to(xy)); + Mouse { xy: vec2_sub(self.xy, xy), + simple_event: relative_simple_event, ..self } } /// sets the new position of the mouse pub fn move_to(&mut self, xy: Point) { + use self::ButtonPosition::Down; + self.xy = xy; } @@ -151,11 +206,6 @@ impl Mouse { pub fn button_down(&mut self, button: MouseButton) { use input::MouseButton::*; - let button_down = MouseButtonDown { - time: SteadyTime::now(), - position: self.xy.clone() - }; - self.set_last_button_down_time(button, Some(button_down)); let mouse_position = self.xy.clone(); let button_state = match button { @@ -168,20 +218,45 @@ impl Mouse { button_state.was_just_pressed = true; } + /// called when a mouse button is released pub fn button_up(&mut self, button: MouseButton) { use input::MouseButton::*; - self.set_last_button_down_time(button, None); - let button_state = match button { - Left => &mut self.left, - Right => &mut self.right, - Middle => &mut self.middle, - _ => &mut self.unknown - }; - button_state.position = ButtonPosition::Up; - button_state.was_just_released = true; + let current_mouse_position = self.xy.clone(); + let drag_distance_threshold = self.drag_distance_threshold; + let mut new_simple_event: Option = None; + { + let button_state = match button { + Left => &mut self.left, + Right => &mut self.right, + Middle => &mut self.middle, + _ => &mut self.unknown + }; + + if let ButtonPosition::Down(time, start_position) = button_state.position { + let drag_distance = distance_between(start_position, current_mouse_position); + if drag_distance > drag_distance_threshold { + new_simple_event = Some(SimpleMouseEvent::Drag(MouseDragEvent{ + mouse_button: button, + start: MouseButtonDown{ time: time, position: start_position }, + current: MouseButtonDown{ time: SteadyTime::now(), position: current_mouse_position } + })); + } else { + new_simple_event = Some(SimpleMouseEvent::Click(MouseClick{ + mouse_button: button, + position: current_mouse_position + })); + } + } + + button_state.position = ButtonPosition::Up; + button_state.was_just_released = true; + } + + self.simple_event = new_simple_event; } + /// indicates whether or not the mouse was clicked pub fn was_clicked(&self, button: MouseButton) -> bool { use input::MouseButton::*; let button_state = match button { @@ -193,21 +268,36 @@ impl Mouse { button_state.was_just_released } + /// Returns the current `SimpleMouseEvent` if there is one pub fn get_simple_event(&self) -> Option { - use input::MouseButton::{Left, Right, Middle}; - None + self.simple_event } - fn get_last_button_down(&self, button: MouseButton) -> Option { - let index: u32 = button.into(); - self.last_button_down_events[index as usize] - } +} - fn set_last_button_down_time(&mut self, button: MouseButton, maybe_last_down: Option) { - let index: u32 = button.into(); - self.last_button_down_events[index as usize] = maybe_last_down; - } +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() +} +#[test] +fn distance_between_should_return_correct_distances_between_two_points() { + let epsilon: Scalar = 0.0001; + let test_cases: Vec<(Point, Point, Scalar)> = vec![ + /* (pointA, pointB, expected distance between) */ + ([0.0, 0.0], [10.0, 0.0], 10.0), + ([-5.0, 0.0], [5.0, 0.0], 10.0), + ([10.0, -5.0], [12.0, 5.0], 10.1980), + ]; + + for (point_a, point_b, expected_distance) in test_cases { + let distance = distance_between(point_a, point_b); + let abs_diff = (expected_distance - distance).abs(); + assert!(abs_diff <= epsilon, + "expected distance between {:?} and {:?} to equal: {}, but was: {}", + point_a, point_b, expected_distance, distance); + } } #[test] @@ -219,20 +309,6 @@ fn move_to_sets_new_mouse_position() { assert_eq!(new_position, mouse.xy); } -#[test] -fn button_down_sets_last_button_down_info_and_button_up_removes_it() { - use input::MouseButton::Left; - - let mut mouse = Mouse::new(); - assert!(mouse.get_last_button_down(Left).is_none()); - - mouse.button_down(Left); - assert!(mouse.get_last_button_down(Left).is_some()); - - mouse.button_up(Left); - assert!(mouse.get_last_button_down(Left).is_none()); -} - #[test] fn button_down_sets_button_state_to_down() { let mut mouse = Mouse::new(); @@ -276,18 +352,68 @@ fn get_simple_event_returns_click_if_button_goes_down_then_up_in_same_position() fn get_simple_event_returns_drag_event_if_mouse_was_dragged() { use input::MouseButton::Left; let mut mouse = Mouse::new(); + let start_position = mouse.xy; + let new_position = [0.0, mouse.drag_distance_threshold + 1.0]; + mouse.button_down(Left); + mouse.move_to(new_position); + + let actual_event = mouse.get_simple_event(); + assert!(actual_event.is_some()); + + let maybe_drag_event: Option = match actual_event { + Some(SimpleMouseEvent::Drag(mouse_drag_info)) => Some(mouse_drag_info), + _ => None + }; + assert!(maybe_drag_event.is_some()); + let drag_event = maybe_drag_event.unwrap(); + + assert_eq!(start_position, drag_event.start.position); + assert_eq!(new_position, drag_event.current.position); +} + +#[test] +fn get_simple_event_returns_drag_event_if_mouse_was_dragged_then_button_released() { + use input::MouseButton::Left; + let mut mouse = Mouse::new(); + let start_position = mouse.xy; let new_position = [0.0, mouse.drag_distance_threshold + 1.0]; mouse.button_down(Left); mouse.move_to(new_position); mouse.button_up(Left); - let expected_event = SimpleMouseEvent::Drag(MouseDragEvent{ - mouse_button: Left, - button_down_position: [0.0, 0.0], - current_position: new_position, - }); + let actual_event = mouse.get_simple_event(); + assert!(actual_event.is_some()); + + let maybe_drag_event: Option = match actual_event { + Some(SimpleMouseEvent::Drag(mouse_drag_info)) => Some(mouse_drag_info), + _ => None + }; + assert!(maybe_drag_event.is_some()); + let drag_event = maybe_drag_event.unwrap(); + + assert_eq!(start_position, drag_event.start.position); + assert_eq!(new_position, drag_event.current.position); +} + +#[test] +fn get_simple_event_returns_click_event_if_mouse_was_dragged_less_than_drag_threshold() { + use input::MouseButton::Left; + let mut mouse = Mouse::new(); + let start_position = mouse.xy; + let new_position = [0.0, mouse.drag_distance_threshold - 1.0]; + mouse.button_down(Left); + mouse.move_to(new_position); + mouse.button_up(Left); let actual_event = mouse.get_simple_event(); assert!(actual_event.is_some()); - assert_eq!(expected_event, actual_event.unwrap()); + + let maybe_click_event: Option = match actual_event { + Some(SimpleMouseEvent::Click(mouse_click)) => Some(mouse_click), + _ => None + }; + assert!(maybe_click_event.is_some()); + let click_event = maybe_click_event.unwrap(); + + assert_eq!(new_position, click_event.position); } From 0ad6ca0cc702430c5cf2edcb37a86c929c1d8ae2 Mon Sep 17 00:00:00 2001 From: pfried Date: Wed, 30 Dec 2015 21:57:54 -0500 Subject: [PATCH 07/19] changed mouse::ButtonPosition::Down to take a MouseButtonDown struct instead of just a time and Point, for consistency --- examples/custom_widget.rs | 6 +++--- src/mouse.rs | 40 +++++++++++++++-------------------- src/ui.rs | 5 ++++- src/widget/button.rs | 6 +++--- src/widget/drag.rs | 6 +++--- src/widget/envelope_editor.rs | 12 +++++------ src/widget/number_dialer.rs | 8 +++---- src/widget/scroll.rs | 8 +++---- src/widget/slider.rs | 6 +++--- src/widget/text_box.rs | 4 ++-- src/widget/title_bar.rs | 6 +++--- src/widget/toggle.rs | 6 +++--- src/widget/xy_pad.rs | 6 +++--- 13 files changed, 58 insertions(+), 61 deletions(-) diff --git a/examples/custom_widget.rs b/examples/custom_widget.rs index 3418339db..b79bab8a8 100644 --- a/examples/custom_widget.rs +++ b/examples/custom_widget.rs @@ -125,14 +125,14 @@ mod circular_button { // 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. @@ -141,7 +141,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. diff --git a/src/mouse.rs b/src/mouse.rs index 1b17b8ebc..a0fa7a92b 100644 --- a/src/mouse.rs +++ b/src/mouse.rs @@ -26,7 +26,7 @@ pub enum ButtonPosition { /// The mouse button is currently up. Up, /// The mouse button is currently down (pressed). - Down(SteadyTime, Point), + Down(MouseButtonDown), } /// Represents the current state of the Mouse. @@ -108,9 +108,9 @@ impl MouseDragEvent { } #[derive(Copy, Clone, Debug, PartialEq)] -struct MouseButtonDown { - time: SteadyTime, - position: Point +pub struct MouseButtonDown { + pub time: SteadyTime, + pub position: Point } impl MouseButtonDown { @@ -155,7 +155,7 @@ impl ButtonState { /// returns true if the mouse button is currently Down, otherwise false pub fn is_down(&self) -> bool { match self.position { - ButtonPosition::Down(_, _) => true, + ButtonPosition::Down(_) => true, _ => false } } @@ -214,7 +214,10 @@ impl Mouse { Middle => &mut self.middle, _ => &mut self.unknown }; - button_state.position = ButtonPosition::Down(SteadyTime::now(), mouse_position); + button_state.position = ButtonPosition::Down(MouseButtonDown{ + time: SteadyTime::now(), + position: mouse_position + }); button_state.was_just_pressed = true; } @@ -233,12 +236,12 @@ impl Mouse { _ => &mut self.unknown }; - if let ButtonPosition::Down(time, start_position) = button_state.position { - let drag_distance = distance_between(start_position, current_mouse_position); + if let ButtonPosition::Down(mouse_down_info) = button_state.position { + let drag_distance = distance_between(mouse_down_info.position, current_mouse_position); if drag_distance > drag_distance_threshold { new_simple_event = Some(SimpleMouseEvent::Drag(MouseDragEvent{ mouse_button: button, - start: MouseButtonDown{ time: time, position: start_position }, + start: MouseButtonDown{ time: mouse_down_info.time, position: mouse_down_info.position }, current: MouseButtonDown{ time: SteadyTime::now(), position: current_mouse_position } })); } else { @@ -256,18 +259,6 @@ impl Mouse { self.simple_event = new_simple_event; } - /// indicates whether or not the mouse was clicked - pub fn was_clicked(&self, button: MouseButton) -> bool { - use input::MouseButton::*; - let button_state = match button { - Left => self.left, - Right => self.right, - Middle => self.middle, - _ => self.unknown - }; - button_state.was_just_released - } - /// Returns the current `SimpleMouseEvent` if there is one pub fn get_simple_event(&self) -> Option { self.simple_event @@ -317,7 +308,7 @@ fn button_down_sets_button_state_to_down() { mouse.button_down(MouseButton::Left); let is_down = match mouse.left.position { - ButtonPosition::Down(_, _) => true, + ButtonPosition::Down(_) => true, _ => false }; assert!(is_down); @@ -326,7 +317,10 @@ fn button_down_sets_button_state_to_down() { #[test] fn button_up_sets_button_state_to_up() { let mut mouse = Mouse::new(); - mouse.left.position = ButtonPosition::Down(SteadyTime::now(), [0.0, 0.0]); + mouse.left.position = ButtonPosition::Down(MouseButtonDown{ + time: SteadyTime::now(), + position: [0.0, 0.0] + }); mouse.button_up(MouseButton::Left); assert_eq!(ButtonPosition::Up, mouse.left.position); diff --git a/src/ui.rs b/src/ui.rs index 7d022efb0..6b637ee5e 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -265,7 +265,10 @@ impl Ui { Middle => &mut self.mouse.middle, _ => &mut self.mouse.unknown, }; - mouse_button.position = mouse::ButtonPosition::Down(SteadyTime::now(), mouse_position); + mouse_button.position = mouse::ButtonPosition::Down(mouse::MouseButtonDown{ + time: SteadyTime::now(), + position: mouse_position + }); mouse_button.was_just_pressed = true; }, Button::Keyboard(key) => self.keys_just_pressed.push(key), diff --git a/src/widget/button.rs b/src/widget/button.rs index e156fd214..345530b7c 100644 --- a/src/widget/button.rs +++ b/src/widget/button.rs @@ -79,10 +79,10 @@ fn get_new_interaction(is_over: bool, prev: Interaction, mouse: Mouse) -> Intera use mouse::ButtonPosition::{Down, Up}; use self::Interaction::{Normal, Highlighted, Clicked}; match (is_over, prev, mouse.left.position) { - (true, Normal, Down(_, _)) => Normal, - (true, _, Down(_, _)) => Clicked, + (true, Normal, Down(_)) => Normal, + (true, _, Down(_)) => Clicked, (true, _, Up) => Highlighted, - (false, Clicked, Down(_, _)) => Clicked, + (false, Clicked, Down(_)) => Clicked, _ => Normal, } } diff --git a/src/widget/drag.rs b/src/widget/drag.rs index f97cfabb5..8cc72ed48 100644 --- a/src/widget/drag.rs +++ b/src/widget/drag.rs @@ -32,10 +32,10 @@ pub fn drag_widget(xy: Point, rel_rect: Rect, state: State, mouse: Mouse) -> (Po // Determine the new drag state. let new_state = match (is_over, state, mouse.left.position) { - (true, Normal, Down(_, _)) => Normal, + (true, Normal, Down(_)) => Normal, (true, _, Up) => Highlighted, - (true, _, Down(_, _)) | - (false, Clicked(_), Down(_, _)) => Clicked(mouse.xy), + (true, _, Down(_)) | + (false, Clicked(_), Down(_)) => Clicked(mouse.xy), _ => Normal, }; diff --git a/src/widget/envelope_editor.rs b/src/widget/envelope_editor.rs index 6a87b9ffd..dd97d4468 100644 --- a/src/widget/envelope_editor.rs +++ b/src/widget/envelope_editor.rs @@ -178,11 +178,11 @@ fn get_new_interaction(is_over_elem: Option, use self::MouseButton::{Left, Right}; use self::Interaction::{Normal, Highlighted, Clicked}; match (is_over_elem, prev, mouse.left.position, mouse.right.position) { - (Some(_), Normal, Down(_, _), Up) => Normal, + (Some(_), Normal, Down(_), Up) => Normal, (Some(elem), _, Up, Up) => Highlighted(elem), - (Some(elem), Highlighted(_), Down(_, _), Up) => Clicked(elem, Left), - (Some(_), Clicked(p_elem, m_button), Down(_, _), Up) | - (Some(_), Clicked(p_elem, m_button), Up, Down(_, _)) => { + (Some(elem), Highlighted(_), Down(_), Up) => Clicked(elem, Left), + (Some(_), Clicked(p_elem, m_button), Down(_), Up) | + (Some(_), Clicked(p_elem, m_button), Up, Down(_)) => { match p_elem { EnvPoint(idx, _) => Clicked(EnvPoint(idx, (mouse.xy[0], mouse.xy[1])), m_button), // CurvePoint(idx, _) => @@ -190,7 +190,7 @@ fn get_new_interaction(is_over_elem: Option, _ => Clicked(p_elem, m_button), } }, - (None, Clicked(p_elem, m_button), Down(_, _), Up) => { + (None, Clicked(p_elem, m_button), Down(_), Up) => { match (p_elem, m_button) { (EnvPoint(idx, _), Left) => Clicked(EnvPoint(idx, (mouse.xy[0], mouse.xy[1])), Left), @@ -199,7 +199,7 @@ fn get_new_interaction(is_over_elem: Option, _ => Clicked(p_elem, Left), } }, - (Some(_), Highlighted(p_elem), Up, Down(_, _)) => { + (Some(_), Highlighted(p_elem), Up, Down(_)) => { match p_elem { EnvPoint(idx, _) => Clicked(EnvPoint(idx, (mouse.xy[0], mouse.xy[1])), Right), // CurvePoint(idx, _) => Clicked(CurvePoint(idx, (mouse.xy[0], mouse.xy[1])), Right), diff --git a/src/widget/number_dialer.rs b/src/widget/number_dialer.rs index b20ed5670..47f82fd74 100644 --- a/src/widget/number_dialer.rs +++ b/src/widget/number_dialer.rs @@ -189,16 +189,16 @@ fn get_new_interaction(is_over_elem: Option, prev: Interaction, mouse: Mou use self::Elem::ValueGlyph; use self::Interaction::{Normal, Highlighted, Clicked}; match (is_over_elem, prev, mouse.left.position) { - (Some(_), Normal, Down(_, _)) => Normal, + (Some(_), Normal, Down(_)) => Normal, (Some(elem), _, Up) => Highlighted(elem), - (Some(elem), Highlighted(_), Down(_, _)) => Clicked(elem), - (Some(_), Clicked(p_elem), Down(_, _)) => { + (Some(elem), Highlighted(_), Down(_)) => Clicked(elem), + (Some(_), Clicked(p_elem), Down(_)) => { match p_elem { ValueGlyph(idx, _) => Clicked(ValueGlyph(idx, mouse.xy[1])), _ => Clicked(p_elem), } }, - (None, Clicked(p_elem), Down(_, _)) => { + (None, Clicked(p_elem), Down(_)) => { match p_elem { ValueGlyph(idx, _) => Clicked(ValueGlyph(idx, mouse.xy[1])), _ => Clicked(p_elem), diff --git a/src/widget/scroll.rs b/src/widget/scroll.rs index 3926f604b..3e69da840 100644 --- a/src/widget/scroll.rs +++ b/src/widget/scroll.rs @@ -476,11 +476,11 @@ fn new_interaction(bar: &Bar, use self::Elem::Handle; use mouse::ButtonPosition::{Down, Up}; match (is_over_elem, bar.interaction, mouse.left.position) { - (Some(_), Normal, Down(_, _)) => Normal, + (Some(_), Normal, Down(_)) => Normal, (Some(elem), _, Up) => Highlighted(elem), - (Some(_), Highlighted(_), Down(_, _)) | - (_, Clicked(Handle(_)), Down(_, _)) => Clicked(Handle(mouse_scalar)), - (_, Clicked(elem), Down(_, _)) => Clicked(elem), + (Some(_), Highlighted(_), Down(_)) | + (_, Clicked(Handle(_)), Down(_)) => Clicked(Handle(mouse_scalar)), + (_, Clicked(elem), Down(_)) => Clicked(elem), _ => Normal, } } diff --git a/src/widget/slider.rs b/src/widget/slider.rs index 4b1f02df6..c909138d2 100644 --- a/src/widget/slider.rs +++ b/src/widget/slider.rs @@ -95,10 +95,10 @@ fn get_new_interaction(is_over: bool, prev: Interaction, mouse: Mouse) -> Intera use mouse::ButtonPosition::{Down, Up}; use self::Interaction::{Normal, Highlighted, Clicked}; match (is_over, prev, mouse.left.position) { - (true, Normal, Down(_, _)) => Normal, - (true, _, Down(_, _)) => Clicked, + (true, Normal, Down(_)) => Normal, + (true, _, Down(_)) => Clicked, (true, _, Up) => Highlighted, - (false, Clicked, Down(_, _)) => Clicked, + (false, Clicked, Down(_)) => Clicked, _ => Normal, } } diff --git a/src/widget/text_box.rs b/src/widget/text_box.rs index 60828b1ca..736a93cf5 100644 --- a/src/widget/text_box.rs +++ b/src/widget/text_box.rs @@ -250,7 +250,7 @@ fn get_new_interaction(over_elem: Elem, prev_interaction: Interaction, mouse: Mo match prev_interaction { Interaction::Captured(mut prev) => match mouse.left.position { - Down(_, _) => match over_elem { + Down(_) => match over_elem { Elem::Nill => if prev.cursor.anchor == Anchor::None { Uncaptured(Normal) } else { @@ -278,7 +278,7 @@ fn get_new_interaction(over_elem: Elem, prev_interaction: Interaction, mouse: Mo }, Interaction::Uncaptured(prev) => match mouse.left.position { - Down(_, _) => match over_elem { + Down(_) => match over_elem { Elem::Nill => Uncaptured(Normal), Elem::Rect => match prev { Normal => prev_interaction, diff --git a/src/widget/title_bar.rs b/src/widget/title_bar.rs index 73677d57b..1bd4e2b7f 100644 --- a/src/widget/title_bar.rs +++ b/src/widget/title_bar.rs @@ -84,10 +84,10 @@ fn get_new_interaction(is_over: bool, prev: Interaction, mouse: Mouse) -> Intera use mouse::ButtonPosition::{Down, Up}; use self::Interaction::{Normal, Highlighted, Clicked}; match (is_over, prev, mouse.left.position) { - (true, Normal, Down(_, _)) => Normal, - (true, _, Down(_, _)) => Clicked, + (true, Normal, Down(_)) => Normal, + (true, _, Down(_)) => Clicked, (true, _, Up) => Highlighted, - (false, Clicked, Down(_, _)) => Clicked, + (false, Clicked, Down(_)) => Clicked, _ => Normal, } } diff --git a/src/widget/toggle.rs b/src/widget/toggle.rs index 39a0f493e..cd56883a3 100644 --- a/src/widget/toggle.rs +++ b/src/widget/toggle.rs @@ -89,10 +89,10 @@ fn get_new_interaction(is_over: bool, use mouse::ButtonPosition::{Down, Up}; use self::Interaction::{Normal, Highlighted, Clicked}; match (is_over, prev, mouse.left.position) { - (true, Normal, Down(_, _)) => Normal, - (true, _, Down(_, _)) => Clicked, + (true, Normal, Down(_)) => Normal, + (true, _, Down(_)) => Clicked, (true, _, Up) => Highlighted, - (false, Clicked, Down(_, _)) => Clicked, + (false, Clicked, Down(_)) => Clicked, _ => Normal, } } diff --git a/src/widget/xy_pad.rs b/src/widget/xy_pad.rs index 16d7b4512..fad58d0e6 100644 --- a/src/widget/xy_pad.rs +++ b/src/widget/xy_pad.rs @@ -98,10 +98,10 @@ fn get_new_interaction(is_over: bool, use mouse::ButtonPosition::{Down, Up}; use self::Interaction::{Normal, Highlighted, Clicked}; match (is_over, prev, mouse.left.position) { - (true, Normal, Down(_, _)) => Normal, - (true, _, Down(_, _)) => Clicked, + (true, Normal, Down(_)) => Normal, + (true, _, Down(_)) => Clicked, (true, _, Up) => Highlighted, - (false, Clicked, Down(_, _)) => Clicked, + (false, Clicked, Down(_)) => Clicked, _ => Normal, } } From c4bb4b8096007be7aa95745eb20668c347039358 Mon Sep 17 00:00:00 2001 From: pfried Date: Wed, 30 Dec 2015 22:47:04 -0500 Subject: [PATCH 08/19] mouse tests passing for click and drag events --- src/mouse.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/src/mouse.rs b/src/mouse.rs index a0fa7a92b..98c88c6d9 100644 --- a/src/mouse.rs +++ b/src/mouse.rs @@ -94,6 +94,7 @@ pub struct MouseDragEvent { mouse_button: MouseButton, start: MouseButtonDown, current: MouseButtonDown, + button_released: bool } impl MouseDragEvent { @@ -197,7 +198,33 @@ impl Mouse { /// sets the new position of the mouse pub fn move_to(&mut self, xy: Point) { + use input::MouseButton::{Left, Middle, Right}; use self::ButtonPosition::Down; + use self::SimpleMouseEvent::Drag; + + let buttons: Vec<(&ButtonState, MouseButton)> = vec![ + (&self.left, Left), + (&self.middle, Middle), + (&self.right, Right) + ]; + + self.simple_event = buttons.iter() + .filter(|state_and_button| state_and_button.0.is_down()) + .next().and_then(|state_and_button| { + if let Down(button_down_info) = state_and_button.0.position { + Some(Drag(MouseDragEvent{ + start: button_down_info, + current: MouseButtonDown{ + time: SteadyTime::now(), + position: xy + }, + mouse_button: state_and_button.1, + button_released: false + })) + } else { + None + } + }); self.xy = xy; } @@ -242,7 +269,8 @@ impl Mouse { new_simple_event = Some(SimpleMouseEvent::Drag(MouseDragEvent{ mouse_button: button, start: MouseButtonDown{ time: mouse_down_info.time, position: mouse_down_info.position }, - current: MouseButtonDown{ time: SteadyTime::now(), position: current_mouse_position } + current: MouseButtonDown{ time: SteadyTime::now(), position: current_mouse_position }, + button_released: true })); } else { new_simple_event = Some(SimpleMouseEvent::Click(MouseClick{ @@ -343,7 +371,7 @@ fn get_simple_event_returns_click_if_button_goes_down_then_up_in_same_position() } #[test] -fn get_simple_event_returns_drag_event_if_mouse_was_dragged() { +fn get_simple_event_returns_drag_event_if_mouse_was_dragged_without_releasing_button() { use input::MouseButton::Left; let mut mouse = Mouse::new(); let start_position = mouse.xy; @@ -363,6 +391,7 @@ fn get_simple_event_returns_drag_event_if_mouse_was_dragged() { assert_eq!(start_position, drag_event.start.position); assert_eq!(new_position, drag_event.current.position); + assert!(!drag_event.button_released); } #[test] @@ -387,6 +416,31 @@ fn get_simple_event_returns_drag_event_if_mouse_was_dragged_then_button_released assert_eq!(start_position, drag_event.start.position); assert_eq!(new_position, drag_event.current.position); + assert!(drag_event.button_released); +} + +#[test] +fn drag_event_has_original_start_position_after_multiple_mouse_move_events() { + use input::MouseButton::Left; + let mut mouse = Mouse::new(); + let start_position = mouse.xy; + let final_position = [0.0, mouse.drag_distance_threshold + 1.0]; + mouse.button_down(Left); + mouse.move_to([-5.0, 10.0]); + mouse.move_to(final_position); + + let actual_event = mouse.get_simple_event(); + assert!(actual_event.is_some()); + + let maybe_drag_event: Option = match actual_event { + Some(SimpleMouseEvent::Drag(mouse_drag_info)) => Some(mouse_drag_info), + _ => None + }; + assert!(maybe_drag_event.is_some()); + let drag_event = maybe_drag_event.unwrap(); + + assert_eq!(start_position, drag_event.start.position); + assert_eq!(final_position, drag_event.current.position); } #[test] From 4190201cbc7806797e85973deb20b2d0f968f5b1 Mon Sep 17 00:00:00 2001 From: pfried Date: Wed, 30 Dec 2015 23:18:57 -0500 Subject: [PATCH 09/19] moved mouse module into a separate directory and separated new simple mouse events into their own file --- src/{mouse.rs => mouse/mod.rs} | 81 ++--------------------------- src/mouse/simple_events.rs | 95 ++++++++++++++++++++++++++++++++++ src/ui.rs | 2 +- 3 files changed, 100 insertions(+), 78 deletions(-) rename src/{mouse.rs => mouse/mod.rs} (88%) create mode 100644 src/mouse/simple_events.rs diff --git a/src/mouse.rs b/src/mouse/mod.rs similarity index 88% rename from src/mouse.rs rename to src/mouse/mod.rs index 98c88c6d9..53f387e5e 100644 --- a/src/mouse.rs +++ b/src/mouse/mod.rs @@ -3,9 +3,11 @@ //! //! The `Ui` will continuously maintain the latest Mouse state, necessary for widget logic. //! +pub mod simple_events; -pub use input::MouseButton; pub use graphics::math::Scalar; + +use self::simple_events::*; use position::Point; use time::{SteadyTime}; @@ -51,81 +53,6 @@ pub struct Mouse { } -/// Used for simplified mouse event handling. Most widgets can probably -/// just use these events -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum SimpleMouseEvent { - Click(MouseClick), - Drag(MouseDragEvent), -} - -impl SimpleMouseEvent { - - pub fn relative_to(&self, xy: Point) -> Self { - use self::SimpleMouseEvent::*; - - match self { - &Click(mouse_click) => Click(mouse_click.relative_to(xy)), - &Drag(mouse_drag) => Drag(mouse_drag.relative_to(xy)) - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq)] -pub struct MouseClick { - mouse_button: MouseButton, - position: Point -} - -impl MouseClick { - - pub fn relative_to(&self, xy: Point) -> MouseClick { - use ::vecmath::vec2_sub; - - MouseClick{ - position: vec2_sub(self.position, xy), - ..*self - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq)] -pub struct MouseDragEvent { - mouse_button: MouseButton, - start: MouseButtonDown, - current: MouseButtonDown, - button_released: bool -} - -impl MouseDragEvent { - - pub fn relative_to(&self, xy: Point) -> MouseDragEvent { - MouseDragEvent{ - start: self.start.relative_to(xy), - current: self.current.relative_to(xy), - ..*self - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq)] -pub struct MouseButtonDown { - pub time: SteadyTime, - pub position: Point -} - -impl MouseButtonDown { - - pub fn relative_to(&self, xy: Point) -> MouseButtonDown { - use ::vecmath::vec2_sub; - - MouseButtonDown{ - position: vec2_sub(self.position, xy), - ..*self - } - } -} - /// The amount of scrolling that has occurred since the last render event. #[derive(Copy, Clone, Debug)] pub struct Scroll { @@ -200,7 +127,7 @@ impl Mouse { pub fn move_to(&mut self, xy: Point) { use input::MouseButton::{Left, Middle, Right}; use self::ButtonPosition::Down; - use self::SimpleMouseEvent::Drag; + use self::simple_events::SimpleMouseEvent::Drag; let buttons: Vec<(&ButtonState, MouseButton)> = vec![ (&self.left, Left), diff --git a/src/mouse/simple_events.rs b/src/mouse/simple_events.rs new file mode 100644 index 000000000..e35035add --- /dev/null +++ b/src/mouse/simple_events.rs @@ -0,0 +1,95 @@ + +pub use input::MouseButton; + +use position::Point; +use time::SteadyTime; + +/// Used for simplified mouse event handling. Most widgets can probably +/// just use these events +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum SimpleMouseEvent { + /// Indicates that the mouse was clicked. A Click event is created when the mouse button is released, not depressed + Click(MouseClick), + /// Drag event is created when the mouse was moved over a certain threshold while a button was depressed + Drag(MouseDragEvent), +} + + +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct MouseClick { + /// Indicates Which button was clicked + pub mouse_button: MouseButton, + /// The Point describing the click location + pub position: Point +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct MouseDragEvent { + /// Which mouse button is being held during the drag + pub mouse_button: MouseButton, + /// The time and location where the drag was initiated (when the button was pressed) + pub start: MouseButtonDown, + /// The current time and location of the mouse + pub current: MouseButtonDown, + /// This will be false if the button is still being held down, or true if the button was released + pub button_released: bool +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct MouseButtonDown { + /// The time that the mouse button was pressed. + pub time: SteadyTime, + /// The location of the mouse when the button was pressed + pub position: Point +} + +impl MouseClick { + + /// Returns a new copy of the event data relative to the given point + pub fn relative_to(&self, xy: Point) -> MouseClick { + use ::vecmath::vec2_sub; + + MouseClick{ + position: vec2_sub(self.position, xy), + ..*self + } + } +} + +impl MouseDragEvent { + + /// Returns a new copy of the event data relative to the given point + pub fn relative_to(&self, xy: Point) -> MouseDragEvent { + MouseDragEvent{ + start: self.start.relative_to(xy), + current: self.current.relative_to(xy), + ..*self + } + } +} + +impl SimpleMouseEvent { + + /// Returns a new copy of the event data relative to the given point + pub fn relative_to(&self, xy: Point) -> Self { + use self::SimpleMouseEvent::*; + + match self { + &Click(mouse_click) => Click(mouse_click.relative_to(xy)), + &Drag(mouse_drag) => Drag(mouse_drag.relative_to(xy)) + } + } +} + +impl MouseButtonDown { + + /// Returns a new copy of the event data relative to the given point + pub fn relative_to(&self, xy: Point) -> MouseButtonDown { + use ::vecmath::vec2_sub; + + MouseButtonDown{ + position: vec2_sub(self.position, xy), + ..*self + } + } +} diff --git a/src/ui.rs b/src/ui.rs index 6b637ee5e..da41bf075 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -265,7 +265,7 @@ impl Ui { Middle => &mut self.mouse.middle, _ => &mut self.mouse.unknown, }; - mouse_button.position = mouse::ButtonPosition::Down(mouse::MouseButtonDown{ + mouse_button.position = mouse::ButtonPosition::Down(mouse::simple_events::MouseButtonDown{ time: SteadyTime::now(), position: mouse_position }); From 3b6d9b21df5238a60eff6c4cc98845373939aa3a Mon Sep 17 00:00:00 2001 From: pfried Date: Thu, 31 Dec 2015 18:21:57 -0500 Subject: [PATCH 10/19] added SimpleMouseEvent for scrolling --- src/lib.rs | 7 +-- src/mouse/mod.rs | 105 ++++++++++++++++++++++++------------- src/mouse/simple_events.rs | 15 +++++- src/ui.rs | 2 +- 4 files changed, 88 insertions(+), 41 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 22c3ac94b..1497389de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,7 +68,8 @@ pub use label::{FontSize, Labelable}; pub use mouse::Mouse; pub use mouse::ButtonState as MouseButtonState; pub use mouse::ButtonPosition as MouseButtonPosition; -pub use mouse::Scroll as MouseScroll; +pub use mouse::simple_events::Scroll as MouseScroll; +pub use mouse::simple_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; @@ -108,10 +109,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. diff --git a/src/mouse/mod.rs b/src/mouse/mod.rs index 53f387e5e..cac28cb61 100644 --- a/src/mouse/mod.rs +++ b/src/mouse/mod.rs @@ -53,16 +53,6 @@ pub struct Mouse { } -/// The amount of scrolling that has occurred since the last render event. -#[derive(Copy, Clone, Debug)] -pub struct Scroll { - /// Scrolling across the x axis. - pub x: f64, - /// Scrolling across the y axis. - pub y: f64, -} - - impl ButtonState { /// Constructor for a default ButtonState. @@ -123,36 +113,45 @@ impl Mouse { simple_event: relative_simple_event, ..self } } - /// sets the new position of the mouse + /// Call whenever the mouse moves, sets the new position of the mouse pub fn move_to(&mut self, xy: Point) { use input::MouseButton::{Left, Middle, Right}; use self::ButtonPosition::Down; use self::simple_events::SimpleMouseEvent::Drag; - let buttons: Vec<(&ButtonState, MouseButton)> = vec![ - (&self.left, Left), - (&self.middle, Middle), - (&self.right, Right) - ]; - - self.simple_event = buttons.iter() - .filter(|state_and_button| state_and_button.0.is_down()) - .next().and_then(|state_and_button| { - if let Down(button_down_info) = state_and_button.0.position { - Some(Drag(MouseDragEvent{ - start: button_down_info, - current: MouseButtonDown{ - time: SteadyTime::now(), - position: xy - }, - mouse_button: state_and_button.1, - button_released: false - })) - } else { - None - } - }); + let new_event: Option = { + + // These are the only buttons we care about at the moment. + let buttons: Vec<(&ButtonState, MouseButton)> = vec![ + (&self.left, Left), + (&self.middle, Middle), + (&self.right, Right) + ]; + + buttons.iter() + // Find the first button that is current in the Down position + .filter(|state_and_button| state_and_button.0.is_down()) + .next().and_then(|state_and_button| { + // Once we have the button down info, map that to a MouseDragEvent + if let Down(button_down_info) = state_and_button.0.position { + Some(Drag(MouseDragEvent{ + start: button_down_info, + current: MouseButtonDown{ + time: SteadyTime::now(), + position: xy + }, + mouse_button: state_and_button.1, + button_released: false + })) + } else { + None + } + }) + }; + + self.set_simple_event(new_event); + // update the current position of the mouse self.xy = xy; } @@ -211,7 +210,15 @@ impl Mouse { button_state.was_just_released = true; } - self.simple_event = new_simple_event; + self.set_simple_event(new_simple_event); + } + + /// called when the mouse wheel is scrolled + pub fn scroll(&mut self, x: f64, y: f64) { + self.set_simple_event(Some(SimpleMouseEvent::Scroll(Scroll{ + x: x, + y: y + }))); } /// Returns the current `SimpleMouseEvent` if there is one @@ -219,6 +226,16 @@ impl Mouse { self.simple_event } + fn set_simple_event(&mut self, event: Option) { + if self.simple_event.is_some() { + let old_event = self.simple_event.take(); + println!("Discarding old MouseEvent: {:?} in favor of {:?}", old_event, event); + } + if event.is_some() { + self.simple_event = event; + } + } + } fn distance_between(a: Point, b: Point) -> Scalar { @@ -227,6 +244,21 @@ fn distance_between(a: Point, b: Point) -> Scalar { (dx_2 + dy_2).abs().sqrt() } +#[test] +fn scroll_should_create_a_simple_mouse_event_of_scroll() { + let mut mouse = Mouse::new(); + mouse.scroll(2.0, 3.0); + + match mouse.get_simple_event() { + Some(SimpleMouseEvent::Scroll(scroll_info)) => { + assert_eq!(2.0, scroll_info.x); + assert_eq!(3.0, scroll_info.y); + }, + Some(thing) => panic!("Expected a SimpleMouseEvent::Scroll, instead got: {:?}", thing), + _ => panic!("expected a SimpleMouseEvent::Scroll, instead got None") + } +} + #[test] fn distance_between_should_return_correct_distances_between_two_points() { let epsilon: Scalar = 0.0001; @@ -267,6 +299,7 @@ fn button_down_sets_button_state_to_down() { _ => false }; assert!(is_down); + assert!(mouse.left.was_just_pressed); } #[test] @@ -279,6 +312,7 @@ fn button_up_sets_button_state_to_up() { mouse.button_up(MouseButton::Left); assert_eq!(ButtonPosition::Up, mouse.left.position); + assert!(mouse.left.was_just_released); } #[test] @@ -374,7 +408,6 @@ fn drag_event_has_original_start_position_after_multiple_mouse_move_events() { fn get_simple_event_returns_click_event_if_mouse_was_dragged_less_than_drag_threshold() { use input::MouseButton::Left; let mut mouse = Mouse::new(); - let start_position = mouse.xy; let new_position = [0.0, mouse.drag_distance_threshold - 1.0]; mouse.button_down(Left); mouse.move_to(new_position); diff --git a/src/mouse/simple_events.rs b/src/mouse/simple_events.rs index e35035add..01f3b9cc7 100644 --- a/src/mouse/simple_events.rs +++ b/src/mouse/simple_events.rs @@ -12,6 +12,8 @@ pub enum SimpleMouseEvent { Click(MouseClick), /// Drag event is created when the mouse was moved over a certain threshold while a button was depressed Drag(MouseDragEvent), + /// Scroll event is created when whenever the scroll wheel is moved + Scroll(Scroll), } @@ -43,6 +45,16 @@ pub struct MouseButtonDown { pub position: Point } +/// The amount of scrolling that has occurred since the last render event. +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct Scroll { + /// Scrolling across the x axis. + pub x: f64, + /// Scrolling across the y axis. + pub y: f64, +} + + impl MouseClick { /// Returns a new copy of the event data relative to the given point @@ -76,7 +88,8 @@ impl SimpleMouseEvent { match self { &Click(mouse_click) => Click(mouse_click.relative_to(xy)), - &Drag(mouse_drag) => Drag(mouse_drag.relative_to(xy)) + &Drag(mouse_drag) => Drag(mouse_drag.relative_to(xy)), + &Scroll(scroll_info) => Scroll(scroll_info) } } } diff --git a/src/ui.rs b/src/ui.rs index da41bf075..fb0a03d36 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -510,7 +510,7 @@ impl Ui { self.text_just_entered.clear(); // Reset the mouse state. - self.mouse.scroll = mouse::Scroll { x: 0.0, y: 0.0 }; + self.mouse.scroll = mouse::simple_events::Scroll { x: 0.0, y: 0.0 }; self.mouse.left.reset_pressed_and_released(); self.mouse.middle.reset_pressed_and_released(); self.mouse.right.reset_pressed_and_released(); From 4da7672400ecc6e90a2bd5267338c54be6838a2b Mon Sep 17 00:00:00 2001 From: pfried Date: Thu, 31 Dec 2015 19:00:03 -0500 Subject: [PATCH 11/19] change ui to use new mouse methods, added reset method to mouse --- src/mouse/mod.rs | 45 +++++++++++++++++++++++++++++++++++++++++---- src/ui.rs | 36 ++++++------------------------------ 2 files changed, 47 insertions(+), 34 deletions(-) diff --git a/src/mouse/mod.rs b/src/mouse/mod.rs index cac28cb61..3ad70ce92 100644 --- a/src/mouse/mod.rs +++ b/src/mouse/mod.rs @@ -221,16 +221,23 @@ impl Mouse { }))); } + /// resets the state of the mouse + pub fn reset(&mut self) { + self.left.reset_pressed_and_released(); + self.right.reset_pressed_and_released(); + self.middle.reset_pressed_and_released(); + self.unknown.reset_pressed_and_released(); + self.simple_event = None; + self.scroll.x = 0.0; + self.scroll.y = 0.0; + } + /// Returns the current `SimpleMouseEvent` if there is one pub fn get_simple_event(&self) -> Option { self.simple_event } fn set_simple_event(&mut self, event: Option) { - if self.simple_event.is_some() { - let old_event = self.simple_event.take(); - println!("Discarding old MouseEvent: {:?} in favor of {:?}", old_event, event); - } if event.is_some() { self.simple_event = event; } @@ -244,6 +251,36 @@ fn distance_between(a: Point, b: Point) -> Scalar { (dx_2 + dy_2).abs().sqrt() } +#[test] +fn reset_should_reset_all_button_states_and_scroll_state() { + let mut mouse = Mouse::new(); + mouse.left.was_just_pressed = true; + mouse.left.was_just_released = true; + mouse.right.was_just_pressed = true; + mouse.right.was_just_released = true; + mouse.middle.was_just_pressed = true; + mouse.middle.was_just_released = true; + mouse.unknown.was_just_pressed = true; + mouse.unknown.was_just_released = true; + mouse.scroll.x = 10.0; + mouse.scroll.y = 20.0; + mouse.simple_event = Some(SimpleMouseEvent::Scroll(Scroll{x: 2.0, y: 33.3})); + + mouse.reset(); + + assert!(!mouse.left.was_just_pressed); + assert!(!mouse.left.was_just_released); + assert!(!mouse.right.was_just_pressed); + assert!(!mouse.right.was_just_released); + assert!(!mouse.middle.was_just_pressed); + assert!(!mouse.middle.was_just_released); + assert!(!mouse.unknown.was_just_pressed); + assert!(!mouse.unknown.was_just_released); + assert_eq!(0.0, mouse.scroll.x); + assert_eq!(0.0, mouse.scroll.y); + assert!(mouse.simple_event.is_none()); +} + #[test] fn scroll_should_create_a_simple_mouse_event_of_scroll() { let mut mouse = Mouse::new(); diff --git a/src/ui.rs b/src/ui.rs index fb0a03d36..8f9908874 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -243,33 +243,20 @@ impl Ui { event.mouse_cursor(|x, y| { // Convert mouse coords to (0, 0) origin. - self.mouse.xy = [x - self.win_w / 2.0, -(y - self.win_h / 2.0)]; + self.mouse.move_to([x - self.win_w / 2.0, -(y - self.win_h / 2.0)]) }); event.mouse_scroll(|x, y| { - self.mouse.scroll.x += x; - self.mouse.scroll.y += y; + self.mouse.scroll(x, y); }); event.press(|button_type| { use input::Button; - use input::MouseButton::{Left, Middle, Right}; - let mouse_position = self.mouse.xy.clone(); match button_type { Button::Mouse(button) => { self.widget_under_mouse_captures_keyboard(); - let mouse_button = match button { - Left => &mut self.mouse.left, - Right => &mut self.mouse.right, - Middle => &mut self.mouse.middle, - _ => &mut self.mouse.unknown, - }; - mouse_button.position = mouse::ButtonPosition::Down(mouse::simple_events::MouseButtonDown{ - time: SteadyTime::now(), - position: mouse_position - }); - mouse_button.was_just_pressed = true; + self.mouse.button_down(button); }, Button::Keyboard(key) => self.keys_just_pressed.push(key), _ => {} @@ -278,18 +265,11 @@ impl Ui { event.release(|button_type| { use input::Button; - use input::MouseButton::{Left, Middle, Right}; + match button_type { Button::Mouse(button) => { self.widget_under_mouse_captures_keyboard(); - let mouse_button = match button { - Left => &mut self.mouse.left, - Right => &mut self.mouse.right, - Middle => &mut self.mouse.middle, - _ => &mut self.mouse.unknown, - }; - mouse_button.position = mouse::ButtonPosition::Up; - mouse_button.was_just_released = true; + self.mouse.button_up(button); }, Button::Keyboard(key) => self.keys_just_released.push(key), _ => {} @@ -510,11 +490,7 @@ impl Ui { self.text_just_entered.clear(); // Reset the mouse state. - self.mouse.scroll = mouse::simple_events::Scroll { x: 0.0, y: 0.0 }; - self.mouse.left.reset_pressed_and_released(); - self.mouse.middle.reset_pressed_and_released(); - self.mouse.right.reset_pressed_and_released(); - self.mouse.unknown.reset_pressed_and_released(); + self.mouse.reset(); } From 3e342d294ac50eee6638767e0f0274cb2e7d143f Mon Sep 17 00:00:00 2001 From: pfried Date: Sun, 3 Jan 2016 12:07:01 -0500 Subject: [PATCH 12/19] more changes to TextBox handling input --- src/mouse/simple_events.rs | 44 +++++++++++ src/widget/text_box.rs | 145 ++++++++++++++++++++++++++----------- 2 files changed, 147 insertions(+), 42 deletions(-) diff --git a/src/mouse/simple_events.rs b/src/mouse/simple_events.rs index 01f3b9cc7..833f69cdb 100644 --- a/src/mouse/simple_events.rs +++ b/src/mouse/simple_events.rs @@ -4,6 +4,9 @@ pub use input::MouseButton; use position::Point; use time::SteadyTime; +#[cfg(test)] +use position::Scalar; + /// Used for simplified mouse event handling. Most widgets can probably /// just use these events #[derive(Copy, Clone, Debug, PartialEq)] @@ -106,3 +109,44 @@ impl MouseButtonDown { } } } + +#[test] +fn click_event_should_be_made_relative_to_a_point() { + let click = MouseClick{ + mouse_button: MouseButton::Left, + position: [10.0, 20.0] + }; + + let relative_click = click.relative_to([5.0, 10.0]); + + assert_float_eq(5.0, relative_click .position[0]); + assert_float_eq(10.0, relative_click .position[1]); +} + +#[test] +fn drag_event_should_be_made_relative_to_a_point() { + let drag = MouseDragEvent{ + mouse_button: MouseButton::Left, + start: MouseButtonDown{ + time: SteadyTime::now(), + position: [4.0, -5.0] + }, + current: MouseButtonDown{ + time: SteadyTime::now(), + position: [24.0, -10.0] + }, + button_released: false + }; + + let relative_drag = drag.relative_to([20.0, -5.0]); + assert_float_eq(-16.0, relative_drag.start.position[0]); + assert_float_eq(0.0, relative_drag.start.position[1]); + assert_float_eq(4.0, relative_drag.current.position[0]); + assert_float_eq(-5.0, relative_drag.current.position[1]); +} + +#[cfg(test)] +pub fn assert_float_eq(a: Scalar, b: Scalar) { + let epsilon = 0.0001; + assert!((a - epsilon) <= b && (a + epsilon) >= b, format!("{} != {}", a, b)); +} diff --git a/src/widget/text_box.rs b/src/widget/text_box.rs index 736a93cf5..82bee276a 100644 --- a/src/widget/text_box.rs +++ b/src/widget/text_box.rs @@ -342,55 +342,116 @@ impl<'a, F> TextBox<'a, F> { } -fn get_clicked_elem(text: &str, args: &UpdateArgs, C>) -> Elem +fn get_clicked_elem(text: &str, point: Point, args: &UpdateArgs, C>) -> Elem where C: CharacterCache, F: FnMut(&mut String) { - let maybe_elem_under_mouse: Option = args.ui.input().maybe_mouse.iter() - .filter(|mouse| mouse.left.was_just_pressed) - .map(|mouse| { - let style = args.style; - let theme = args.ui.theme(); - let font_size = style.font_size(theme); - let (xy, widget_dimension) = args.rect.xy_dim(); - let inner_dimension = get_inner_dimensions(widget_dimension, style, theme); - let relative_mouse = mouse.relative_to(xy); - let glyph_cache = args.ui.glyph_cache(); - let text_w = glyph_cache.width(font_size, text); - let text_x = text_w / 2.0 - inner_dimension[0] / 2.0 + TEXT_PADDING; - let text_start_x = text_x - text_w / 2.0; - over_elem(args.ui.glyph_cache(), - relative_mouse.xy, - widget_dimension, - inner_dimension, - text_start_x, - text_w, - font_size, - text) - }).next(); - maybe_elem_under_mouse.unwrap_or(Elem::Nill) + let style = args.style; + let theme = args.ui.theme(); + let font_size = style.font_size(theme); + let (xy, widget_dimension) = args.rect.xy_dim(); + let inner_dimension = get_inner_dimensions(widget_dimension, style, theme); + let glyph_cache = args.ui.glyph_cache(); + let text_w = glyph_cache.width(font_size, text); + let text_x = text_w / 2.0 - inner_dimension[0] / 2.0 + TEXT_PADDING; + let text_start_x = text_x - text_w / 2.0; + over_elem(args.ui.glyph_cache(), + point, + widget_dimension, + inner_dimension, + text_start_x, + text_w, + font_size, + text) +} + +fn get_elem_for_drag(text: &str, point: Point, args: &UpdateArgs, C>) -> Elem + where C: CharacterCache, + F: FnMut(&mut String) { + let style = args.style; + let theme = args.ui.theme(); + let font_size = style.font_size(theme); + let (_xy, widget_dimension) = args.rect.xy_dim(); + let inner_dimension = get_inner_dimensions(widget_dimension, style, theme); + let glyph_cache = args.ui.glyph_cache(); + let text_w = glyph_cache.width(font_size, text); + let text_x = text_w / 2.0 - inner_dimension[0] / 2.0 + TEXT_PADDING; + let text_start_x = text_x - text_w / 2.0; + let (cursor_index, _) = closest_idx(args.ui.glyph_cache(), + point, + text_start_x, + text_w, + font_size, + text); + Elem::Char(cursor_index) +} + +fn get_interaction_for_click(text: &str, clicked_elem: Elem) -> Interaction { + match clicked_elem { + Elem::Char(idx) => Interaction::Captured(View{ + cursor: Cursor::from_index(idx), + offset: 0f64 + }), + Elem::Rect => Interaction::Captured(View{ + cursor: Cursor::from_range(0, text.chars().count()), + offset: 0f64 + }), + Elem::Nill => Interaction::Uncaptured(Uncaptured::Normal) + } +} + +fn get_interaction_for_drag(start_elem: Elem, end_elem: Elem) -> Interaction { + if let (Elem::Char(start_idx), Elem::Char(end_idx)) = (start_elem, end_elem) { + Interaction::Captured(View{ + cursor: Cursor::from_range(start_idx, end_idx), + offset: 0f64 + }) + } else { + Interaction::Captured(View{ + cursor: Cursor::from_index(0), + offset: 0f64 + }) + } +} + +fn get_highlight_all_interaction(text: &str) -> Interaction { + Interaction::Captured(View{ + cursor: Cursor::from_range(0, text.chars().count()), + offset: 0.0 + }) } fn get_new_interaction_2(text: &str, args: &UpdateArgs, C>) -> Interaction where C: CharacterCache, F: FnMut(&mut String) { - let prev_state: &State = args.state.view(); - let clicked_elem = get_clicked_elem(text, args); - match clicked_elem { - Elem::Char(char_index) => - Interaction::Captured(View{cursor: Cursor::from_index(char_index), offset: 0f64 }), - Elem::Rect => - Interaction::Captured(View{cursor: Cursor::from_range(0, text.chars().count()), offset: 0f64}), - Elem::Nill if args.ui.is_capturing_keyboard() => { - if let Interaction::Captured(view) = prev_state.interaction { - Interaction::Captured(view) - } else { - Interaction::Captured(View{cursor: Cursor::from_range(0, text.chars().count()), offset: 0f64}) - } - }, - _ => args.ui.input().maybe_mouse.map(|_mouse| { - Interaction::Uncaptured(Uncaptured::Highlighted) - }).unwrap_or(Interaction::Uncaptured(Uncaptured::Normal)) - } + let maybe_mouse = args.ui.input().maybe_mouse; + let maybe_mouse_event = maybe_mouse.and_then(|mouse| mouse.get_simple_event()); + + maybe_mouse_event.and_then(|event| { + use ::mouse::simple_events::SimpleMouseEvent::{Click, Drag}; + use ::mouse::simple_events::MouseButton; + match event { + Click(click_info) if click_info.mouse_button == MouseButton::Left => { + let clicked_elem = get_clicked_elem(text, click_info.position, args); + Some(get_interaction_for_click(text, clicked_elem)) + }, + Drag(drag_info) => { + let drag_start = get_elem_for_drag(text, drag_info.start.position, args); + let drag_end = get_elem_for_drag(text, drag_info.current.position, args); + Some(get_interaction_for_drag(drag_start, drag_end)) + }, + _ => None + } + }).unwrap_or_else(|| { + let prev_interaction = args.state.view().interaction; + let is_capturing_keyboard = args.ui.is_capturing_keyboard(); + match prev_interaction { + Interaction::Captured(_) if is_capturing_keyboard => + prev_interaction, + _ if is_capturing_keyboard => get_highlight_all_interaction(text), + _ if maybe_mouse.is_some() => Interaction::Uncaptured(Uncaptured::Highlighted), + _ => Interaction::Uncaptured(Uncaptured::Normal) + } + }) } fn get_inner_dimensions(outer_rect: Dimensions, style: &Style, theme: &Theme) -> Dimensions { From 152c14270555703b707fdd20b549749c27cdbdad Mon Sep 17 00:00:00 2001 From: pfried Date: Sun, 3 Jan 2016 16:52:27 -0500 Subject: [PATCH 13/19] added doc comments to MouseButtonMap --- src/lib.rs | 4 +++- src/mouse/mod.rs | 14 ++++++++++++-- src/mouse/simple_events.rs | 14 ++++++++++++-- src/widget/text_box.rs | 2 +- 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1497389de..ce17f499d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,7 +65,9 @@ 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::simple_events::Scroll as MouseScroll; diff --git a/src/mouse/mod.rs b/src/mouse/mod.rs index 3ad70ce92..968b7afe8 100644 --- a/src/mouse/mod.rs +++ b/src/mouse/mod.rs @@ -3,10 +3,14 @@ //! //! The `Ui` will continuously maintain the latest Mouse state, necessary for widget logic. //! + pub mod simple_events; +mod button_map; -pub use graphics::math::Scalar; +pub use self::button_map::{ButtonMap, NUM_MOUSE_BUTTONS}; +pub use input::MouseButton; +use graphics::math::Scalar; use self::simple_events::*; use position::Point; use time::{SteadyTime}; @@ -20,6 +24,8 @@ pub struct ButtonState { pub was_just_released: bool, /// The current position of the button. pub position: ButtonPosition, + /// SimpleMouseEvent for events corresponding to this button. + pub event: Option, } /// Represents the current state of a mouse button. @@ -50,6 +56,8 @@ pub struct Mouse { pub drag_distance_threshold: Scalar, /// Simple mouse event that is waiting to be consumed pub simple_event: Option, + /// Map of MouseButton to ButtonState + pub button_map: ButtonMap, } @@ -61,6 +69,7 @@ impl ButtonState { was_just_released: false, was_just_pressed: false, position: ButtonPosition::Up, + event: None } } @@ -100,7 +109,8 @@ impl Mouse { unknown: ButtonState::new(), scroll: Scroll { x: 0.0, y: 0.0 }, drag_distance_threshold: 2.0, - simple_event: None + simple_event: None, + button_map: ButtonMap::new(), } } diff --git a/src/mouse/simple_events.rs b/src/mouse/simple_events.rs index 833f69cdb..118ba65a3 100644 --- a/src/mouse/simple_events.rs +++ b/src/mouse/simple_events.rs @@ -1,6 +1,8 @@ +//! module for abstracting over most mouse events. +//! Handles most common mouse events so that widgets don't have to +//! store any mouse state. -pub use input::MouseButton; - +use input::MouseButton; use position::Point; use time::SteadyTime; @@ -20,6 +22,9 @@ pub enum SimpleMouseEvent { } +/// Info on a simple mouse click event. This event gets dispatched when a +/// mouse button goes down then up without moving more than the drag threshold +/// while the button is depressed. #[derive(Copy, Clone, Debug, PartialEq)] pub struct MouseClick { /// Indicates Which button was clicked @@ -28,6 +33,10 @@ pub struct MouseClick { pub position: Point } +/// Info on a simple mouse drag event. This event gets dispached when a mouse +/// button is depressed and the mouse is moved a distance greater than the +/// drag threshold. Holds the start and end positions of the drag, as well as +/// whether the mouse button is still being depressed. #[derive(Copy, Clone, Debug, PartialEq)] pub struct MouseDragEvent { /// Which mouse button is being held during the drag @@ -40,6 +49,7 @@ pub struct MouseDragEvent { pub button_released: bool } +/// Holds info on when a mouse button was depressed or released. #[derive(Copy, Clone, Debug, PartialEq)] pub struct MouseButtonDown { /// The time that the mouse button was pressed. diff --git a/src/widget/text_box.rs b/src/widget/text_box.rs index 82bee276a..95b45e6e8 100644 --- a/src/widget/text_box.rs +++ b/src/widget/text_box.rs @@ -428,7 +428,7 @@ fn get_new_interaction_2(text: &str, args: &UpdateArgs, C>) -> maybe_mouse_event.and_then(|event| { use ::mouse::simple_events::SimpleMouseEvent::{Click, Drag}; - use ::mouse::simple_events::MouseButton; + use ::mouse::MouseButton; match event { Click(click_info) if click_info.mouse_button == MouseButton::Left => { let clicked_elem = get_clicked_elem(text, click_info.position, args); From ab04e2f494e08e10abb969142b1586b4cd15523b Mon Sep 17 00:00:00 2001 From: pfried Date: Sun, 3 Jan 2016 18:53:03 -0500 Subject: [PATCH 14/19] changed all widgets and examples to use mouse ButtonMap instead of mouse.left and such --- examples/custom_widget.rs | 5 +- src/mouse/button_map.rs | 100 +++++++++++++++++++++ src/mouse/mod.rs | 158 +++++++++++++++------------------- src/widget/button.rs | 5 +- src/widget/drag.rs | 4 +- src/widget/drop_down_list.rs | 4 +- src/widget/envelope_editor.rs | 17 ++-- src/widget/mod.rs | 4 +- src/widget/number_dialer.rs | 3 +- src/widget/scroll.rs | 5 +- src/widget/slider.rs | 4 +- src/widget/text_box.rs | 6 +- src/widget/title_bar.rs | 4 +- src/widget/toggle.rs | 4 +- src/widget/xy_pad.rs | 4 +- 15 files changed, 214 insertions(+), 113 deletions(-) create mode 100644 src/mouse/button_map.rs diff --git a/examples/custom_widget.rs b/examples/custom_widget.rs index b79bab8a8..fa50c0964 100644 --- a/examples/custom_widget.rs +++ b/examples/custom_widget.rs @@ -120,8 +120,11 @@ 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. diff --git a/src/mouse/button_map.rs b/src/mouse/button_map.rs new file mode 100644 index 000000000..48a496a16 --- /dev/null +++ b/src/mouse/button_map.rs @@ -0,0 +1,100 @@ +use super::MouseButton; +use super::ButtonState; +use std::slice::{IterMut, Iter}; + +/// 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(&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::>() + } + + /// returns an iterator over mutable references to all the button states + pub fn iter_mut(&mut self) -> IterMut { + self.button_states.iter_mut() + } + + pub fn iter(&self) -> Iter { + self.button_states.iter() + } + + fn button_idx(button: MouseButton) -> usize { + u32::from(button) as usize + } +} diff --git a/src/mouse/mod.rs b/src/mouse/mod.rs index 968b7afe8..0fff43066 100644 --- a/src/mouse/mod.rs +++ b/src/mouse/mod.rs @@ -42,14 +42,6 @@ pub enum ButtonPosition { pub struct Mouse { /// Position of the mouse cursor. pub xy: Point, - /// Left mouse button state. - pub left: ButtonState, - /// Middle mouse button state. - pub middle: ButtonState, - /// Right mouse button state. - pub right: ButtonState, - /// Unknown button state. - pub unknown: ButtonState, /// Amount that the mouse has scrolled since the last render. pub scroll: Scroll, /// Movements less than this threshold will not be considered drags @@ -57,7 +49,7 @@ pub struct Mouse { /// Simple mouse event that is waiting to be consumed pub simple_event: Option, /// Map of MouseButton to ButtonState - pub button_map: ButtonMap, + pub buttons: ButtonMap, } @@ -103,14 +95,10 @@ impl Mouse { pub fn new() -> Mouse { Mouse { xy: [0.0, 0.0], - left: ButtonState::new(), - middle: ButtonState::new(), - right: ButtonState::new(), - unknown: ButtonState::new(), scroll: Scroll { x: 0.0, y: 0.0 }, drag_distance_threshold: 2.0, simple_event: None, - button_map: ButtonMap::new(), + buttons: ButtonMap::new(), } } @@ -125,36 +113,30 @@ impl Mouse { /// Call whenever the mouse moves, sets the new position of the mouse pub fn move_to(&mut self, xy: Point) { - use input::MouseButton::{Left, Middle, Right}; use self::ButtonPosition::Down; use self::simple_events::SimpleMouseEvent::Drag; let new_event: Option = { - // These are the only buttons we care about at the moment. - let buttons: Vec<(&ButtonState, MouseButton)> = vec![ - (&self.left, Left), - (&self.middle, Middle), - (&self.right, Right) - ]; - - buttons.iter() + self.buttons.all_buttons().iter() // Find the first button that is current in the Down position - .filter(|state_and_button| state_and_button.0.is_down()) - .next().and_then(|state_and_button| { + .filter(|button_and_state| button_and_state.1.is_down()) + .next().and_then(|button_and_state| { // Once we have the button down info, map that to a MouseDragEvent - if let Down(button_down_info) = state_and_button.0.position { - Some(Drag(MouseDragEvent{ - start: button_down_info, - current: MouseButtonDown{ - time: SteadyTime::now(), - position: xy - }, - mouse_button: state_and_button.1, - button_released: false - })) - } else { - None + let button_position = button_and_state.1.position; + match button_position { + Down(info) if self.is_drag(info.position, xy) => { + Some(Drag(MouseDragEvent{ + start: info, + current: MouseButtonDown{ + time: SteadyTime::now(), + position: xy + }, + mouse_button: button_and_state.0, + button_released: false + })) + }, + _ => None } }) }; @@ -167,37 +149,24 @@ impl Mouse { /// Sends the mouse a button down event pub fn button_down(&mut self, button: MouseButton) { - use input::MouseButton::*; - let mouse_position = self.xy.clone(); - let button_state = match button { - Left => &mut self.left, - Right => &mut self.right, - Middle => &mut self.middle, - _ => &mut self.unknown - }; - button_state.position = ButtonPosition::Down(MouseButtonDown{ - time: SteadyTime::now(), - position: mouse_position + self.buttons.update(button, |state| { + state.position = ButtonPosition::Down(MouseButtonDown{ + time: SteadyTime::now(), + position: mouse_position + }); + state.was_just_pressed = true; }); - button_state.was_just_pressed = true; } /// called when a mouse button is released pub fn button_up(&mut self, button: MouseButton) { - use input::MouseButton::*; - let current_mouse_position = self.xy.clone(); let drag_distance_threshold = self.drag_distance_threshold; let mut new_simple_event: Option = None; { - let button_state = match button { - Left => &mut self.left, - Right => &mut self.right, - Middle => &mut self.middle, - _ => &mut self.unknown - }; + let button_state = self.buttons.get_mut(button); if let ButtonPosition::Down(mouse_down_info) = button_state.position { let drag_distance = distance_between(mouse_down_info.position, current_mouse_position); @@ -233,10 +202,10 @@ impl Mouse { /// resets the state of the mouse pub fn reset(&mut self) { - self.left.reset_pressed_and_released(); - self.right.reset_pressed_and_released(); - self.middle.reset_pressed_and_released(); - self.unknown.reset_pressed_and_released(); + for button_state in self.buttons.iter_mut() { + button_state.reset_pressed_and_released(); + button_state.event = None; + } self.simple_event = None; self.scroll.x = 0.0; self.scroll.y = 0.0; @@ -247,6 +216,10 @@ impl Mouse { self.simple_event } + fn is_drag(&self, start_position: Point, end_position: Point) -> bool { + distance_between(start_position, end_position) >= self.drag_distance_threshold + } + fn set_simple_event(&mut self, event: Option) { if event.is_some() { self.simple_event = event; @@ -264,28 +237,20 @@ fn distance_between(a: Point, b: Point) -> Scalar { #[test] fn reset_should_reset_all_button_states_and_scroll_state() { let mut mouse = Mouse::new(); - mouse.left.was_just_pressed = true; - mouse.left.was_just_released = true; - mouse.right.was_just_pressed = true; - mouse.right.was_just_released = true; - mouse.middle.was_just_pressed = true; - mouse.middle.was_just_released = true; - mouse.unknown.was_just_pressed = true; - mouse.unknown.was_just_released = true; + for button in mouse.buttons.iter_mut() { + button.was_just_pressed = true; + button.was_just_released = true; + } mouse.scroll.x = 10.0; mouse.scroll.y = 20.0; mouse.simple_event = Some(SimpleMouseEvent::Scroll(Scroll{x: 2.0, y: 33.3})); mouse.reset(); - assert!(!mouse.left.was_just_pressed); - assert!(!mouse.left.was_just_released); - assert!(!mouse.right.was_just_pressed); - assert!(!mouse.right.was_just_released); - assert!(!mouse.middle.was_just_pressed); - assert!(!mouse.middle.was_just_released); - assert!(!mouse.unknown.was_just_pressed); - assert!(!mouse.unknown.was_just_released); + for button in mouse.buttons.iter() { + assert!(!button.was_just_pressed); + assert!(!button.was_just_released); + } assert_eq!(0.0, mouse.scroll.x); assert_eq!(0.0, mouse.scroll.y); assert!(mouse.simple_event.is_none()); @@ -337,29 +302,29 @@ fn move_to_sets_new_mouse_position() { #[test] fn button_down_sets_button_state_to_down() { let mut mouse = Mouse::new(); - mouse.left.position = ButtonPosition::Up; + mouse.buttons.get_mut(MouseButton::Left).position = ButtonPosition::Up; mouse.button_down(MouseButton::Left); - - let is_down = match mouse.left.position { - ButtonPosition::Down(_) => true, - _ => false - }; - assert!(is_down); - assert!(mouse.left.was_just_pressed); + let button_state = mouse.buttons.get(MouseButton::Left); + assert!(button_state.is_down()); + assert!(button_state.was_just_pressed); } #[test] fn button_up_sets_button_state_to_up() { let mut mouse = Mouse::new(); - mouse.left.position = ButtonPosition::Down(MouseButtonDown{ - time: SteadyTime::now(), - position: [0.0, 0.0] + mouse.buttons.update(MouseButton::Left, |state| { + state.position = ButtonPosition::Down(MouseButtonDown{ + time: SteadyTime::now(), + position: [0.0, 0.0] + }); }); mouse.button_up(MouseButton::Left); - assert_eq!(ButtonPosition::Up, mouse.left.position); - assert!(mouse.left.was_just_released); + + let state = mouse.buttons.get(MouseButton::Left); + assert_eq!(ButtonPosition::Up, state.position); + assert!(state.was_just_released); } #[test] @@ -402,6 +367,19 @@ fn get_simple_event_returns_drag_event_if_mouse_was_dragged_without_releasing_bu assert!(!drag_event.button_released); } +#[test] +fn drag_event_is_not_created_when_mouse_is_dragged_less_than_threshold() { + use input::MouseButton::Left; + let mut mouse = Mouse::new(); + let new_position = [0.0, mouse.drag_distance_threshold - 1.0]; + mouse.button_down(Left); + mouse.move_to(new_position); + // mouse button stays down + + let actual_event = mouse.get_simple_event(); + assert!(actual_event.is_none()); +} + #[test] fn get_simple_event_returns_drag_event_if_mouse_was_dragged_then_button_released() { use input::MouseButton::Left; diff --git a/src/widget/button.rs b/src/widget/button.rs index 345530b7c..67667d40a 100644 --- a/src/widget/button.rs +++ b/src/widget/button.rs @@ -77,8 +77,11 @@ impl Interaction { /// Check the current state of the button. fn get_new_interaction(is_over: bool, prev: Interaction, mouse: Mouse) -> Interaction { use mouse::ButtonPosition::{Down, Up}; + use mouse::MouseButton::Left; use self::Interaction::{Normal, Highlighted, Clicked}; - match (is_over, prev, mouse.left.position) { + + let left_mouse_button = mouse.buttons.get(Left); + match (is_over, prev, left_mouse_button.position) { (true, Normal, Down(_)) => Normal, (true, _, Down(_)) => Clicked, (true, _, Up) => Highlighted, diff --git a/src/widget/drag.rs b/src/widget/drag.rs index 8cc72ed48..f6492269f 100644 --- a/src/widget/drag.rs +++ b/src/widget/drag.rs @@ -23,6 +23,7 @@ pub enum State { pub fn drag_widget(xy: Point, rel_rect: Rect, state: State, mouse: Mouse) -> (Point, State) { use self::State::{Normal, Highlighted, Clicked}; use mouse::ButtonPosition::{Up, Down}; + use mouse::MouseButton::Left as LeftButton; // Find the absolute position of the draggable area. let abs_rect = rel_rect.shift(xy); @@ -31,7 +32,8 @@ pub fn drag_widget(xy: Point, rel_rect: Rect, state: State, mouse: Mouse) -> (Po let is_over = abs_rect.is_over(mouse.xy); // Determine the new drag state. - let new_state = match (is_over, state, mouse.left.position) { + let left_button_position = mouse.buttons.get(LeftButton).position; + let new_state = match (is_over, state, left_button_position) { (true, Normal, Down(_)) => Normal, (true, _, Up) => Highlighted, (true, _, Down(_)) | diff --git a/src/widget/drop_down_list.rs b/src/widget/drop_down_list.rs index 4948cb154..2148dcf61 100644 --- a/src/widget/drop_down_list.rs +++ b/src/widget/drop_down_list.rs @@ -158,6 +158,8 @@ impl<'a, F> Widget for DropDownList<'a, F> where /// Update the state of the DropDownList. fn update(mut self, args: widget::UpdateArgs) { + use mouse::MouseButton; + let widget::UpdateArgs { idx, state, rect, style, mut ui, .. } = args; let (global_mouse, window_dim) = { @@ -280,7 +282,7 @@ impl<'a, F> Widget for DropDownList<'a, F> where MenuState::Closed // Otherwise if the mouse was released somewhere else we should close the menu. - } else if global_mouse.left.was_just_pressed + } else if global_mouse.buttons.get(MouseButton::Left).was_just_pressed && !canvas_rect.is_over(global_mouse.xy) { MenuState::Closed } else { diff --git a/src/widget/envelope_editor.rs b/src/widget/envelope_editor.rs index dd97d4468..97f5865f2 100644 --- a/src/widget/envelope_editor.rs +++ b/src/widget/envelope_editor.rs @@ -29,6 +29,7 @@ use std::default::Default; use std::fmt::Debug; use utils::{clamp, map_range, percentage, val_to_string}; use widget; +use mouse::MouseButton; /// Used for editing a series of 2D Points on a cartesian (X, Y) plane within some given range. @@ -110,14 +111,6 @@ pub enum Elem { // CurvePoint(usize, (f64, f64)), } -/// An enum to define which button is clicked. -#[derive(Debug, PartialEq, Clone, Copy)] -pub enum MouseButton { - Left, - Right, -} - - /// `EnvPoint` must be implemented for any type that is used as a 2D point within the /// EnvelopeEditor. pub trait EnvelopePoint: Any + Clone + Debug + PartialEq { @@ -175,9 +168,12 @@ fn get_new_interaction(is_over_elem: Option, mouse: Mouse) -> Interaction { use mouse::ButtonPosition::{Down, Up}; use self::Elem::{EnvPoint};//, CurvePoint}; - use self::MouseButton::{Left, Right}; + use mouse::MouseButton::{Left, Right}; use self::Interaction::{Normal, Highlighted, Clicked}; - match (is_over_elem, prev, mouse.left.position, mouse.right.position) { + + let left_button_position = mouse.buttons.get(Left).position; + let right_button_position = mouse.buttons.get(Right).position; + match (is_over_elem, prev, left_button_position, right_button_position) { (Some(_), Normal, Down(_), Up) => Normal, (Some(elem), _, Up, Up) => Highlighted(elem), (Some(elem), Highlighted(_), Down(_), Up) => Clicked(elem, Left), @@ -467,6 +463,7 @@ impl<'a, E, F> Widget for EnvelopeEditor<'a, E, F> react(env, idx); } }, + _ => {} } }, (Clicked(_, prev_m_button), Clicked(_, m_button)) => { diff --git a/src/widget/mod.rs b/src/widget/mod.rs index 735eaa3b3..ec40371fe 100644 --- a/src/widget/mod.rs +++ b/src/widget/mod.rs @@ -703,6 +703,8 @@ fn set_widget<'a, C, W>(widget: W, idx: Index, ui: &mut Ui) where C: CharacterCache, W: Widget, { + use mouse::MouseButton; + let kind = widget.unique_kind(); // Take the previous state of the widget from the cache if there is some to collect. @@ -834,7 +836,7 @@ fn set_widget<'a, C, W>(widget: W, idx: Index, ui: &mut Ui) where let maybe_mouse = ui::get_mouse_state(ui, idx); match (prev.maybe_floating, maybe_mouse) { (Some(prev_floating), Some(mouse)) => { - if mouse.left.is_down() { + if mouse.buttons.get(MouseButton::Left).is_down() { Some(new_floating()) } else { Some(prev_floating) diff --git a/src/widget/number_dialer.rs b/src/widget/number_dialer.rs index 47f82fd74..2ad114852 100644 --- a/src/widget/number_dialer.rs +++ b/src/widget/number_dialer.rs @@ -186,9 +186,10 @@ fn is_over(mouse_xy: Point, /// Check and return the current state of the NumberDialer. fn get_new_interaction(is_over_elem: Option, prev: Interaction, mouse: Mouse) -> Interaction { use mouse::ButtonPosition::{Down, Up}; + use mouse::MouseButton::Left as LeftButton; use self::Elem::ValueGlyph; use self::Interaction::{Normal, Highlighted, Clicked}; - match (is_over_elem, prev, mouse.left.position) { + match (is_over_elem, prev, mouse.buttons.get(LeftButton).position) { (Some(_), Normal, Down(_)) => Normal, (Some(elem), _, Up) => Highlighted(elem), (Some(elem), Highlighted(_), Down(_)) => Clicked(elem), diff --git a/src/widget/scroll.rs b/src/widget/scroll.rs index 3e69da840..d2471b697 100644 --- a/src/widget/scroll.rs +++ b/src/widget/scroll.rs @@ -468,6 +468,7 @@ fn new_interaction(bar: &Bar, mouse_scalar: Scalar) -> Interaction { use self::Interaction::{Normal, Highlighted, Clicked}; + use mouse::MouseButton::Left as LeftButton; // If there's no need for a scroll bar, leave the interaction as `Normal`. if bar.scrollable.len() == 0.0 { @@ -475,7 +476,9 @@ fn new_interaction(bar: &Bar, } else { use self::Elem::Handle; use mouse::ButtonPosition::{Down, Up}; - match (is_over_elem, bar.interaction, mouse.left.position) { + + let left_button_position = mouse.buttons.get(LeftButton).position; + match (is_over_elem, bar.interaction, left_button_position) { (Some(_), Normal, Down(_)) => Normal, (Some(elem), _, Up) => Highlighted(elem), (Some(_), Highlighted(_), Down(_)) | diff --git a/src/widget/slider.rs b/src/widget/slider.rs index c909138d2..c464d9294 100644 --- a/src/widget/slider.rs +++ b/src/widget/slider.rs @@ -93,8 +93,10 @@ impl Interaction { /// Check the current state of the slider. fn get_new_interaction(is_over: bool, prev: Interaction, mouse: Mouse) -> Interaction { use mouse::ButtonPosition::{Down, Up}; + use mouse::MouseButton::Left as LeftButton; use self::Interaction::{Normal, Highlighted, Clicked}; - match (is_over, prev, mouse.left.position) { + + match (is_over, prev, mouse.buttons.get(LeftButton).position) { (true, Normal, Down(_)) => Normal, (true, _, Down(_)) => Clicked, (true, _, Up) => Highlighted, diff --git a/src/widget/text_box.rs b/src/widget/text_box.rs index 95b45e6e8..c6b12a245 100644 --- a/src/widget/text_box.rs +++ b/src/widget/text_box.rs @@ -20,6 +20,7 @@ use { Theme, }; +use mouse::MouseButton; use input::keyboard::Key::{Backspace, Left, Right, Return, A, E, LCtrl, RCtrl}; use vecmath::vec2_sub; use widget::{self, Widget, KidArea, UpdateArgs}; @@ -248,8 +249,9 @@ fn get_new_interaction(over_elem: Elem, prev_interaction: Interaction, mouse: Mo use self::Interaction::{Captured, Uncaptured}; use self::Uncaptured::{Normal, Highlighted}; + let left_button_state = mouse.buttons.get(MouseButton::Left); match prev_interaction { - Interaction::Captured(mut prev) => match mouse.left.position { + Interaction::Captured(mut prev) => match left_button_state.position { Down(_) => match over_elem { Elem::Nill => if prev.cursor.anchor == Anchor::None { Uncaptured(Normal) @@ -277,7 +279,7 @@ fn get_new_interaction(over_elem: Elem, prev_interaction: Interaction, mouse: Mo }, }, - Interaction::Uncaptured(prev) => match mouse.left.position { + Interaction::Uncaptured(prev) => match left_button_state.position { Down(_) => match over_elem { Elem::Nill => Uncaptured(Normal), Elem::Rect => match prev { diff --git a/src/widget/title_bar.rs b/src/widget/title_bar.rs index 1bd4e2b7f..9190ee0c9 100644 --- a/src/widget/title_bar.rs +++ b/src/widget/title_bar.rs @@ -82,8 +82,10 @@ impl Style { /// Check the current state of the button. fn get_new_interaction(is_over: bool, prev: Interaction, mouse: Mouse) -> Interaction { use mouse::ButtonPosition::{Down, Up}; + use mouse::MouseButton::Left as LeftButton; use self::Interaction::{Normal, Highlighted, Clicked}; - match (is_over, prev, mouse.left.position) { + + match (is_over, prev, mouse.buttons.get(LeftButton).position) { (true, Normal, Down(_)) => Normal, (true, _, Down(_)) => Clicked, (true, _, Up) => Highlighted, diff --git a/src/widget/toggle.rs b/src/widget/toggle.rs index cd56883a3..fbe72cdcf 100644 --- a/src/widget/toggle.rs +++ b/src/widget/toggle.rs @@ -87,8 +87,10 @@ fn get_new_interaction(is_over: bool, prev: Interaction, mouse: Mouse) -> Interaction { use mouse::ButtonPosition::{Down, Up}; + use mouse::MouseButton::Left as LeftButton; use self::Interaction::{Normal, Highlighted, Clicked}; - match (is_over, prev, mouse.left.position) { + + match (is_over, prev, mouse.buttons.get(LeftButton).position) { (true, Normal, Down(_)) => Normal, (true, _, Down(_)) => Clicked, (true, _, Up) => Highlighted, diff --git a/src/widget/xy_pad.rs b/src/widget/xy_pad.rs index fad58d0e6..888486e2d 100644 --- a/src/widget/xy_pad.rs +++ b/src/widget/xy_pad.rs @@ -96,8 +96,10 @@ fn get_new_interaction(is_over: bool, prev: Interaction, mouse: Mouse) -> Interaction { use mouse::ButtonPosition::{Down, Up}; + use mouse::MouseButton::Left as LeftButton; use self::Interaction::{Normal, Highlighted, Clicked}; - match (is_over, prev, mouse.left.position) { + + match (is_over, prev, mouse.buttons.get(LeftButton).position) { (true, Normal, Down(_)) => Normal, (true, _, Down(_)) => Clicked, (true, _, Up) => Highlighted, From a2b22bcffb8ca3d94cc57932b30bfd0e0c1373b7 Mon Sep 17 00:00:00 2001 From: pfried Date: Tue, 5 Jan 2016 00:14:53 -0500 Subject: [PATCH 15/19] added event iterator to Mouse --- src/mouse/button_map.rs | 58 +++++++++++ src/mouse/mod.rs | 203 +++++++++++++++++++++---------------- src/mouse/simple_events.rs | 2 + src/widget/text_box.rs | 5 +- 4 files changed, 180 insertions(+), 88 deletions(-) diff --git a/src/mouse/button_map.rs b/src/mouse/button_map.rs index 48a496a16..2125aea7f 100644 --- a/src/mouse/button_map.rs +++ b/src/mouse/button_map.rs @@ -1,6 +1,7 @@ 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; @@ -90,11 +91,68 @@ impl ButtonMap { self.button_states.iter_mut() } + /// returns an iterator over immutable references to all the button states pub fn iter(&self) -> Iter { 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::simple_events::*; + use super::simple_events::SimpleMouseEvent::{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); + } +} diff --git a/src/mouse/mod.rs b/src/mouse/mod.rs index 0fff43066..5e3013c5a 100644 --- a/src/mouse/mod.rs +++ b/src/mouse/mod.rs @@ -46,12 +46,17 @@ pub struct Mouse { pub scroll: Scroll, /// Movements less than this threshold will not be considered drags pub drag_distance_threshold: Scalar, - /// Simple mouse event that is waiting to be consumed - pub simple_event: Option, /// Map of MouseButton to ButtonState pub buttons: ButtonMap, + /// Holds a scroll event if there is one + pub maybe_scroll_event: Option, } +/// Iterator over mouse events. +pub struct MouseEventIterator<'a>{ + mouse: &'a Mouse, + idx: u32, +} impl ButtonState { @@ -88,7 +93,6 @@ impl ButtonState { } } - impl Mouse { /// Constructor for a default Mouse struct. @@ -97,51 +101,38 @@ impl Mouse { xy: [0.0, 0.0], scroll: Scroll { x: 0.0, y: 0.0 }, drag_distance_threshold: 2.0, - simple_event: None, + maybe_scroll_event: None, buttons: ButtonMap::new(), } } - /// Return the mouse state with its position relative to the given position. + /// Return the mouse state with all `position::Point`s relative to the given point. pub fn relative_to(self, xy: Point) -> Mouse { use ::vecmath::vec2_sub; - let relative_simple_event = self.simple_event.map(|mouse_event| mouse_event.relative_to(xy)); + let relative_button_map = self.buttons.relative_to(xy); Mouse { xy: vec2_sub(self.xy, xy), - simple_event: relative_simple_event, ..self } + buttons: relative_button_map, ..self } } - /// Call whenever the mouse moves, sets the new position of the mouse + /// Call whenever the mouse moves, sets the new position of the mouse and + /// creates a Drag event if a button is being held down. pub fn move_to(&mut self, xy: Point) { - use self::ButtonPosition::Down; - use self::simple_events::SimpleMouseEvent::Drag; - - let new_event: Option = { - - self.buttons.all_buttons().iter() - // Find the first button that is current in the Down position - .filter(|button_and_state| button_and_state.1.is_down()) - .next().and_then(|button_and_state| { - // Once we have the button down info, map that to a MouseDragEvent - let button_position = button_and_state.1.position; - match button_position { - Down(info) if self.is_drag(info.position, xy) => { - Some(Drag(MouseDragEvent{ - start: info, - current: MouseButtonDown{ - time: SteadyTime::now(), - position: xy - }, - mouse_button: button_and_state.0, - button_released: false - })) - }, - _ => None - } - }) - }; - - self.set_simple_event(new_event); + // Check to see if moving the mouse will create a Drag event. + // Update any events if it does. + let buttons_and_events = self.buttons.all_buttons().iter() + // Find the first button that is current in the Down position + .filter(|button_and_state| button_and_state.1.is_down()) + .map(|button_and_state| { + // Once we have the button down info, map that to a MouseDragEvent + let maybe_event = self.get_drag_event_for_move(button_and_state.0, button_and_state.1, xy); + (button_and_state.0, maybe_event) + }).filter(|button_and_event| button_and_event.1.is_some()) + .collect::)>>(); + + for button_and_event in buttons_and_events { + self.buttons.get_mut(button_and_event.0).event = button_and_event.1; + } // update the current position of the mouse self.xy = xy; @@ -162,42 +153,39 @@ impl Mouse { /// called when a mouse button is released pub fn button_up(&mut self, button: MouseButton) { - let current_mouse_position = self.xy.clone(); - let drag_distance_threshold = self.drag_distance_threshold; - let mut new_simple_event: Option = None; - { - let button_state = self.buttons.get_mut(button); - - if let ButtonPosition::Down(mouse_down_info) = button_state.position { - let drag_distance = distance_between(mouse_down_info.position, current_mouse_position); - if drag_distance > drag_distance_threshold { - new_simple_event = Some(SimpleMouseEvent::Drag(MouseDragEvent{ - mouse_button: button, - start: MouseButtonDown{ time: mouse_down_info.time, position: mouse_down_info.position }, - current: MouseButtonDown{ time: SteadyTime::now(), position: current_mouse_position }, - button_released: true - })); - } else { - new_simple_event = Some(SimpleMouseEvent::Click(MouseClick{ - mouse_button: button, - position: current_mouse_position - })); - } - } - - button_state.position = ButtonPosition::Up; - button_state.was_just_released = true; - } + use self::ButtonPosition::Down; - self.set_simple_event(new_simple_event); + let new_simple_event = match self.buttons.get(button).position { + ButtonPosition::Down(mouse_down_info) if self.is_drag(mouse_down_info.position, self.xy) => { + Some(SimpleMouseEvent::Drag(MouseDragEvent{ + mouse_button: button, + start: MouseButtonDown{ time: mouse_down_info.time, position: mouse_down_info.position }, + current: MouseButtonDown{ time: SteadyTime::now(), position: self.xy }, + button_released: true + })) + }, + ButtonPosition::Down(_) => { + Some(SimpleMouseEvent::Click(MouseClick{ + mouse_button: button, + position: self.xy + })) + }, + _ => None + }; + + self.buttons.update(button, |state| { + state.position = ButtonPosition::Up; + state.was_just_released = true; + state.event = new_simple_event; + }); } /// called when the mouse wheel is scrolled pub fn scroll(&mut self, x: f64, y: f64) { - self.set_simple_event(Some(SimpleMouseEvent::Scroll(Scroll{ + self.maybe_scroll_event = Some(SimpleMouseEvent::Scroll(Scroll{ x: x, y: y - }))); + })); } /// resets the state of the mouse @@ -206,23 +194,39 @@ impl Mouse { button_state.reset_pressed_and_released(); button_state.event = None; } - self.simple_event = None; + self.maybe_scroll_event = None; self.scroll.x = 0.0; self.scroll.y = 0.0; } - /// Returns the current `SimpleMouseEvent` if there is one - pub fn get_simple_event(&self) -> Option { - self.simple_event + /// Returns any mouse events since the last update. + pub fn events(&self) -> MouseEventIterator { + MouseEventIterator::new(self) } fn is_drag(&self, start_position: Point, end_position: Point) -> bool { distance_between(start_position, end_position) >= self.drag_distance_threshold } - fn set_simple_event(&mut self, event: Option) { - if event.is_some() { - self.simple_event = event; + fn get_drag_event_for_move(&self, button: MouseButton, + current_button_state: &ButtonState, + current_xy: Point) -> Option { + use self::ButtonPosition::Down; + use self::simple_events::SimpleMouseEvent::Drag; + + match current_button_state.position { + Down(info) if self.is_drag(info.position, current_xy) => { + Some(Drag(MouseDragEvent{ + start: info, + current: MouseButtonDown{ + time: SteadyTime::now(), + position: current_xy + }, + mouse_button: button, + button_released: false + })) + }, + _ => None } } @@ -234,6 +238,32 @@ fn distance_between(a: Point, b: Point) -> Scalar { (dx_2 + dy_2).abs().sqrt() } +impl<'a> ::std::iter::Iterator for MouseEventIterator<'a> { + type Item = SimpleMouseEvent; + + fn next(&mut self) -> Option { + let mut evt: Option = None; + while (self.idx as usize) < NUM_MOUSE_BUTTONS && evt.is_none() { + let button_state = self.mouse.buttons.get(MouseButton::from(self.idx)); + evt = button_state.event; + self.idx += 1; + } + if (self.idx as usize) == NUM_MOUSE_BUTTONS && evt.is_none() { + evt = self.mouse.maybe_scroll_event; + } + evt + } +} + +impl<'a> MouseEventIterator<'a> { + fn new(mouse: &Mouse) -> MouseEventIterator { + MouseEventIterator{ + mouse: mouse, + idx: 0 + } + } +} + #[test] fn reset_should_reset_all_button_states_and_scroll_state() { let mut mouse = Mouse::new(); @@ -243,7 +273,7 @@ fn reset_should_reset_all_button_states_and_scroll_state() { } mouse.scroll.x = 10.0; mouse.scroll.y = 20.0; - mouse.simple_event = Some(SimpleMouseEvent::Scroll(Scroll{x: 2.0, y: 33.3})); + mouse.maybe_scroll_event = Some(SimpleMouseEvent::Scroll(Scroll{x: 2.0, y: 33.3})); mouse.reset(); @@ -253,15 +283,16 @@ fn reset_should_reset_all_button_states_and_scroll_state() { } assert_eq!(0.0, mouse.scroll.x); assert_eq!(0.0, mouse.scroll.y); - assert!(mouse.simple_event.is_none()); + assert!(mouse.maybe_scroll_event.is_none()); + assert_eq!(0, mouse.events().count()); } #[test] -fn scroll_should_create_a_simple_mouse_event_of_scroll() { +fn scroll_should_create_a_new_scroll_event() { let mut mouse = Mouse::new(); mouse.scroll(2.0, 3.0); - match mouse.get_simple_event() { + match mouse.maybe_scroll_event { Some(SimpleMouseEvent::Scroll(scroll_info)) => { assert_eq!(2.0, scroll_info.x); assert_eq!(3.0, scroll_info.y); @@ -328,7 +359,7 @@ fn button_up_sets_button_state_to_up() { } #[test] -fn get_simple_event_returns_click_if_button_goes_down_then_up_in_same_position() { +fn events_returns_click_if_button_goes_down_then_up_in_same_position() { use input::MouseButton::Right; let mut mouse = Mouse::new(); mouse.button_down(Right); @@ -338,13 +369,13 @@ fn get_simple_event_returns_click_if_button_goes_down_then_up_in_same_position() position: mouse.xy.clone() }); - let actual_event = mouse.get_simple_event(); + let actual_event = mouse.events().next(); assert!(actual_event.is_some()); assert_eq!(expected_event, actual_event.unwrap()); } #[test] -fn get_simple_event_returns_drag_event_if_mouse_was_dragged_without_releasing_button() { +fn events_returns_drag_event_if_mouse_was_dragged_without_releasing_button() { use input::MouseButton::Left; let mut mouse = Mouse::new(); let start_position = mouse.xy; @@ -352,7 +383,7 @@ fn get_simple_event_returns_drag_event_if_mouse_was_dragged_without_releasing_bu mouse.button_down(Left); mouse.move_to(new_position); - let actual_event = mouse.get_simple_event(); + let actual_event = mouse.events().next(); assert!(actual_event.is_some()); let maybe_drag_event: Option = match actual_event { @@ -376,12 +407,12 @@ fn drag_event_is_not_created_when_mouse_is_dragged_less_than_threshold() { mouse.move_to(new_position); // mouse button stays down - let actual_event = mouse.get_simple_event(); + let actual_event = mouse.events().next(); assert!(actual_event.is_none()); } #[test] -fn get_simple_event_returns_drag_event_if_mouse_was_dragged_then_button_released() { +fn events_returns_drag_event_if_mouse_was_dragged_then_button_released() { use input::MouseButton::Left; let mut mouse = Mouse::new(); let start_position = mouse.xy; @@ -390,7 +421,7 @@ fn get_simple_event_returns_drag_event_if_mouse_was_dragged_then_button_released mouse.move_to(new_position); mouse.button_up(Left); - let actual_event = mouse.get_simple_event(); + let actual_event = mouse.events().next(); assert!(actual_event.is_some()); let maybe_drag_event: Option = match actual_event { @@ -415,7 +446,7 @@ fn drag_event_has_original_start_position_after_multiple_mouse_move_events() { mouse.move_to([-5.0, 10.0]); mouse.move_to(final_position); - let actual_event = mouse.get_simple_event(); + let actual_event = mouse.events().next(); assert!(actual_event.is_some()); let maybe_drag_event: Option = match actual_event { @@ -438,7 +469,7 @@ fn get_simple_event_returns_click_event_if_mouse_was_dragged_less_than_drag_thre mouse.move_to(new_position); mouse.button_up(Left); - let actual_event = mouse.get_simple_event(); + let actual_event = mouse.events().next(); assert!(actual_event.is_some()); let maybe_click_event: Option = match actual_event { diff --git a/src/mouse/simple_events.rs b/src/mouse/simple_events.rs index 118ba65a3..c8adbd95b 100644 --- a/src/mouse/simple_events.rs +++ b/src/mouse/simple_events.rs @@ -9,6 +9,8 @@ use time::SteadyTime; #[cfg(test)] use position::Scalar; +// pub type MouseEventIterator = (); + /// Used for simplified mouse event handling. Most widgets can probably /// just use these events #[derive(Copy, Clone, Debug, PartialEq)] diff --git a/src/widget/text_box.rs b/src/widget/text_box.rs index c6b12a245..5164784b1 100644 --- a/src/widget/text_box.rs +++ b/src/widget/text_box.rs @@ -425,12 +425,13 @@ fn get_highlight_all_interaction(text: &str) -> Interaction { fn get_new_interaction_2(text: &str, args: &UpdateArgs, C>) -> Interaction where C: CharacterCache, F: FnMut(&mut String) { + use ::mouse::MouseButton; + let maybe_mouse = args.ui.input().maybe_mouse; - let maybe_mouse_event = maybe_mouse.and_then(|mouse| mouse.get_simple_event()); + let maybe_mouse_event = maybe_mouse.and_then(|mouse| mouse.buttons.get(MouseButton::Left).event); maybe_mouse_event.and_then(|event| { use ::mouse::simple_events::SimpleMouseEvent::{Click, Drag}; - use ::mouse::MouseButton; match event { Click(click_info) if click_info.mouse_button == MouseButton::Left => { let clicked_elem = get_clicked_elem(text, click_info.position, args); From 920ebc5cb1550180cccc60750d162e1582158ded Mon Sep 17 00:00:00 2001 From: pfried Date: Tue, 5 Jan 2016 19:53:51 -0500 Subject: [PATCH 16/19] renamed SimpleMouseEvent to MouseEvent and simple_events to events --- src/lib.rs | 4 +- src/mouse/button_map.rs | 4 +- src/mouse/{simple_events.rs => events.rs} | 6 +-- src/mouse/mod.rs | 46 +++++++++++------------ src/widget/text_box.rs | 2 +- 5 files changed, 31 insertions(+), 31 deletions(-) rename src/mouse/{simple_events.rs => events.rs} (98%) diff --git a/src/lib.rs b/src/lib.rs index ce17f499d..75570450a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,8 +70,8 @@ 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::simple_events::Scroll as MouseScroll; -pub use mouse::simple_events as simple_mouse_events; +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; diff --git a/src/mouse/button_map.rs b/src/mouse/button_map.rs index 2125aea7f..8ae7da893 100644 --- a/src/mouse/button_map.rs +++ b/src/mouse/button_map.rs @@ -114,8 +114,8 @@ impl ButtonMap { #[test] fn relative_to_changes_all_mouse_events_to_relative_positions() { use input::MouseButton::*; - use super::simple_events::*; - use super::simple_events::SimpleMouseEvent::{Click, Drag}; + use super::events::*; + use super::events::MouseEvent::{Click, Drag}; use time::SteadyTime; let mut map_a = ButtonMap::new(); diff --git a/src/mouse/simple_events.rs b/src/mouse/events.rs similarity index 98% rename from src/mouse/simple_events.rs rename to src/mouse/events.rs index c8adbd95b..841c22012 100644 --- a/src/mouse/simple_events.rs +++ b/src/mouse/events.rs @@ -14,7 +14,7 @@ use position::Scalar; /// Used for simplified mouse event handling. Most widgets can probably /// just use these events #[derive(Copy, Clone, Debug, PartialEq)] -pub enum SimpleMouseEvent { +pub enum MouseEvent { /// Indicates that the mouse was clicked. A Click event is created when the mouse button is released, not depressed Click(MouseClick), /// Drag event is created when the mouse was moved over a certain threshold while a button was depressed @@ -95,11 +95,11 @@ impl MouseDragEvent { } } -impl SimpleMouseEvent { +impl MouseEvent { /// Returns a new copy of the event data relative to the given point pub fn relative_to(&self, xy: Point) -> Self { - use self::SimpleMouseEvent::*; + use self::MouseEvent::*; match self { &Click(mouse_click) => Click(mouse_click.relative_to(xy)), diff --git a/src/mouse/mod.rs b/src/mouse/mod.rs index 5e3013c5a..aaa678a30 100644 --- a/src/mouse/mod.rs +++ b/src/mouse/mod.rs @@ -4,14 +4,14 @@ //! The `Ui` will continuously maintain the latest Mouse state, necessary for widget logic. //! -pub mod simple_events; +pub mod events; mod button_map; pub use self::button_map::{ButtonMap, NUM_MOUSE_BUTTONS}; pub use input::MouseButton; use graphics::math::Scalar; -use self::simple_events::*; +use self::events::*; use position::Point; use time::{SteadyTime}; @@ -24,8 +24,8 @@ pub struct ButtonState { pub was_just_released: bool, /// The current position of the button. pub position: ButtonPosition, - /// SimpleMouseEvent for events corresponding to this button. - pub event: Option, + /// MouseEvent for events corresponding to this button. + pub event: Option, } /// Represents the current state of a mouse button. @@ -49,7 +49,7 @@ pub struct Mouse { /// Map of MouseButton to ButtonState pub buttons: ButtonMap, /// Holds a scroll event if there is one - pub maybe_scroll_event: Option, + pub maybe_scroll_event: Option, } /// Iterator over mouse events. @@ -128,7 +128,7 @@ impl Mouse { let maybe_event = self.get_drag_event_for_move(button_and_state.0, button_and_state.1, xy); (button_and_state.0, maybe_event) }).filter(|button_and_event| button_and_event.1.is_some()) - .collect::)>>(); + .collect::)>>(); for button_and_event in buttons_and_events { self.buttons.get_mut(button_and_event.0).event = button_and_event.1; @@ -157,7 +157,7 @@ impl Mouse { let new_simple_event = match self.buttons.get(button).position { ButtonPosition::Down(mouse_down_info) if self.is_drag(mouse_down_info.position, self.xy) => { - Some(SimpleMouseEvent::Drag(MouseDragEvent{ + Some(MouseEvent::Drag(MouseDragEvent{ mouse_button: button, start: MouseButtonDown{ time: mouse_down_info.time, position: mouse_down_info.position }, current: MouseButtonDown{ time: SteadyTime::now(), position: self.xy }, @@ -165,7 +165,7 @@ impl Mouse { })) }, ButtonPosition::Down(_) => { - Some(SimpleMouseEvent::Click(MouseClick{ + Some(MouseEvent::Click(MouseClick{ mouse_button: button, position: self.xy })) @@ -182,7 +182,7 @@ impl Mouse { /// called when the mouse wheel is scrolled pub fn scroll(&mut self, x: f64, y: f64) { - self.maybe_scroll_event = Some(SimpleMouseEvent::Scroll(Scroll{ + self.maybe_scroll_event = Some(MouseEvent::Scroll(Scroll{ x: x, y: y })); @@ -210,9 +210,9 @@ impl Mouse { fn get_drag_event_for_move(&self, button: MouseButton, current_button_state: &ButtonState, - current_xy: Point) -> Option { + current_xy: Point) -> Option { use self::ButtonPosition::Down; - use self::simple_events::SimpleMouseEvent::Drag; + use self::events::MouseEvent::Drag; match current_button_state.position { Down(info) if self.is_drag(info.position, current_xy) => { @@ -239,10 +239,10 @@ fn distance_between(a: Point, b: Point) -> Scalar { } impl<'a> ::std::iter::Iterator for MouseEventIterator<'a> { - type Item = SimpleMouseEvent; + type Item = MouseEvent; - fn next(&mut self) -> Option { - let mut evt: Option = None; + fn next(&mut self) -> Option { + let mut evt: Option = None; while (self.idx as usize) < NUM_MOUSE_BUTTONS && evt.is_none() { let button_state = self.mouse.buttons.get(MouseButton::from(self.idx)); evt = button_state.event; @@ -273,7 +273,7 @@ fn reset_should_reset_all_button_states_and_scroll_state() { } mouse.scroll.x = 10.0; mouse.scroll.y = 20.0; - mouse.maybe_scroll_event = Some(SimpleMouseEvent::Scroll(Scroll{x: 2.0, y: 33.3})); + mouse.maybe_scroll_event = Some(MouseEvent::Scroll(Scroll{x: 2.0, y: 33.3})); mouse.reset(); @@ -293,12 +293,12 @@ fn scroll_should_create_a_new_scroll_event() { mouse.scroll(2.0, 3.0); match mouse.maybe_scroll_event { - Some(SimpleMouseEvent::Scroll(scroll_info)) => { + Some(MouseEvent::Scroll(scroll_info)) => { assert_eq!(2.0, scroll_info.x); assert_eq!(3.0, scroll_info.y); }, - Some(thing) => panic!("Expected a SimpleMouseEvent::Scroll, instead got: {:?}", thing), - _ => panic!("expected a SimpleMouseEvent::Scroll, instead got None") + Some(thing) => panic!("Expected a MouseEvent::Scroll, instead got: {:?}", thing), + _ => panic!("expected a MouseEvent::Scroll, instead got None") } } @@ -364,7 +364,7 @@ fn events_returns_click_if_button_goes_down_then_up_in_same_position() { let mut mouse = Mouse::new(); mouse.button_down(Right); mouse.button_up(Right); - let expected_event = SimpleMouseEvent::Click(MouseClick{ + let expected_event = MouseEvent::Click(MouseClick{ mouse_button: Right, position: mouse.xy.clone() }); @@ -387,7 +387,7 @@ fn events_returns_drag_event_if_mouse_was_dragged_without_releasing_button() { assert!(actual_event.is_some()); let maybe_drag_event: Option = match actual_event { - Some(SimpleMouseEvent::Drag(mouse_drag_info)) => Some(mouse_drag_info), + Some(MouseEvent::Drag(mouse_drag_info)) => Some(mouse_drag_info), _ => None }; assert!(maybe_drag_event.is_some()); @@ -425,7 +425,7 @@ fn events_returns_drag_event_if_mouse_was_dragged_then_button_released() { assert!(actual_event.is_some()); let maybe_drag_event: Option = match actual_event { - Some(SimpleMouseEvent::Drag(mouse_drag_info)) => Some(mouse_drag_info), + Some(MouseEvent::Drag(mouse_drag_info)) => Some(mouse_drag_info), _ => None }; assert!(maybe_drag_event.is_some()); @@ -450,7 +450,7 @@ fn drag_event_has_original_start_position_after_multiple_mouse_move_events() { assert!(actual_event.is_some()); let maybe_drag_event: Option = match actual_event { - Some(SimpleMouseEvent::Drag(mouse_drag_info)) => Some(mouse_drag_info), + Some(MouseEvent::Drag(mouse_drag_info)) => Some(mouse_drag_info), _ => None }; assert!(maybe_drag_event.is_some()); @@ -473,7 +473,7 @@ fn get_simple_event_returns_click_event_if_mouse_was_dragged_less_than_drag_thre assert!(actual_event.is_some()); let maybe_click_event: Option = match actual_event { - Some(SimpleMouseEvent::Click(mouse_click)) => Some(mouse_click), + Some(MouseEvent::Click(mouse_click)) => Some(mouse_click), _ => None }; assert!(maybe_click_event.is_some()); diff --git a/src/widget/text_box.rs b/src/widget/text_box.rs index 5164784b1..b837237a7 100644 --- a/src/widget/text_box.rs +++ b/src/widget/text_box.rs @@ -431,7 +431,7 @@ fn get_new_interaction_2(text: &str, args: &UpdateArgs, C>) -> let maybe_mouse_event = maybe_mouse.and_then(|mouse| mouse.buttons.get(MouseButton::Left).event); maybe_mouse_event.and_then(|event| { - use ::mouse::simple_events::SimpleMouseEvent::{Click, Drag}; + use ::mouse::events::MouseEvent::{Click, Drag}; match event { Click(click_info) if click_info.mouse_button == MouseButton::Left => { let clicked_elem = get_clicked_elem(text, click_info.position, args); From f8fa6d59a52bffc31a91158607be384633c81a17 Mon Sep 17 00:00:00 2001 From: pfried Date: Tue, 5 Jan 2016 21:17:55 -0500 Subject: [PATCH 17/19] fixed highlight issue when text box takes focus --- src/widget/text_box.rs | 143 ++++++++++------------------------------- 1 file changed, 34 insertions(+), 109 deletions(-) diff --git a/src/widget/text_box.rs b/src/widget/text_box.rs index b837237a7..90e0c18ac 100644 --- a/src/widget/text_box.rs +++ b/src/widget/text_box.rs @@ -9,7 +9,6 @@ use { GlyphCache, IndexSlot, Line, - Mouse, Padding, Point, Positionable, @@ -20,7 +19,6 @@ use { Theme, }; -use mouse::MouseButton; use input::keyboard::Key::{Backspace, Left, Right, Return, A, E, LCtrl, RCtrl}; use vecmath::vec2_sub; use widget::{self, Widget, KidArea, UpdateArgs}; @@ -178,8 +176,6 @@ impl Cursor { } } -// widget_fns!(TextBox, State, Kind::TextBox(State::Uncaptured(Uncaptured::Normal))); - /// Find the position of a character in a text box. fn cursor_position(glyph_cache: &GlyphCache, idx: usize, @@ -243,71 +239,6 @@ fn closest_idx(glyph_cache: &GlyphCache, (text.chars().count(), text_start_x + text_w) } -/// Check and return the current state of the TextBox. -fn get_new_interaction(over_elem: Elem, prev_interaction: Interaction, mouse: Mouse) -> Interaction { - use mouse::ButtonPosition::{Down, Up}; - use self::Interaction::{Captured, Uncaptured}; - use self::Uncaptured::{Normal, Highlighted}; - - let left_button_state = mouse.buttons.get(MouseButton::Left); - match prev_interaction { - Interaction::Captured(mut prev) => match left_button_state.position { - Down(_) => match over_elem { - Elem::Nill => if prev.cursor.anchor == Anchor::None { - Uncaptured(Normal) - } else { - prev_interaction - }, - Elem::Rect => if prev.cursor.anchor == Anchor::None { - prev.cursor = Cursor::from_index(0); - Captured(prev) - } else { - prev_interaction - }, - Elem::Char(idx) => { - match prev.cursor.anchor { - Anchor::None => prev.cursor = Cursor::from_index(idx), - Anchor::Start => prev.cursor = Cursor::from_range(prev.cursor.start, idx), - Anchor::End => prev.cursor = Cursor::from_range(prev.cursor.end, idx), - } - Captured(prev) - }, - }, - Up => { - prev.cursor.anchor = Anchor::None; - Captured(prev) - }, - }, - - Interaction::Uncaptured(prev) => match left_button_state.position { - Down(_) => match over_elem { - Elem::Nill => Uncaptured(Normal), - Elem::Rect => match prev { - Normal => prev_interaction, - Highlighted => Captured(View { - cursor: Cursor::from_index(0), - offset: 0.0, - }) - }, - Elem::Char(idx) => match prev { - Normal => prev_interaction, - Highlighted => Captured(View { - cursor: Cursor::from_index(idx), - offset: 0.0, - }) - }, - }, - Up => match over_elem { - Elem::Nill => Uncaptured(Normal), - Elem::Char(_) | Elem::Rect => match prev { - Normal => Uncaptured(Highlighted), - Highlighted => prev_interaction, - }, - }, - }, - } -} - impl<'a, F> TextBox<'a, F> { /// Construct a TextBox widget. @@ -340,8 +271,6 @@ impl<'a, F> TextBox<'a, F> { self } - - } fn get_clicked_elem(text: &str, point: Point, args: &UpdateArgs, C>) -> Elem @@ -350,7 +279,7 @@ fn get_clicked_elem(text: &str, point: Point, args: &UpdateArgs let style = args.style; let theme = args.ui.theme(); let font_size = style.font_size(theme); - let (xy, widget_dimension) = args.rect.xy_dim(); + let (_xy, widget_dimension) = args.rect.xy_dim(); let inner_dimension = get_inner_dimensions(widget_dimension, style, theme); let glyph_cache = args.ui.glyph_cache(); let text_w = glyph_cache.width(font_size, text); @@ -422,39 +351,42 @@ fn get_highlight_all_interaction(text: &str) -> Interaction { }) } -fn get_new_interaction_2(text: &str, args: &UpdateArgs, C>) -> Interaction +fn get_new_interaction(text: &str, args: &UpdateArgs, C>) -> Interaction where C: CharacterCache, F: FnMut(&mut String) { use ::mouse::MouseButton; let maybe_mouse = args.ui.input().maybe_mouse; - let maybe_mouse_event = maybe_mouse.and_then(|mouse| mouse.buttons.get(MouseButton::Left).event); - - maybe_mouse_event.and_then(|event| { - use ::mouse::events::MouseEvent::{Click, Drag}; - match event { - Click(click_info) if click_info.mouse_button == MouseButton::Left => { - let clicked_elem = get_clicked_elem(text, click_info.position, args); - Some(get_interaction_for_click(text, clicked_elem)) - }, - Drag(drag_info) => { - let drag_start = get_elem_for_drag(text, drag_info.start.position, args); - let drag_end = get_elem_for_drag(text, drag_info.current.position, args); - Some(get_interaction_for_drag(drag_start, drag_end)) - }, - _ => None - } - }).unwrap_or_else(|| { - let prev_interaction = args.state.view().interaction; - let is_capturing_keyboard = args.ui.is_capturing_keyboard(); - match prev_interaction { - Interaction::Captured(_) if is_capturing_keyboard => - prev_interaction, - _ if is_capturing_keyboard => get_highlight_all_interaction(text), - _ if maybe_mouse.is_some() => Interaction::Uncaptured(Uncaptured::Highlighted), - _ => Interaction::Uncaptured(Uncaptured::Normal) - } - }) + maybe_mouse.map(|m| m.relative_to(args.rect.xy())).iter() + .flat_map(|mouse| mouse.events()) + // map MouseEvent to an Option + .filter_map(|event| { + use ::mouse::events::MouseEvent::{Click, Drag}; + match event { + Click(click_info) if click_info.mouse_button == MouseButton::Left => { + let clicked_elem = get_clicked_elem(text, click_info.position, args); + Some(get_interaction_for_click(text, clicked_elem)) + }, + Drag(drag_info) if drag_info.mouse_button == MouseButton::Left => { + let drag_start = get_elem_for_drag(text, drag_info.start.position, args); + let drag_end = get_elem_for_drag(text, drag_info.current.position, args); + Some(get_interaction_for_drag(drag_start, drag_end)) + }, + _ => None + } + }).next() + .unwrap_or_else(|| { + // If there was no interaction from a new mouse event, then we check previous interaction + let prev_interaction = args.state.view().interaction; + let is_capturing_keyboard = args.ui.is_capturing_keyboard(); + match prev_interaction { + Interaction::Captured(_) if is_capturing_keyboard => prev_interaction, + _ if is_capturing_keyboard && maybe_mouse.is_none() => + get_highlight_all_interaction(text), + _ if maybe_mouse.is_some() => Interaction::Uncaptured(Uncaptured::Highlighted), + _ => Interaction::Uncaptured(Uncaptured::Normal) + } + }) } fn get_inner_dimensions(outer_rect: Dimensions, style: &Style, theme: &Theme) -> Dimensions { @@ -507,10 +439,10 @@ impl<'a, F> Widget for TextBox<'a, F> where F: FnMut(&mut String) { /// Update the state of the TextBox. fn update(mut self, args: widget::UpdateArgs) { - let mut new_interaction = get_new_interaction_2(&self.text, &args); + let mut new_interaction = get_new_interaction(&self.text, &args); let widget::UpdateArgs { idx, state, rect, style, mut ui, .. } = args; - let (xy, dim) = rect.xy_dim(); + let dim = rect.dim(); let frame = style.frame(ui.theme()); let inner_rect = rect.pad(frame); let font_size = style.font_size(ui.theme()); @@ -640,13 +572,6 @@ impl<'a, F> Widget for TextBox<'a, F> where F: FnMut(&mut String) { new_interaction = Interaction::Captured(View { cursor: cursor, .. captured }); } - // Check the interactions to determine whether we need to capture or uncapture the keyboard. - match (state.view().interaction, new_interaction) { - (Interaction::Uncaptured(_), Interaction::Captured(_)) => { ui.capture_keyboard(); }, - (Interaction::Captured(_), Interaction::Uncaptured(_)) => { ui.uncapture_keyboard(); }, - _ => (), - } - if state.view().interaction != new_interaction { state.update(|state| state.interaction = new_interaction); } From 7f3f7f3edca1cdc3334dc36a91ac2766629b96c9 Mon Sep 17 00:00:00 2001 From: pfried Date: Tue, 5 Jan 2016 21:36:52 -0500 Subject: [PATCH 18/19] added a mouse button down event --- src/mouse/events.rs | 4 ++++ src/mouse/mod.rs | 26 ++++++++++++++++++++++---- src/ui.rs | 5 +++-- src/widget/mod.rs | 2 ++ 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/mouse/events.rs b/src/mouse/events.rs index 841c22012..d41698e7a 100644 --- a/src/mouse/events.rs +++ b/src/mouse/events.rs @@ -15,6 +15,9 @@ use position::Scalar; /// just use these events #[derive(Copy, Clone, Debug, PartialEq)] pub enum MouseEvent { + /// When the mouse button has been pressed but not yet released or dragged. + /// This event is created as soon as the button is pressed. + Down(MouseButtonDown), /// Indicates that the mouse was clicked. A Click event is created when the mouse button is released, not depressed Click(MouseClick), /// Drag event is created when the mouse was moved over a certain threshold while a button was depressed @@ -102,6 +105,7 @@ impl MouseEvent { use self::MouseEvent::*; match self { + &Down(button_down) => Down(button_down.relative_to(xy)), &Click(mouse_click) => Click(mouse_click.relative_to(xy)), &Drag(mouse_drag) => Drag(mouse_drag.relative_to(xy)), &Scroll(scroll_info) => Scroll(scroll_info) diff --git a/src/mouse/mod.rs b/src/mouse/mod.rs index aaa678a30..9a360dfda 100644 --- a/src/mouse/mod.rs +++ b/src/mouse/mod.rs @@ -143,11 +143,13 @@ impl Mouse { let mouse_position = self.xy.clone(); self.buttons.update(button, |state| { - state.position = ButtonPosition::Down(MouseButtonDown{ + let button_down = MouseButtonDown{ time: SteadyTime::now(), position: mouse_position - }); + }; + state.position = ButtonPosition::Down(button_down); state.was_just_pressed = true; + state.event = Some(MouseEvent::Down(button_down)) }); } @@ -341,6 +343,20 @@ fn button_down_sets_button_state_to_down() { assert!(button_state.was_just_pressed); } +#[test] +fn button_down_creates_a_button_down_event() { + let mut mouse = Mouse::new(); + mouse.buttons.get_mut(MouseButton::Left).position = ButtonPosition::Up; + + mouse.button_down(MouseButton::Left); + let button_state = mouse.buttons.get(MouseButton::Left); + + match button_state.event { + Some(MouseEvent::Down(_)) => {}, + _ => panic!("Expected a button down event") + } +} + #[test] fn button_up_sets_button_state_to_up() { let mut mouse = Mouse::new(); @@ -408,7 +424,9 @@ fn drag_event_is_not_created_when_mouse_is_dragged_less_than_threshold() { // mouse button stays down let actual_event = mouse.events().next(); - assert!(actual_event.is_none()); + if let Some(MouseEvent::Drag(_)) = actual_event { + panic!("Should not have gotten a drag event"); + } } #[test] @@ -461,7 +479,7 @@ fn drag_event_has_original_start_position_after_multiple_mouse_move_events() { } #[test] -fn get_simple_event_returns_click_event_if_mouse_was_dragged_less_than_drag_threshold() { +fn events_returns_click_event_if_mouse_was_dragged_less_than_drag_threshold() { use input::MouseButton::Left; let mut mouse = Mouse::new(); let new_position = [0.0, mouse.drag_distance_threshold - 1.0]; diff --git a/src/ui.rs b/src/ui.rs index 8f9908874..57f293d6b 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -4,7 +4,7 @@ use backend::graphics::{Context, Graphics}; use color::Color; use glyph_cache::GlyphCache; use graph::{self, Graph, NodeIndex}; -use mouse::{self, Mouse}; +use mouse::Mouse; use input; use input::{ GenericEvent, @@ -20,7 +20,6 @@ use std::collections::HashSet; use std::io::Write; use theme::Theme; use widget::{self, Widget}; -use time::SteadyTime; /// Indicates whether or not the Mouse has been captured by a widget. @@ -184,10 +183,12 @@ impl Ui { self.rect_of(idx).map(|rect| rect.w()) } + /// Returns true if the given widget is currently capturing the keyboard pub fn is_capturing_keyboard(&self, id: widget::Index) -> bool { self.maybe_captured_keyboard == Some(Capturing::Captured(id)) } + /// Returns true if the given widget is currently capturing the mouse pub fn is_capturing_mouse(&self, id: widget::Index) -> bool { self.maybe_captured_mouse == Some(Capturing::Captured(id)) } diff --git a/src/widget/mod.rs b/src/widget/mod.rs index ec40371fe..60802eef4 100644 --- a/src/widget/mod.rs +++ b/src/widget/mod.rs @@ -1058,10 +1058,12 @@ impl<'a, C> UiCell<'a, C> { ui::keyboard_uncaptured_by(self.ui, self.idx) } + /// Returns true if the widget is currently capturing the keyboard pub fn is_capturing_keyboard(&self) -> bool { self.ui.is_capturing_keyboard(self.idx) } + /// Returns true if the widget is currently capturing the mouse pub fn is_capturing_mouse(&self) -> bool { self.ui.is_capturing_mouse(self.idx) } From e8ecf34969b9bae88cd2da28f7f8a55f904b4771 Mon Sep 17 00:00:00 2001 From: pfried Date: Thu, 7 Jan 2016 22:43:18 -0500 Subject: [PATCH 19/19] changed mouse down event to use a new struct instead of repurposing mouseButtonDown, handle mouse button down in text box --- src/mouse/events.rs | 28 +++++++++++++++++++++++----- src/mouse/mod.rs | 15 ++++++++++----- src/widget/text_box.rs | 6 +++++- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/mouse/events.rs b/src/mouse/events.rs index d41698e7a..a76cb10f5 100644 --- a/src/mouse/events.rs +++ b/src/mouse/events.rs @@ -5,6 +5,7 @@ use input::MouseButton; use position::Point; use time::SteadyTime; +use ::vecmath::vec2_sub; #[cfg(test)] use position::Scalar; @@ -17,7 +18,7 @@ use position::Scalar; pub enum MouseEvent { /// When the mouse button has been pressed but not yet released or dragged. /// This event is created as soon as the button is pressed. - Down(MouseButtonDown), + Down(ButtonDownEvent), /// Indicates that the mouse was clicked. A Click event is created when the mouse button is released, not depressed Click(MouseClick), /// Drag event is created when the mouse was moved over a certain threshold while a button was depressed @@ -54,6 +55,16 @@ pub struct MouseDragEvent { pub button_released: bool } +/// Event that is created when a mouse button if first pressed (but not yet released) +/// a ButtonDownEvent will always precipitate either a Click or a Drag event. +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct ButtonDownEvent { + /// The mouse button that was pressed + pub mouse_button: MouseButton, + /// The position of the mouse when the button was pressed + pub position: Point, +} + /// Holds info on when a mouse button was depressed or released. #[derive(Copy, Clone, Debug, PartialEq)] pub struct MouseButtonDown { @@ -77,8 +88,6 @@ impl MouseClick { /// Returns a new copy of the event data relative to the given point pub fn relative_to(&self, xy: Point) -> MouseClick { - use ::vecmath::vec2_sub; - MouseClick{ position: vec2_sub(self.position, xy), ..*self @@ -98,6 +107,17 @@ impl MouseDragEvent { } } +impl ButtonDownEvent { + + /// Returns a new copy of the event data relative to the given point + pub fn relative_to(&self, xy: Point) -> ButtonDownEvent { + ButtonDownEvent { + position: vec2_sub(self.position, xy), + ..*self + } + } +} + impl MouseEvent { /// Returns a new copy of the event data relative to the given point @@ -117,8 +137,6 @@ impl MouseButtonDown { /// Returns a new copy of the event data relative to the given point pub fn relative_to(&self, xy: Point) -> MouseButtonDown { - use ::vecmath::vec2_sub; - MouseButtonDown{ position: vec2_sub(self.position, xy), ..*self diff --git a/src/mouse/mod.rs b/src/mouse/mod.rs index 9a360dfda..f403aebb8 100644 --- a/src/mouse/mod.rs +++ b/src/mouse/mod.rs @@ -143,13 +143,15 @@ impl Mouse { let mouse_position = self.xy.clone(); self.buttons.update(button, |state| { - let button_down = MouseButtonDown{ + state.position = ButtonPosition::Down(MouseButtonDown{ time: SteadyTime::now(), position: mouse_position - }; - state.position = ButtonPosition::Down(button_down); + }); state.was_just_pressed = true; - state.event = Some(MouseEvent::Down(button_down)) + state.event = Some(MouseEvent::Down(ButtonDownEvent{ + mouse_button: button, + position: mouse_position + })); }); } @@ -352,7 +354,10 @@ fn button_down_creates_a_button_down_event() { let button_state = mouse.buttons.get(MouseButton::Left); match button_state.event { - Some(MouseEvent::Down(_)) => {}, + Some(MouseEvent::Down(button_down_event)) => { + assert_eq!(mouse.xy, button_down_event.position); + assert_eq!(MouseButton::Left, button_down_event.mouse_button); + }, _ => panic!("Expected a button down event") } } diff --git a/src/widget/text_box.rs b/src/widget/text_box.rs index 90e0c18ac..0428a8458 100644 --- a/src/widget/text_box.rs +++ b/src/widget/text_box.rs @@ -361,8 +361,12 @@ fn get_new_interaction(text: &str, args: &UpdateArgs, C>) -> In .flat_map(|mouse| mouse.events()) // map MouseEvent to an Option .filter_map(|event| { - use ::mouse::events::MouseEvent::{Click, Drag}; + use ::mouse::events::MouseEvent::{Click, Down, Drag}; match event { + Down(down_info) if down_info.mouse_button == MouseButton::Left => { + let clicked_elem = get_clicked_elem(text, down_info.position, args); + Some(get_interaction_for_click(text, clicked_elem)) + }, Click(click_info) if click_info.mouse_button == MouseButton::Left => { let clicked_elem = get_clicked_elem(text, click_info.position, args); Some(get_interaction_for_click(text, clicked_elem))