diff --git a/druid-shell/examples/shello.rs b/druid-shell/examples/shello.rs index 702973e69f..9bf00ef834 100644 --- a/druid-shell/examples/shello.rs +++ b/druid-shell/examples/shello.rs @@ -18,8 +18,8 @@ use druid_shell::kurbo::{Line, Rect, Vec2}; use druid_shell::piet::{Color, RenderContext}; use druid_shell::{ - Application, Cursor, FileDialogOptions, FileSpec, HotKey, KeyEvent, KeyModifiers, Menu, - MouseEvent, SysMods, TimerToken, WinHandler, WindowBuilder, WindowHandle, + Application, ClickEvent, Cursor, FileDialogOptions, FileSpec, HotKey, KeyEvent, KeyModifiers, + Menu, MoveEvent, SysMods, TimerToken, WinHandler, WindowBuilder, WindowHandle, }; const BG_COLOR: Color = Color::rgb8(0x27, 0x28, 0x22); @@ -75,16 +75,16 @@ impl WinHandler for HelloState { println!("mouse_wheel {:?} {:?}", delta, mods); } - fn mouse_move(&mut self, event: &MouseEvent) { + fn mouse_move(&mut self, event: &MoveEvent) { self.handle.set_cursor(&Cursor::Arrow); println!("mouse_move {:?}", event); } - fn mouse_down(&mut self, event: &MouseEvent) { + fn mouse_down(&mut self, event: &ClickEvent) { println!("mouse_down {:?}", event); } - fn mouse_up(&mut self, event: &MouseEvent) { + fn mouse_up(&mut self, event: &ClickEvent) { println!("mouse_up {:?}", event); } diff --git a/druid-shell/src/lib.rs b/druid-shell/src/lib.rs index ed9a26edb0..98a8c94c6f 100644 --- a/druid-shell/src/lib.rs +++ b/druid-shell/src/lib.rs @@ -46,7 +46,7 @@ pub use hotkey::{HotKey, KeyCompare, RawMods, SysMods}; pub use keyboard::{KeyEvent, KeyModifiers}; pub use keycodes::KeyCode; pub use menu::Menu; -pub use mouse::{Cursor, MouseButton, MouseEvent}; +pub use mouse::{ClickEvent, Cursor, MouseButton, MouseButtons, MoveEvent}; pub use window::{ IdleHandle, IdleToken, Text, TimerToken, WinHandler, WindowBuilder, WindowHandle, }; diff --git a/druid-shell/src/mouse.rs b/druid-shell/src/mouse.rs index feac321198..70a41e4a60 100644 --- a/druid-shell/src/mouse.rs +++ b/druid-shell/src/mouse.rs @@ -18,50 +18,241 @@ use crate::kurbo::Point; use crate::keyboard::KeyModifiers; -/// The state of the mouse for a click, mouse-up, or move event. +/// Information about the mouse move event. #[derive(Debug, Clone, PartialEq)] -pub struct MouseEvent { +pub struct MoveEvent { /// The location of the mouse in the current window. /// - /// This is in px units, that is, adjusted for hi-dpi. + /// This is in px units not device pixels, that is, adjusted for hi-dpi. pub pos: Point, - /// Keyboard modifiers at the time of the mouse event. + /// Mouse buttons being held down at the time of the event. + pub buttons: MouseButtons, + /// Keyboard modifiers at the time of the event. + pub mods: KeyModifiers, +} + +/// Information about the mouse click event. +#[derive(Debug, Clone, PartialEq)] +pub struct ClickEvent { + /// The location of the mouse in the current window. + /// + /// This is in px units not device pixels, that is, adjusted for hi-dpi. + pub pos: Point, + /// Mouse buttons being held down after the event. + /// Thus it will contain the `button` that triggered a mouse-down event, + /// and it will not contain the `button` that triggered a mouse-up event. + pub buttons: MouseButtons, + /// Keyboard modifiers at the time of the event. pub mods: KeyModifiers, /// The number of mouse clicks associated with this event. This will always /// be `0` for a mouse-up event. - pub count: u32, - /// The currently pressed button in the case of a move or click event, - /// or the released button in the case of a mouse-up event. + pub count: u8, + /// The button that was pressed down in the case of mouse-down, + /// or the button that was released in the case of mouse-up. pub button: MouseButton, } /// An indicator of which mouse button was pressed. #[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[repr(u8)] pub enum MouseButton { /// Left mouse button. Left, - /// Middle mouse button. - Middle, /// Right mouse button. Right, + /// Middle mouse button. + Middle, /// First X button. X1, /// Second X button. X2, + /// Some other unknown button. + Other, } impl MouseButton { - /// Returns `true` if this is the left mouse button. - #[inline(always)] + /// Returns `true` if this is [`MouseButton::Left`]. + /// + /// [`MouseButton::Left`]: #variant.Left + #[inline] pub fn is_left(self) -> bool { self == MouseButton::Left } - /// Returns `true` if this is the right mouse button. - #[inline(always)] + /// Returns `true` if this is [`MouseButton::Right`]. + /// + /// [`MouseButton::Right`]: #variant.Right + #[inline] pub fn is_right(self) -> bool { self == MouseButton::Right } + + /// Returns `true` if this is [`MouseButton::Middle`]. + /// + /// [`MouseButton::Middle`]: #variant.Middle + #[inline] + pub fn is_middle(self) -> bool { + self == MouseButton::Middle + } + + /// Returns `true` if this is [`MouseButton::X1`]. + /// + /// [`MouseButton::X1`]: #variant.X1 + #[inline] + pub fn is_x1(self) -> bool { + self == MouseButton::X1 + } + + /// Returns `true` if this is [`MouseButton::X2`]. + /// + /// [`MouseButton::X2`]: #variant.X2 + #[inline] + pub fn is_x2(self) -> bool { + self == MouseButton::X2 + } + + /// Returns `true` if this is [`MouseButton::Other`]. + /// + /// [`MouseButton::Other`]: #variant.Other + #[inline] + pub fn is_other(self) -> bool { + self == MouseButton::Other + } +} + +/// A set of [`MouseButton`]s. +/// +/// [`MouseButton`]: enum.MouseButton.html +#[derive(PartialEq, Eq, Clone, Copy, Default)] +pub struct MouseButtons(u8); + +impl MouseButtons { + /// Create a new empty set. + #[inline] + pub fn new() -> MouseButtons { + MouseButtons(0) + } + + /// Add the `button` to the set. + #[inline] + pub fn add(&mut self, button: MouseButton) { + self.0 |= 1 << button as u8; + } + + /// Remove the `button` from the set. + #[inline] + pub fn remove(&mut self, button: MouseButton) { + self.0 &= !(1 << button as u8); + } + + /// Builder-style method for adding the `button` to the set. + #[inline] + pub fn with(mut self, button: MouseButton) -> MouseButtons { + // TODO: Does this compile down well enough or should we do the bit work here? + self.add(button); + self + } + + /// Builder-style method for removing the `button` from the set. + #[inline] + pub fn without(mut self, button: MouseButton) -> MouseButtons { + self.remove(button); + self + } + + /// Returns `true` if the `button` is in the set. + #[inline] + pub fn has(self, button: MouseButton) -> bool { + (self.0 & (1 << button as u8)) != 0 + } + + /// Returns `true` if any button is in the set. + #[inline] + pub fn has_any(self) -> bool { + self.0 != 0 + } + + /// Returns `true` if the set is empty. + #[inline] + pub fn has_none(self) -> bool { + self.0 == 0 + } + + /// Returns `true` if [`MouseButton::Left`] is in the set. + /// + /// [`MouseButton::Left`]: enum.MouseButton.html#variant.Left + #[inline] + pub fn has_left(self) -> bool { + self.has(MouseButton::Left) + } + + /// Returns `true` if [`MouseButton::Right`] is in the set. + /// + /// [`MouseButton::Right`]: enum.MouseButton.html#variant.Right + #[inline] + pub fn has_right(self) -> bool { + self.has(MouseButton::Right) + } + + /// Returns `true` if [`MouseButton::Middle`] is in the set. + /// + /// [`MouseButton::Middle`]: enum.MouseButton.html#variant.Middle + #[inline] + pub fn has_middle(self) -> bool { + self.has(MouseButton::Middle) + } + + /// Returns `true` if [`MouseButton::X1`] is in the set. + /// + /// [`MouseButton::X1`]: enum.MouseButton.html#variant.X1 + #[inline] + pub fn has_x1(self) -> bool { + self.has(MouseButton::X1) + } + + /// Returns `true` if [`MouseButton::X2`] is in the set. + /// + /// [`MouseButton::X2`]: enum.MouseButton.html#variant.X2 + #[inline] + pub fn has_x2(self) -> bool { + self.has(MouseButton::X2) + } + + /// Returns `true` if [`MouseButton::Other`] is in the set. + /// + /// [`MouseButton::Other`]: enum.MouseButton.html#variant.Other + #[inline] + pub fn has_other(self) -> bool { + self.has(MouseButton::Other) + } + + /// Adds all the [`MouseButton`]s in `other` to the set. + /// + /// [`MouseButton`]: enum.MouseButton.html + pub fn union(&mut self, other: MouseButtons) { + self.0 |= other.0; + } + + /// Builder-style method for adding all the [`MouseButton`]s in `other`. + /// + /// [`MouseButton`]: enum.MouseButton.html + #[inline] + pub fn with_other(mut self, other: MouseButtons) -> MouseButtons { + self.union(other); + self + } + + /// Clear the set. + #[inline] + pub fn clear(&mut self) { + self.0 = 0; + } +} + +impl std::fmt::Debug for MouseButtons { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "MouseButtons({:06b})", self.0) + } } //NOTE: this currently only contains cursors that are included by default on diff --git a/druid-shell/src/platform/gtk/window.rs b/druid-shell/src/platform/gtk/window.rs index bf726979cb..b484f249e9 100644 --- a/druid-shell/src/platform/gtk/window.rs +++ b/druid-shell/src/platform/gtk/window.rs @@ -41,7 +41,7 @@ use super::util::assert_main_thread; use crate::common_util::IdleCallback; use crate::dialog::{FileDialogOptions, FileDialogType, FileInfo}; use crate::keyboard; -use crate::mouse::{Cursor, MouseButton, MouseEvent}; +use crate::mouse::{ClickEvent, Cursor, MouseButton, MouseButtons, MoveEvent}; use crate::window::{IdleToken, Text, TimerToken, WinHandler}; use crate::Error; @@ -282,12 +282,17 @@ impl WindowBuilder { drawing_area.connect_button_press_event(clone!(handle => move |_widget, button| { if let Some(state) = handle.state.upgrade() { if let Ok(mut handler) = state.handler.try_borrow_mut() { + let button_state = button.get_state(); + let btn = get_mouse_button(button.get_button()); + let mut buttons = get_mouse_buttons_from_modifiers(button_state); + buttons.add(btn); handler.mouse_down( - &MouseEvent { + &ClickEvent { pos: Point::from(button.get_position()), + buttons, + mods: get_modifiers(button_state), count: get_mouse_click_count(button.get_event_type()), - mods: get_modifiers(button.get_state()), - button: get_mouse_button(button.get_button()), + button: btn, }, ); } else { @@ -301,12 +306,17 @@ impl WindowBuilder { drawing_area.connect_button_release_event(clone!(handle => move |_widget, button| { if let Some(state) = handle.state.upgrade() { if let Ok(mut handler) = state.handler.try_borrow_mut() { + let button_state = button.get_state(); + let btn = get_mouse_button(button.get_button()); + let mut buttons = get_mouse_buttons_from_modifiers(button_state); + buttons.remove(btn); handler.mouse_up( - &MouseEvent { + &ClickEvent { pos: Point::from(button.get_position()), - mods: get_modifiers(button.get_state()), + buttons, + mods: get_modifiers(button_state), count: 0, - button: get_mouse_button(button.get_button()), + button: btn, }, ); } else { @@ -319,17 +329,15 @@ impl WindowBuilder { drawing_area.connect_motion_notify_event(clone!(handle => move |_widget, motion| { if let Some(state) = handle.state.upgrade() { - - let pos = Point::from(motion.get_position()); - let mouse_event = MouseEvent { - pos, - mods: get_modifiers(motion.get_state()), - count: 0, - button: get_mouse_button_from_modifiers(motion.get_state()), + let motion_state = motion.get_state(); + let move_event = MoveEvent { + pos: Point::from(motion.get_position()), + buttons: get_mouse_buttons_from_modifiers(motion_state), + mods: get_modifiers(motion_state), }; if let Ok(mut handler) = state.handler.try_borrow_mut() { - handler.mouse_move(&mouse_event); + handler.mouse_move(&move_event); } else { log::info!("GTK event was dropped because the handler was already borrowed"); } @@ -340,17 +348,15 @@ impl WindowBuilder { drawing_area.connect_leave_notify_event(clone!(handle => move |_widget, crossing| { if let Some(state) = handle.state.upgrade() { - - let pos = Point::from(crossing.get_position()); - let mouse_event = MouseEvent { - pos, - mods: get_modifiers(crossing.get_state()), - count: 0, - button: get_mouse_button_from_modifiers(crossing.get_state()), + let crossing_state = crossing.get_state(); + let move_event = MoveEvent { + pos: Point::from(crossing.get_position()), + buttons: get_mouse_buttons_from_modifiers(crossing_state), + mods: get_modifiers(crossing_state), }; if let Ok(mut handler) = state.handler.try_borrow_mut() { - handler.mouse_move(&mouse_event); + handler.mouse_move(&move_event); } else { log::info!("GTK event was dropped because the handler was already borrowed"); } @@ -734,36 +740,51 @@ fn make_gdk_cursor(cursor: &Cursor, gdk_window: &gdk::Window) -> Option MouseButton { + println!("Button: {}", button); match button { 1 => MouseButton::Left, 2 => MouseButton::Middle, 3 => MouseButton::Right, - 4 => MouseButton::X1, - 5 => MouseButton::X2, - _ => MouseButton::Left, + // GDK X backend interprets button press events for button 4-7 as scroll events + 8 => MouseButton::X1, + 9 => MouseButton::X2, + _ => MouseButton::Other, } } -fn get_mouse_button_from_modifiers(modifiers: gdk::ModifierType) -> MouseButton { - match modifiers { - modifiers if modifiers.contains(ModifierType::BUTTON1_MASK) => MouseButton::Left, - modifiers if modifiers.contains(ModifierType::BUTTON2_MASK) => MouseButton::Middle, - modifiers if modifiers.contains(ModifierType::BUTTON3_MASK) => MouseButton::Right, - modifiers if modifiers.contains(ModifierType::BUTTON4_MASK) => MouseButton::X1, - modifiers if modifiers.contains(ModifierType::BUTTON5_MASK) => MouseButton::X2, - _ => { - //FIXME: what about when no modifiers match? - MouseButton::Left - } +fn get_mouse_buttons_from_modifiers(modifiers: gdk::ModifierType) -> MouseButtons { + let mut buttons = MouseButtons::new(); + if modifiers.contains(ModifierType::BUTTON1_MASK) { + buttons.add(MouseButton::Left); + } + if modifiers.contains(ModifierType::BUTTON2_MASK) { + buttons.add(MouseButton::Middle); } + if modifiers.contains(ModifierType::BUTTON3_MASK) { + buttons.add(MouseButton::Right); + } + // TODO: Determine X1/X2 state (do caching ourselves if needed) + // Checking for BUTTON4_MASK/BUTTON5_MASK does not work with GDK X, + // because those are wheel events instead. + if modifiers.contains(ModifierType::BUTTON4_MASK) { + buttons.add(MouseButton::X1); + } + if modifiers.contains(ModifierType::BUTTON5_MASK) { + buttons.add(MouseButton::X2); + } + buttons } -fn get_mouse_click_count(event_type: gdk::EventType) -> u32 { +fn get_mouse_click_count(event_type: gdk::EventType) -> u8 { match event_type { gdk::EventType::ButtonPress => 1, gdk::EventType::DoubleButtonPress => 2, gdk::EventType::TripleButtonPress => 3, - _ => 0, + gdk::EventType::ButtonRelease => 0, + _ => { + log::warn!("Unexpected mouse click event type: {:?}", event_type); + 0 + } } } diff --git a/druid-shell/src/platform/mac/window.rs b/druid-shell/src/platform/mac/window.rs index 1e28f8100e..4e135a0ec8 100644 --- a/druid-shell/src/platform/mac/window.rs +++ b/druid-shell/src/platform/mac/window.rs @@ -28,7 +28,9 @@ use cocoa::appkit::{ NSWindowStyleMask, }; use cocoa::base::{id, nil, BOOL, NO, YES}; -use cocoa::foundation::{NSAutoreleasePool, NSPoint, NSRect, NSSize, NSString}; +use cocoa::foundation::{ + NSAutoreleasePool, NSInteger, NSPoint, NSRect, NSSize, NSString, NSUInteger, +}; use lazy_static::lazy_static; use objc::declare::ClassDecl; use objc::rc::WeakPtr; @@ -48,7 +50,7 @@ use crate::common_util::IdleCallback; use crate::dialog::{FileDialogOptions, FileDialogType, FileInfo}; use crate::keyboard::{KeyEvent, KeyModifiers}; use crate::keycodes::KeyCode; -use crate::mouse::{Cursor, MouseButton, MouseEvent}; +use crate::mouse::{ClickEvent, Cursor, MouseButton, MouseButtons, MoveEvent}; use crate::window::{IdleToken, Text, TimerToken, WinHandler}; use crate::Error; @@ -254,6 +256,10 @@ lazy_static! { sel!(rightMouseDown:), mouse_down_right as extern "C" fn(&mut Object, Sel, id), ); + decl.add_method( + sel!(otherMouseDown:), + mouse_down_other as extern "C" fn(&mut Object, Sel, id), + ); decl.add_method( sel!(mouseUp:), mouse_up_left as extern "C" fn(&mut Object, Sel, id), @@ -262,6 +268,10 @@ lazy_static! { sel!(rightMouseUp:), mouse_up_right as extern "C" fn(&mut Object, Sel, id), ); + decl.add_method( + sel!(otherMouseUp:), + mouse_up_other as extern "C" fn(&mut Object, Sel, id), + ); decl.add_method( sel!(mouseMoved:), mouse_move as extern "C" fn(&mut Object, Sel, id), @@ -270,6 +280,10 @@ lazy_static! { sel!(mouseDragged:), mouse_move as extern "C" fn(&mut Object, Sel, id), ); + decl.add_method( + sel!(otherMouseDragged:), + mouse_move as extern "C" fn(&mut Object, Sel, id), + ); decl.add_method( sel!(scrollWheel:), scroll_wheel as extern "C" fn(&mut Object, Sel, id), @@ -345,22 +359,16 @@ extern "C" fn set_frame_size(this: &mut Object, _: Sel, size: NSSize) { } } -// NOTE: If we know the button (because of the origin call) we pass it through, -// otherwise we get it from the event itself. -fn mouse_event(nsevent: id, view: id, button: Option) -> MouseEvent { +fn click_event(nsevent: id, view: id, count: u8, button: MouseButton) -> ClickEvent { unsafe { - let button = button.unwrap_or_else(|| { - let button = NSEvent::pressedMouseButtons(nsevent); - get_mouse_button(button as usize) - }); let point = nsevent.locationInWindow(); let view_point = view.convertPoint_fromView_(point, nil); let pos = Point::new(view_point.x as f64, view_point.y as f64); - let modifiers = nsevent.modifierFlags(); - let modifiers = make_modifiers(modifiers); - let count = nsevent.clickCount() as u32; - MouseEvent { + let buttons = get_mouse_buttons(NSEvent::pressedMouseButtons(nsevent)); + let modifiers = make_modifiers(nsevent.modifierFlags()); + ClickEvent { pos, + buttons, mods: modifiers, count, button, @@ -368,22 +376,52 @@ fn mouse_event(nsevent: id, view: id, button: Option) -> MouseEvent } } -fn get_mouse_button(mask: usize) -> MouseButton { - //TODO: this doesn't correctly handle multiple buttons being pressed. - match mask { - mask if mask & 1 > 0 => MouseButton::Left, - mask if mask & 1 << 1 > 0 => MouseButton::Right, - mask if mask & 1 << 2 > 0 => MouseButton::Middle, - mask if mask & 1 << 3 > 0 => MouseButton::X1, - mask if mask & 1 << 4 > 0 => MouseButton::X2, - _ => { - //FIXME: this gets called when the mouse moves, where there - //may be no buttons down. This is mostly a problem with our API? - MouseButton::Left +fn move_event(nsevent: id, view: id) -> MoveEvent { + unsafe { + let point = nsevent.locationInWindow(); + let view_point = view.convertPoint_fromView_(point, nil); + let pos = Point::new(view_point.x as f64, view_point.y as f64); + let buttons = get_mouse_buttons(NSEvent::pressedMouseButtons(nsevent)); + let modifiers = make_modifiers(nsevent.modifierFlags()); + MoveEvent { + pos, + buttons, + mods: modifiers, } } } +fn get_mouse_button(button: NSInteger) -> MouseButton { + match button { + 0 => MouseButton::Left, + 1 => MouseButton::Right, + 2 => MouseButton::Middle, + 3 => MouseButton::X1, + 4 => MouseButton::X2, + _ => MouseButton::Other, + } +} + +fn get_mouse_buttons(mask: NSUInteger) -> MouseButtons { + let mut buttons = MouseButtons::new(); + if mask & 1 != 0 { + buttons.add(MouseButton::Left); + } + if mask & 1 << 1 != 0 { + buttons.add(MouseButton::Right); + } + if mask & 1 << 2 != 0 { + buttons.add(MouseButton::Middle); + } + if mask & 1 << 3 != 0 { + buttons.add(MouseButton::X1); + } + if mask & 1 << 4 != 0 { + buttons.add(MouseButton::X2); + } + buttons +} + extern "C" fn mouse_down_left(this: &mut Object, _: Sel, nsevent: id) { mouse_down(this, nsevent, MouseButton::Left) } @@ -392,11 +430,19 @@ extern "C" fn mouse_down_right(this: &mut Object, _: Sel, nsevent: id) { mouse_down(this, nsevent, MouseButton::Right) } +extern "C" fn mouse_down_other(this: &mut Object, _: Sel, nsevent: id) { + unsafe { + let button = get_mouse_button(nsevent.buttonNumber()); + mouse_down(this, nsevent, button) + } +} + fn mouse_down(this: &mut Object, nsevent: id, button: MouseButton) { unsafe { let view_state: *mut c_void = *this.get_ivar("viewState"); let view_state = &mut *(view_state as *mut ViewState); - let event = mouse_event(nsevent, this as id, Some(button)); + let count = nsevent.clickCount() as u8; + let event = click_event(nsevent, this as id, count, button); (*view_state).handler.mouse_down(&event); } } @@ -409,11 +455,18 @@ extern "C" fn mouse_up_right(this: &mut Object, _: Sel, nsevent: id) { mouse_up(this, nsevent, MouseButton::Right) } +extern "C" fn mouse_up_other(this: &mut Object, _: Sel, nsevent: id) { + unsafe { + let button = get_mouse_button(nsevent.buttonNumber()); + mouse_up(this, nsevent, button) + } +} + fn mouse_up(this: &mut Object, nsevent: id, button: MouseButton) { unsafe { let view_state: *mut c_void = *this.get_ivar("viewState"); let view_state = &mut *(view_state as *mut ViewState); - let event = mouse_event(nsevent, this as id, Some(button)); + let event = click_event(nsevent, this as id, 0, button); (*view_state).handler.mouse_up(&event); } } @@ -422,7 +475,7 @@ extern "C" fn mouse_move(this: &mut Object, _: Sel, nsevent: id) { unsafe { let view_state: *mut c_void = *this.get_ivar("viewState"); let view_state = &mut *(view_state as *mut ViewState); - let event = mouse_event(nsevent, this as id, None); + let event = move_event(nsevent, this as id); (*view_state).handler.mouse_move(&event); } } diff --git a/druid-shell/src/platform/web/window.rs b/druid-shell/src/platform/web/window.rs index 685d979fb3..22ce8d8787 100644 --- a/druid-shell/src/platform/web/window.rs +++ b/druid-shell/src/platform/web/window.rs @@ -37,7 +37,7 @@ use crate::dialog::{FileDialogOptions, FileDialogType, FileInfo}; use crate::keyboard; use crate::keycodes::KeyCode; -use crate::mouse::{Cursor, MouseButton, MouseEvent}; +use crate::mouse::{ClickEvent, Cursor, MouseButton, MouseButtons, MoveEvent}; use crate::window::{IdleToken, Text, TimerToken, WinHandler}; use crate::KeyModifiers; @@ -144,42 +144,45 @@ impl WindowState { fn setup_mouse_down_callback(ws: &Rc) { let state = ws.clone(); register_canvas_event_listener(ws, "mousedown", move |event: web_sys::MouseEvent| { - let button = mouse_button(event.button()).unwrap(); - let event = MouseEvent { + let button = mouse_button(event.button()); + let buttons = mouse_buttons(event.buttons()); + let event = ClickEvent { pos: Point::new(event.offset_x() as f64, event.offset_y() as f64), + buttons, mods: get_modifiers!(event), - button, count: 1, + button, }; state.handler.borrow_mut().mouse_down(&event); }); } -fn setup_mouse_move_callback(ws: &Rc) { +fn setup_mouse_up_callback(ws: &Rc) { let state = ws.clone(); - register_canvas_event_listener(ws, "mousemove", move |event: web_sys::MouseEvent| { - let button = mouse_button(event.button()).unwrap(); - let event = MouseEvent { + register_canvas_event_listener(ws, "mouseup", move |event: web_sys::MouseEvent| { + let button = mouse_button(event.button()); + let buttons = mouse_buttons(event.buttons()); + let event = ClickEvent { pos: Point::new(event.offset_x() as f64, event.offset_y() as f64), + buttons, mods: get_modifiers!(event), + count: 0, button, - count: 1, }; - state.handler.borrow_mut().mouse_move(&event); + state.handler.borrow_mut().mouse_up(&event); }); } -fn setup_mouse_up_callback(ws: &Rc) { +fn setup_mouse_move_callback(ws: &Rc) { let state = ws.clone(); - register_canvas_event_listener(ws, "mouseup", move |event: web_sys::MouseEvent| { - let button = mouse_button(event.button()).unwrap(); - let event = MouseEvent { + register_canvas_event_listener(ws, "mousemove", move |event: web_sys::MouseEvent| { + let buttons = mouse_buttons(event.buttons()); + let event = MoveEvent { pos: Point::new(event.offset_x() as f64, event.offset_y() as f64), + buttons, mods: get_modifiers!(event), - button, - count: 0, }; - state.handler.borrow_mut().mouse_up(&event); + state.handler.borrow_mut().mouse_move(&event); }); } @@ -590,13 +593,35 @@ impl IdleHandle { } } -fn mouse_button(button: i16) -> Option { +fn mouse_button(button: i16) -> MouseButton { match button { - 0 => Some(MouseButton::Left), - 1 => Some(MouseButton::Middle), - 2 => Some(MouseButton::Right), - _ => None, + 0 => MouseButton::Left, + 1 => MouseButton::Middle, + 2 => MouseButton::Right, + 3 => MouseButton::X1, + 4 => MouseButton::X2, + _ => MouseButton::Other, + } +} + +fn mouse_buttons(mask: u16) -> MouseButtons { + let mut buttons = MouseButtons::new(); + if mask & 1 != 0 { + buttons.add(MouseButton::Left); + } + if mask & 1 << 1 != 0 { + buttons.add(MouseButton::Right); + } + if mask & 1 << 2 != 0 { + buttons.add(MouseButton::Middle); + } + if mask & 1 << 3 != 0 { + buttons.add(MouseButton::X1); + } + if mask & 1 << 4 != 0 { + buttons.add(MouseButton::X2); } + buttons } fn set_cursor(canvas: &web_sys::HtmlCanvasElement, cursor: &Cursor) { diff --git a/druid-shell/src/platform/windows/window.rs b/druid-shell/src/platform/windows/window.rs index 7e424b96ac..ad8bc0b350 100644 --- a/druid-shell/src/platform/windows/window.rs +++ b/druid-shell/src/platform/windows/window.rs @@ -59,7 +59,7 @@ use crate::common_util::IdleCallback; use crate::dialog::{FileDialogOptions, FileDialogType, FileInfo}; use crate::keyboard::{KeyEvent, KeyModifiers}; use crate::keycodes::KeyCode; -use crate::mouse::{Cursor, MouseButton, MouseEvent}; +use crate::mouse::{ClickEvent, Cursor, MouseButton, MouseButtons, MoveEvent}; use crate::window::{IdleToken, Text, TimerToken, WinHandler}; extern "system" { @@ -169,10 +169,10 @@ struct WndState { /// The `char` of the last `WM_CHAR` event, if there has not already been /// a `WM_KEYUP` event. stashed_char: Option, - // Stores a bit mask of all mouse buttons that are currently holding mouse + // Stores a set of all mouse buttons that are currently holding mouse // capture. When the first mouse button is down on our window we enter // capture, and we hold it until the last mouse button is up. - captured_mouse_buttons: u32, + captured_mouse_buttons: MouseButtons, // Is this window the topmost window under the mouse cursor has_mouse_focus: bool, //TODO: track surrogate orphan @@ -215,20 +215,53 @@ impl Default for PresentStrategy { /// Must only be called while handling an input message. /// This queries the keyboard state at the time of message delivery. fn get_mod_state() -> KeyModifiers { - //FIXME: does not handle windows key - unsafe { - let mut mod_state = KeyModifiers::default(); - if GetKeyState(VK_MENU) < 0 { - mod_state.alt = true; - } - if GetKeyState(VK_CONTROL) < 0 { - mod_state.ctrl = true; - } - if GetKeyState(VK_SHIFT) < 0 { - mod_state.shift = true; - } - mod_state + KeyModifiers { + shift: get_mod_state_shift(), + alt: get_mod_state_alt(), + ctrl: get_mod_state_ctrl(), + meta: get_mod_state_win(), + } +} + +#[inline] +fn get_mod_state_shift() -> bool { + unsafe { GetKeyState(VK_SHIFT) < 0 } +} + +#[inline] +fn get_mod_state_alt() -> bool { + unsafe { GetKeyState(VK_MENU) < 0 } +} + +#[inline] +fn get_mod_state_ctrl() -> bool { + unsafe { GetKeyState(VK_CONTROL) < 0 } +} + +#[inline] +fn get_mod_state_win() -> bool { + unsafe { GetKeyState(VK_LWIN) < 0 || GetKeyState(VK_RWIN) < 0 } +} + +/// Extract the buttons that are being held down from wparam in mouse events. +fn get_buttons(wparam: WPARAM) -> MouseButtons { + let mut buttons = MouseButtons::new(); + if wparam & MK_LBUTTON != 0 { + buttons.add(MouseButton::Left); } + if wparam & MK_RBUTTON != 0 { + buttons.add(MouseButton::Right); + } + if wparam & MK_MBUTTON != 0 { + buttons.add(MouseButton::Middle); + } + if wparam & MK_XBUTTON1 != 0 { + buttons.add(MouseButton::X1); + } + if wparam & MK_XBUTTON2 != 0 { + buttons.add(MouseButton::X2); + } + buttons } fn is_point_in_client_rect(hwnd: HWND, x: i32, y: i32) -> bool { @@ -283,17 +316,17 @@ impl WndState { } fn enter_mouse_capture(&mut self, hwnd: HWND, button: MouseButton) { - if self.captured_mouse_buttons == 0 { + if self.captured_mouse_buttons.has_none() { unsafe { SetCapture(hwnd); } } - self.captured_mouse_buttons |= 1 << (button as u32); + self.captured_mouse_buttons.add(button); } fn exit_mouse_capture(&mut self, button: MouseButton) -> bool { - self.captured_mouse_buttons &= !(1 << (button as u32)); - self.captured_mouse_buttons == 0 + self.captured_mouse_buttons.remove(button); + self.captured_mouse_buttons.has_none() } } @@ -631,23 +664,14 @@ impl WndProc for MyWndProc { let (px, py) = self.handle.borrow().pixels_to_px_xy(x, y); let pos = Point::new(px as f64, py as f64); - let mods = get_mod_state(); - let button = match wparam { - w if (w & 1) > 0 => MouseButton::Left, - w if (w & 1 << 1) > 0 => MouseButton::Right, - w if (w & 1 << 5) > 0 => MouseButton::Middle, - w if (w & 1 << 6) > 0 => MouseButton::X1, - w if (w & 1 << 7) > 0 => MouseButton::X2, - //FIXME: I guess we probably do want `MouseButton::None`? - //this feels bad, but also this gets discarded in druid anyway. - _ => MouseButton::Left, - }; - let event = MouseEvent { - pos, - mods, - button, - count: 0, + let mods = KeyModifiers { + shift: wparam & MK_SHIFT != 0, + alt: get_mod_state_alt(), + ctrl: wparam & MK_CONTROL != 0, + meta: get_mod_state_win(), }; + let buttons = get_buttons(wparam); + let event = MoveEvent { pos, buttons, mods }; s.handler.mouse_move(&event); } else { self.log_dropped_msg(hwnd, msg, wparam, lparam); @@ -666,24 +690,21 @@ impl WndProc for MyWndProc { } // TODO: not clear where double-click processing should happen. Currently disabled // because CS_DBLCLKS is not set - WM_LBUTTONDBLCLK | WM_LBUTTONDOWN | WM_LBUTTONUP | WM_MBUTTONDBLCLK - | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_RBUTTONDBLCLK | WM_RBUTTONDOWN | WM_RBUTTONUP + WM_LBUTTONDBLCLK | WM_LBUTTONDOWN | WM_LBUTTONUP | WM_RBUTTONDBLCLK + | WM_RBUTTONDOWN | WM_RBUTTONUP | WM_MBUTTONDBLCLK | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_XBUTTONDBLCLK | WM_XBUTTONDOWN | WM_XBUTTONUP => { let mut should_release_capture = false; if let Ok(mut s) = self.state.try_borrow_mut() { let s = s.as_mut().unwrap(); let button = match msg { WM_LBUTTONDBLCLK | WM_LBUTTONDOWN | WM_LBUTTONUP => MouseButton::Left, - WM_MBUTTONDBLCLK | WM_MBUTTONDOWN | WM_MBUTTONUP => MouseButton::Middle, WM_RBUTTONDBLCLK | WM_RBUTTONDOWN | WM_RBUTTONUP => MouseButton::Right, + WM_MBUTTONDBLCLK | WM_MBUTTONDOWN | WM_MBUTTONUP => MouseButton::Middle, WM_XBUTTONDBLCLK | WM_XBUTTONDOWN | WM_XBUTTONUP => { match HIWORD(wparam as u32) { - 1 => MouseButton::X1, - 2 => MouseButton::X2, - _ => { - warn!("unexpected X button event"); - return None; - } + XBUTTON1 => MouseButton::X1, + XBUTTON2 => MouseButton::X2, + _ => MouseButton::Other, // Will never happen with current Windows } } _ => unreachable!(), @@ -699,12 +720,19 @@ impl WndProc for MyWndProc { let y = HIWORD(lparam as u32) as i16 as i32; let (px, py) = self.handle.borrow().pixels_to_px_xy(x, y); let pos = Point::new(px as f64, py as f64); - let mods = get_mod_state(); - let event = MouseEvent { + let mods = KeyModifiers { + shift: wparam & MK_SHIFT != 0, + alt: get_mod_state_alt(), + ctrl: wparam & MK_CONTROL != 0, + meta: get_mod_state_win(), + }; + let buttons = get_buttons(wparam); + let event = ClickEvent { pos, + buttons, mods, - button, count, + button, }; if count > 0 { s.enter_mouse_capture(hwnd, button); @@ -764,7 +792,7 @@ impl WndProc for MyWndProc { WM_CAPTURECHANGED => { if let Ok(mut s) = self.state.try_borrow_mut() { let s = s.as_mut().unwrap(); - s.captured_mouse_buttons = 0; + s.captured_mouse_buttons.clear(); } else { self.log_dropped_msg(hwnd, msg, wparam, lparam); } @@ -897,7 +925,7 @@ impl WindowBuilder { min_size: self.min_size, stashed_key_code: KeyCode::Unknown(0), stashed_char: None, - captured_mouse_buttons: 0, + captured_mouse_buttons: MouseButtons::new(), has_mouse_focus: false, }; win.wndproc.connect(&handle, state); diff --git a/druid-shell/src/platform/x11/application.rs b/druid-shell/src/platform/x11/application.rs index 15700b46c8..e2bbf5749f 100644 --- a/druid-shell/src/platform/x11/application.rs +++ b/druid-shell/src/platform/x11/application.rs @@ -24,7 +24,7 @@ use super::clipboard::Clipboard; use super::window::XWindow; use crate::application::AppHandler; use crate::kurbo::Point; -use crate::{KeyCode, KeyModifiers, MouseButton, MouseEvent}; +use crate::{ClickEvent, KeyCode, KeyModifiers, MouseButton, MouseButtons, MoveEvent}; struct XcbConnection { connection: Arc, @@ -100,11 +100,13 @@ impl Application { xcb::BUTTON_PRESS => { let button_press: &xcb::ButtonPressEvent = unsafe { xcb::cast_event(&ev) }; let window_id = button_press.event(); - let mouse_event = MouseEvent { + let click_event = ClickEvent { pos: Point::new( button_press.event_x() as f64, button_press.event_y() as f64, ), + // TODO: Fill with held down buttons + buttons: MouseButtons::new().with(MouseButton::Left), mods: KeyModifiers { shift: false, alt: false, @@ -117,7 +119,7 @@ impl Application { WINDOW_MAP.with(|map| { let mut windows = map.borrow_mut(); if let Some(w) = windows.get_mut(&window_id) { - w.mouse_down(&mouse_event); + w.mouse_down(&click_event); } }) } @@ -125,11 +127,13 @@ impl Application { let button_release: &xcb::ButtonReleaseEvent = unsafe { xcb::cast_event(&ev) }; let window_id = button_release.event(); - let mouse_event = MouseEvent { + let click_event = ClickEvent { pos: Point::new( button_release.event_x() as f64, button_release.event_y() as f64, ), + // TODO: Fill with held down buttons + buttons: MouseButtons::new(), mods: KeyModifiers { shift: false, alt: false, @@ -142,31 +146,31 @@ impl Application { WINDOW_MAP.with(|map| { let mut windows = map.borrow_mut(); if let Some(w) = windows.get_mut(&window_id) { - w.mouse_up(&mouse_event); + w.mouse_up(&click_event); } }) } xcb::MOTION_NOTIFY => { let mouse_move: &xcb::MotionNotifyEvent = unsafe { xcb::cast_event(&ev) }; let window_id = mouse_move.event(); - let mouse_event = MouseEvent { + let move_event = MoveEvent { pos: Point::new( mouse_move.event_x() as f64, mouse_move.event_y() as f64, ), + // TODO: Fill with held down buttons + buttons: MouseButtons::new(), mods: KeyModifiers { shift: false, alt: false, ctrl: false, meta: false, }, - count: 0, - button: MouseButton::Left, }; WINDOW_MAP.with(|map| { let mut windows = map.borrow_mut(); if let Some(w) = windows.get_mut(&window_id) { - w.mouse_move(&mouse_event); + w.mouse_move(&move_event); } }) } diff --git a/druid-shell/src/platform/x11/window.rs b/druid-shell/src/platform/x11/window.rs index 38fe8a4df1..cf204d3a03 100644 --- a/druid-shell/src/platform/x11/window.rs +++ b/druid-shell/src/platform/x11/window.rs @@ -23,7 +23,7 @@ use crate::dialog::{FileDialogOptions, FileInfo}; use crate::keyboard::{KeyEvent, KeyModifiers}; use crate::keycodes::KeyCode; use crate::kurbo::{Point, Size}; -use crate::mouse::{Cursor, MouseEvent}; +use crate::mouse::{ClickEvent, Cursor, MoveEvent}; use crate::piet::{Piet, RenderContext}; use crate::window::{IdleToken, Text, TimerToken, WinHandler}; @@ -258,16 +258,16 @@ impl XWindow { )); } - pub fn mouse_down(&mut self, mouse_event: &MouseEvent) { - self.handler.mouse_down(mouse_event); + pub fn mouse_down(&mut self, click_event: &ClickEvent) { + self.handler.mouse_down(click_event); } - pub fn mouse_up(&mut self, mouse_event: &MouseEvent) { - self.handler.mouse_up(mouse_event); + pub fn mouse_up(&mut self, click_event: &ClickEvent) { + self.handler.mouse_up(click_event); } - pub fn mouse_move(&mut self, mouse_event: &MouseEvent) { - self.handler.mouse_move(mouse_event); + pub fn mouse_move(&mut self, move_event: &MoveEvent) { + self.handler.mouse_move(move_event); } fn communicate_size(&mut self, size: Size) { @@ -419,7 +419,7 @@ impl WindowHandle { pub fn get_dpi(&self) -> f32 { // TODO(x11/dpi_scaling): figure out DPI scaling - log::warn!("WindowHandle::get_dpi is currently unimplemented for X11 platforms."); + //log::warn!("WindowHandle::get_dpi is currently unimplemented for X11 platforms."); 96.0 } } diff --git a/druid-shell/src/window.rs b/druid-shell/src/window.rs index 22d1ad94cf..7bd4130b36 100644 --- a/druid-shell/src/window.rs +++ b/druid-shell/src/window.rs @@ -23,7 +23,7 @@ use crate::error::Error; use crate::keyboard::{KeyEvent, KeyModifiers}; use crate::kurbo::{Point, Size, Vec2}; use crate::menu::Menu; -use crate::mouse::{Cursor, MouseEvent}; +use crate::mouse::{ClickEvent, Cursor, MoveEvent}; use crate::platform::window as platform; // It's possible we'll want to make this type alias at a lower level, @@ -324,15 +324,15 @@ pub trait WinHandler { /// Called when the mouse moves. #[allow(unused_variables)] - fn mouse_move(&mut self, event: &MouseEvent) {} + fn mouse_move(&mut self, event: &MoveEvent) {} /// Called on mouse button down. #[allow(unused_variables)] - fn mouse_down(&mut self, event: &MouseEvent) {} + fn mouse_down(&mut self, event: &ClickEvent) {} /// Called on mouse button up. #[allow(unused_variables)] - fn mouse_up(&mut self, event: &MouseEvent) {} + fn mouse_up(&mut self, event: &ClickEvent) {} /// Called when the mouse cursor has left the application window fn mouse_leave(&mut self) {} diff --git a/druid/src/event.rs b/druid/src/event.rs index 6bb306f9c8..5b8e64c139 100644 --- a/druid/src/event.rs +++ b/druid/src/event.rs @@ -18,7 +18,7 @@ use crate::kurbo::{Rect, Shape, Size, Vec2}; use druid_shell::{Clipboard, KeyEvent, KeyModifiers, TimerToken}; -use crate::mouse::MouseEvent; +use crate::mouse::{ClickEvent, MoveEvent}; use crate::{Command, Target, WidgetId}; /// An event, propagated downwards during event flow. @@ -64,9 +64,9 @@ pub enum Event { /// to just handle it in `layout`. WindowSize(Size), /// Called when a mouse button is pressed. - MouseDown(MouseEvent), + MouseDown(ClickEvent), /// Called when a mouse button is released. - MouseUp(MouseEvent), + MouseUp(ClickEvent), /// Called when the mouse is moved. /// /// The `MouseMove` event is propagated to the active widget, if @@ -79,7 +79,7 @@ pub enum Event { /// propagated to active or hot widgets. /// /// [`set_cursor`]: struct.EventCtx.html#method.set_cursor - MouseMove(MouseEvent), + MouseMove(MoveEvent), /// Called when a key is pressed. /// /// Note: the intent is for each physical key press to correspond to diff --git a/druid/src/lib.rs b/druid/src/lib.rs index f60053aa01..31fea7cdfd 100644 --- a/druid/src/lib.rs +++ b/druid/src/lib.rs @@ -140,8 +140,8 @@ pub use piet::{Color, LinearGradient, RadialGradient, RenderContext, UnitPoint}; // these are the types from shell that we expose; others we only use internally. pub use shell::{ Application, Clipboard, ClipboardFormat, Cursor, Error as PlatformError, FileDialogOptions, - FileInfo, FileSpec, FormatId, HotKey, KeyCode, KeyEvent, KeyModifiers, MouseButton, RawMods, - SysMods, Text, TimerToken, WindowHandle, + FileInfo, FileSpec, FormatId, HotKey, KeyCode, KeyEvent, KeyModifiers, MouseButton, + MouseButtons, RawMods, SysMods, Text, TimerToken, WindowHandle, }; pub use crate::core::WidgetPod; @@ -157,7 +157,7 @@ pub use ext_event::{ExtEventError, ExtEventSink}; pub use lens::{Lens, LensExt, LensWrap}; pub use localization::LocalizedString; pub use menu::{sys as platform_menus, ContextMenu, MenuDesc, MenuItem}; -pub use mouse::MouseEvent; +pub use mouse::{ClickEvent, MoveEvent}; pub use widget::{Widget, WidgetExt, WidgetId}; pub use win_handler::DruidHandler; pub use window::{Window, WindowId}; diff --git a/druid/src/mouse.rs b/druid/src/mouse.rs index 223e8ab3c9..9757ca9c1a 100644 --- a/druid/src/mouse.rs +++ b/druid/src/mouse.rs @@ -15,39 +15,73 @@ //! The mousey bits use crate::kurbo::Point; -use crate::{KeyModifiers, MouseButton}; +use crate::{KeyModifiers, MouseButton, MouseButtons}; -/// The state of the mouse for a click, mouse-up, or move event. +/// Information about the mouse move event. +/// +/// In `druid`, unlike in `druid_shell`, we treat the widget's coordinate +/// space and the window's coordinate space separately. +#[derive(Debug, Clone, PartialEq)] +pub struct MoveEvent { + /// The position of the mouse in the coordinate space of the receiver. + pub pos: Point, + /// The position of the mouse in the coordinate space of the window. + pub window_pos: Point, + /// Mouse buttons being held down at the time of the event. + pub buttons: MouseButtons, + /// Keyboard modifiers at the time of the event. + pub mods: KeyModifiers, +} + +/// Information about the mouse click event. /// /// In `druid`, unlike in `druid_shell`, we treat the widget's coordinate /// space and the window's coordinate space separately. #[derive(Debug, Clone)] -pub struct MouseEvent { +pub struct ClickEvent { /// The position of the mouse in the coordinate space of the receiver. pub pos: Point, /// The position of the mouse in the coordinate space of the window. pub window_pos: Point, - /// Keyboard modifiers at the time of the mouse event. + /// Mouse buttons being held down after the event. + /// Thus it will contain the `button` that triggered a mouse-down event, + /// and it will not contain the `button` that triggered a mouse-up event. + pub buttons: MouseButtons, + /// Keyboard modifiers at the time of the event. pub mods: KeyModifiers, /// The number of mouse clicks associated with this event. This will always /// be `0` for a mouse-up event. - pub count: u32, - /// The currently pressed button in the case of a move or click event, - /// or the released button in the case of a mouse-up event. + pub count: u8, + /// The button that was pressed down in the case of mouse-down, + /// or the button that was released in the case of mouse-up. pub button: MouseButton, } -impl From for MouseEvent { - fn from(src: druid_shell::MouseEvent) -> MouseEvent { - let druid_shell::MouseEvent { +impl From for MoveEvent { + fn from(src: druid_shell::MoveEvent) -> MoveEvent { + let druid_shell::MoveEvent { pos, buttons, mods } = src; + MoveEvent { + pos, + window_pos: pos, + buttons, + mods, + } + } +} + +impl From for ClickEvent { + fn from(src: druid_shell::ClickEvent) -> ClickEvent { + let druid_shell::ClickEvent { pos, + buttons, mods, count, button, } = src; - MouseEvent { + ClickEvent { pos, window_pos: pos, + buttons, mods, count, button, diff --git a/druid/src/tests/mod.rs b/druid/src/tests/mod.rs index 58766e27fd..8f8c683b13 100644 --- a/druid/src/tests/mod.rs +++ b/druid/src/tests/mod.rs @@ -72,14 +72,13 @@ fn propogate_hot() { .record(&root_rec) .with_id(root); - fn make_mouse(x: f64, y: f64) -> MouseEvent { + fn move_mouse(x: f64, y: f64) -> MoveEvent { let pos = Point::new(x, y); - MouseEvent { + MoveEvent { pos, window_pos: pos, + buttons: MouseButtons::default(), mods: KeyModifiers::default(), - count: 0, - button: MouseButton::Left, } } #[allow(clippy::cognitive_complexity)] @@ -98,7 +97,7 @@ fn propogate_hot() { // and verifying both the widget's `is_hot` status and also that // each widget received the expected HotChanged messages. - harness.event(Event::MouseMove(make_mouse(10., 10.))); + harness.event(Event::MouseMove(move_mouse(10., 10.))); assert!(harness.get_state(root).is_hot); assert!(harness.get_state(empty).is_hot); assert!(!harness.get_state(pad).is_hot); @@ -107,7 +106,7 @@ fn propogate_hot() { assert_matches!(root_rec.next(), Record::E(Event::MouseMove(_))); assert!(root_rec.is_empty() && padding_rec.is_empty() && button_rec.is_empty()); - harness.event(Event::MouseMove(make_mouse(210., 10.))); + harness.event(Event::MouseMove(move_mouse(210., 10.))); assert!(harness.get_state(root).is_hot); assert!(!harness.get_state(empty).is_hot); @@ -119,7 +118,7 @@ fn propogate_hot() { assert_matches!(padding_rec.next(), Record::E(Event::MouseMove(_))); assert!(root_rec.is_empty() && padding_rec.is_empty() && button_rec.is_empty()); - harness.event(Event::MouseMove(make_mouse(260., 60.))); + harness.event(Event::MouseMove(move_mouse(260., 60.))); assert!(harness.get_state(root).is_hot); assert!(!harness.get_state(empty).is_hot); assert!(harness.get_state(button).is_hot); @@ -131,7 +130,7 @@ fn propogate_hot() { assert_matches!(button_rec.next(), Record::E(Event::MouseMove(_))); assert!(root_rec.is_empty() && padding_rec.is_empty() && button_rec.is_empty()); - harness.event(Event::MouseMove(make_mouse(10., 10.))); + harness.event(Event::MouseMove(move_mouse(10., 10.))); assert!(harness.get_state(root).is_hot); assert!(harness.get_state(empty).is_hot); assert!(!harness.get_state(button).is_hot); diff --git a/druid/src/widget/scroll.rs b/druid/src/widget/scroll.rs index 1fb3d1ee93..c2cd40b714 100644 --- a/druid/src/widget/scroll.rs +++ b/druid/src/widget/scroll.rs @@ -20,8 +20,8 @@ use std::time::Duration; use crate::kurbo::{Affine, Point, Rect, RoundedRect, Size, Vec2}; use crate::theme; use crate::{ - BoxConstraints, Data, Env, Event, EventCtx, LayoutCtx, LifeCycle, LifeCycleCtx, PaintCtx, - RenderContext, TimerToken, UpdateCtx, Widget, WidgetPod, + BoxConstraints, ClickEvent, Data, Env, Event, EventCtx, LayoutCtx, LifeCycle, LifeCycleCtx, + MoveEvent, PaintCtx, RenderContext, TimerToken, UpdateCtx, Widget, WidgetPod, }; const SCROLLBAR_MIN_SIZE: f64 = 45.0; @@ -297,8 +297,10 @@ impl> Widget for Scroll { let viewport = Rect::from_origin_size(Point::ORIGIN, size); let scrollbar_is_hovered = match event { - Event::MouseMove(e) | Event::MouseUp(e) | Event::MouseDown(e) => { - let offset_pos = e.pos + self.scroll_offset; + Event::MouseMove(MoveEvent { pos, .. }) + | Event::MouseUp(ClickEvent { pos, .. }) + | Event::MouseDown(ClickEvent { pos, .. }) => { + let offset_pos = *pos + self.scroll_offset; self.point_hits_vertical_bar(viewport, offset_pos, env) || self.point_hits_horizontal_bar(viewport, offset_pos, env) } diff --git a/druid/src/win_handler.rs b/druid/src/win_handler.rs index 5fc65839dc..c4bc703c14 100644 --- a/druid/src/win_handler.rs +++ b/druid/src/win_handler.rs @@ -22,7 +22,7 @@ use std::rc::Rc; use crate::kurbo::{Size, Vec2}; use crate::piet::Piet; use crate::shell::{ - Application, FileDialogOptions, IdleToken, MouseEvent, WinHandler, WindowHandle, + Application, ClickEvent, FileDialogOptions, IdleToken, MoveEvent, WinHandler, WindowHandle, }; use crate::app_delegate::{AppDelegate, DelegateCtx}; @@ -624,18 +624,18 @@ impl WinHandler for DruidHandler { self.app_state.handle_system_cmd(id, Some(self.window_id)); } - fn mouse_down(&mut self, event: &MouseEvent) { + fn mouse_down(&mut self, event: &ClickEvent) { // TODO: double-click detection (or is this done in druid-shell?) let event = Event::MouseDown(event.clone().into()); self.app_state.do_window_event(event, self.window_id); } - fn mouse_up(&mut self, event: &MouseEvent) { + fn mouse_up(&mut self, event: &ClickEvent) { let event = Event::MouseUp(event.clone().into()); self.app_state.do_window_event(event, self.window_id); } - fn mouse_move(&mut self, event: &MouseEvent) { + fn mouse_move(&mut self, event: &MoveEvent) { let event = Event::MouseMove(event.clone().into()); self.app_state.do_window_event(event, self.window_id); } diff --git a/druid/src/window.rs b/druid/src/window.rs index 73223206db..b6981cb973 100644 --- a/druid/src/window.rs +++ b/druid/src/window.rs @@ -26,9 +26,9 @@ use crate::shell::{Counter, Cursor, WindowHandle}; use crate::core::{BaseState, CommandQueue, FocusChange}; use crate::win_handler::RUN_COMMANDS_TOKEN; use crate::{ - BoxConstraints, Command, Data, Env, Event, EventCtx, InternalEvent, InternalLifeCycle, - LayoutCtx, LifeCycle, LifeCycleCtx, LocalizedString, MenuDesc, PaintCtx, UpdateCtx, Widget, - WidgetId, WidgetPod, WindowDesc, + BoxConstraints, ClickEvent, Command, Data, Env, Event, EventCtx, InternalEvent, + InternalLifeCycle, LayoutCtx, LifeCycle, LifeCycleCtx, LocalizedString, MenuDesc, MoveEvent, + PaintCtx, UpdateCtx, Widget, WidgetId, WidgetPod, WindowDesc, }; /// A unique identifier for a window. @@ -151,9 +151,9 @@ impl Window { env: &Env, ) -> bool { match &event { - Event::MouseDown(e) | Event::MouseUp(e) | Event::MouseMove(e) => { - self.last_mouse_pos = Some(e.pos) - } + Event::MouseDown(ClickEvent { pos, .. }) + | Event::MouseUp(ClickEvent { pos, .. }) + | Event::MouseMove(MoveEvent { pos, .. }) => self.last_mouse_pos = Some(*pos), Event::Internal(InternalEvent::MouseLeave) => self.last_mouse_pos = None, _ => (), }