Merge pull request #117 from robbert-vdh/feature/mouse-event-modifiers
Add the active keyboard modifiers to the mouse events
This commit is contained in:
commit
eae4033e7d
40
src/event.rs
40
src/event.rs
|
@ -1,4 +1,4 @@
|
||||||
use keyboard_types::KeyboardEvent;
|
use keyboard_types::{KeyboardEvent, Modifiers};
|
||||||
|
|
||||||
use crate::{Point, WindowInfo};
|
use crate::{Point, WindowInfo};
|
||||||
|
|
||||||
|
@ -32,38 +32,48 @@ pub enum ScrollDelta {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
|
||||||
pub struct MouseClick {
|
|
||||||
pub button: MouseButton,
|
|
||||||
pub click_count: usize,
|
|
||||||
/// The logical coordinates of the mouse position
|
|
||||||
pub position: Point,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub enum MouseEvent {
|
pub enum MouseEvent {
|
||||||
/// The mouse cursor was moved
|
/// The mouse cursor was moved
|
||||||
CursorMoved {
|
CursorMoved {
|
||||||
/// The logical coordinates of the mouse position
|
/// The logical coordinates of the mouse position
|
||||||
position: Point,
|
position: Point,
|
||||||
|
/// The modifiers that were held down just before the event.
|
||||||
|
modifiers: Modifiers,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// A mouse button was pressed.
|
/// A mouse button was pressed.
|
||||||
ButtonPressed(MouseButton),
|
ButtonPressed {
|
||||||
|
/// The button that was pressed.
|
||||||
|
button: MouseButton,
|
||||||
|
/// The modifiers that were held down just before the event.
|
||||||
|
modifiers: Modifiers,
|
||||||
|
},
|
||||||
|
|
||||||
/// A mouse button was released.
|
/// A mouse button was released.
|
||||||
ButtonReleased(MouseButton),
|
ButtonReleased {
|
||||||
|
/// The button that was released.
|
||||||
/// A mouse button was clicked.
|
button: MouseButton,
|
||||||
Click(MouseClick),
|
/// The modifiers that were held down just before the event.
|
||||||
|
modifiers: Modifiers,
|
||||||
|
},
|
||||||
|
|
||||||
/// The mouse wheel was scrolled.
|
/// The mouse wheel was scrolled.
|
||||||
WheelScrolled(ScrollDelta),
|
WheelScrolled {
|
||||||
|
/// How much was scrolled, in factional lines.
|
||||||
|
delta: ScrollDelta,
|
||||||
|
/// The modifiers that were held down just before the event.
|
||||||
|
modifiers: Modifiers,
|
||||||
|
},
|
||||||
|
|
||||||
/// The mouse cursor entered the window.
|
/// The mouse cursor entered the window.
|
||||||
|
///
|
||||||
|
/// May not be available on all platforms.
|
||||||
CursorEntered,
|
CursorEntered,
|
||||||
|
|
||||||
/// The mouse cursor left the window.
|
/// The mouse cursor left the window.
|
||||||
|
///
|
||||||
|
/// May not be available on all platforms.
|
||||||
CursorLeft,
|
CursorLeft,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ use crate::{
|
||||||
WindowOpenOptions,
|
WindowOpenOptions,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::keyboard::make_modifiers;
|
||||||
use super::window::WindowState;
|
use super::window::WindowState;
|
||||||
|
|
||||||
/// Name of the field used to store the `WindowState` pointer.
|
/// Name of the field used to store the `WindowState` pointer.
|
||||||
|
@ -42,6 +43,31 @@ macro_rules! add_simple_mouse_class_method {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Similar to [add_simple_mouse_class_method!], but this creates its own event object for the
|
||||||
|
/// press/release event and adds the active modifier keys to that event.
|
||||||
|
macro_rules! add_mouse_button_class_method {
|
||||||
|
($class:ident, $sel:ident, $event_ty:ident, $button:expr) => {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
extern "C" fn $sel(this: &Object, _: Sel, event: id){
|
||||||
|
let state: &mut WindowState = unsafe {
|
||||||
|
WindowState::from_field(this)
|
||||||
|
};
|
||||||
|
|
||||||
|
let modifiers = unsafe { NSEvent::modifierFlags(event) };
|
||||||
|
|
||||||
|
state.trigger_event(Event::Mouse($event_ty {
|
||||||
|
button: $button,
|
||||||
|
modifiers: make_modifiers(modifiers),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
$class.add_method(
|
||||||
|
sel!($sel:),
|
||||||
|
$sel as extern "C" fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! add_simple_keyboard_class_method {
|
macro_rules! add_simple_keyboard_class_method {
|
||||||
($class:ident, $sel:ident) => {
|
($class:ident, $sel:ident) => {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
@ -126,12 +152,12 @@ unsafe fn create_view_class() -> &'static Class {
|
||||||
view_did_change_backing_properties as extern "C" fn(&Object, Sel, id),
|
view_did_change_backing_properties as extern "C" fn(&Object, Sel, id),
|
||||||
);
|
);
|
||||||
|
|
||||||
add_simple_mouse_class_method!(class, mouseDown, ButtonPressed(MouseButton::Left));
|
add_mouse_button_class_method!(class, mouseDown, ButtonPressed, MouseButton::Left);
|
||||||
add_simple_mouse_class_method!(class, mouseUp, ButtonReleased(MouseButton::Left));
|
add_mouse_button_class_method!(class, mouseUp, ButtonReleased, MouseButton::Left);
|
||||||
add_simple_mouse_class_method!(class, rightMouseDown, ButtonPressed(MouseButton::Right));
|
add_mouse_button_class_method!(class, rightMouseDown, ButtonPressed, MouseButton::Right);
|
||||||
add_simple_mouse_class_method!(class, rightMouseUp, ButtonReleased(MouseButton::Right));
|
add_mouse_button_class_method!(class, rightMouseUp, ButtonReleased, MouseButton::Right);
|
||||||
add_simple_mouse_class_method!(class, otherMouseDown, ButtonPressed(MouseButton::Middle));
|
add_mouse_button_class_method!(class, otherMouseDown, ButtonPressed, MouseButton::Middle);
|
||||||
add_simple_mouse_class_method!(class, otherMouseUp, ButtonReleased(MouseButton::Middle));
|
add_mouse_button_class_method!(class, otherMouseUp, ButtonReleased, MouseButton::Middle);
|
||||||
add_simple_mouse_class_method!(class, mouseEntered, MouseEvent::CursorEntered);
|
add_simple_mouse_class_method!(class, mouseEntered, MouseEvent::CursorEntered);
|
||||||
add_simple_mouse_class_method!(class, mouseExited, MouseEvent::CursorLeft);
|
add_simple_mouse_class_method!(class, mouseExited, MouseEvent::CursorLeft);
|
||||||
|
|
||||||
|
@ -308,8 +334,12 @@ extern "C" fn mouse_moved(this: &Object, _sel: Sel, event: id) {
|
||||||
|
|
||||||
msg_send![this, convertPoint:point fromView:nil]
|
msg_send![this, convertPoint:point fromView:nil]
|
||||||
};
|
};
|
||||||
|
let modifiers = unsafe { NSEvent::modifierFlags(event) };
|
||||||
|
|
||||||
let position = Point { x: point.x, y: point.y };
|
let position = Point { x: point.x, y: point.y };
|
||||||
|
|
||||||
state.trigger_event(Event::Mouse(MouseEvent::CursorMoved { position }));
|
state.trigger_event(Event::Mouse(MouseEvent::CursorMoved {
|
||||||
|
position,
|
||||||
|
modifiers: make_modifiers(modifiers),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,14 +29,14 @@ use winapi::shared::ntdef::SHORT;
|
||||||
use winapi::shared::windef::HWND;
|
use winapi::shared::windef::HWND;
|
||||||
use winapi::um::winuser::{
|
use winapi::um::winuser::{
|
||||||
GetKeyState, GetKeyboardLayout, MapVirtualKeyExW, PeekMessageW, ToUnicodeEx, MAPVK_VK_TO_CHAR,
|
GetKeyState, GetKeyboardLayout, MapVirtualKeyExW, PeekMessageW, ToUnicodeEx, MAPVK_VK_TO_CHAR,
|
||||||
MAPVK_VSC_TO_VK_EX, PM_NOREMOVE, VK_ACCEPT, VK_ADD, VK_APPS, VK_ATTN, VK_BACK, VK_BROWSER_BACK,
|
MAPVK_VSC_TO_VK_EX, MK_CONTROL, MK_SHIFT, PM_NOREMOVE, VK_ACCEPT, VK_ADD, VK_APPS, VK_ATTN,
|
||||||
VK_BROWSER_FAVORITES, VK_BROWSER_FORWARD, VK_BROWSER_HOME, VK_BROWSER_REFRESH,
|
VK_BACK, VK_BROWSER_BACK, VK_BROWSER_FAVORITES, VK_BROWSER_FORWARD, VK_BROWSER_HOME,
|
||||||
VK_BROWSER_SEARCH, VK_BROWSER_STOP, VK_CANCEL, VK_CAPITAL, VK_CLEAR, VK_CONTROL, VK_CONVERT,
|
VK_BROWSER_REFRESH, VK_BROWSER_SEARCH, VK_BROWSER_STOP, VK_CANCEL, VK_CAPITAL, VK_CLEAR,
|
||||||
VK_CRSEL, VK_DECIMAL, VK_DELETE, VK_DIVIDE, VK_DOWN, VK_END, VK_EREOF, VK_ESCAPE, VK_EXECUTE,
|
VK_CONTROL, VK_CONVERT, VK_CRSEL, VK_DECIMAL, VK_DELETE, VK_DIVIDE, VK_DOWN, VK_END, VK_EREOF,
|
||||||
VK_EXSEL, VK_F1, VK_F10, VK_F11, VK_F12, VK_F2, VK_F3, VK_F4, VK_F5, VK_F6, VK_F7, VK_F8,
|
VK_ESCAPE, VK_EXECUTE, VK_EXSEL, VK_F1, VK_F10, VK_F11, VK_F12, VK_F2, VK_F3, VK_F4, VK_F5,
|
||||||
VK_F9, VK_FINAL, VK_HELP, VK_HOME, VK_INSERT, VK_JUNJA, VK_KANA, VK_KANJI, VK_LAUNCH_APP1,
|
VK_F6, VK_F7, VK_F8, VK_F9, VK_FINAL, VK_HELP, VK_HOME, VK_INSERT, VK_JUNJA, VK_KANA, VK_KANJI,
|
||||||
VK_LAUNCH_APP2, VK_LAUNCH_MAIL, VK_LAUNCH_MEDIA_SELECT, VK_LCONTROL, VK_LEFT, VK_LMENU,
|
VK_LAUNCH_APP1, VK_LAUNCH_APP2, VK_LAUNCH_MAIL, VK_LAUNCH_MEDIA_SELECT, VK_LCONTROL, VK_LEFT,
|
||||||
VK_LSHIFT, VK_LWIN, VK_MEDIA_NEXT_TRACK, VK_MEDIA_PLAY_PAUSE, VK_MEDIA_PREV_TRACK,
|
VK_LMENU, VK_LSHIFT, VK_LWIN, VK_MEDIA_NEXT_TRACK, VK_MEDIA_PLAY_PAUSE, VK_MEDIA_PREV_TRACK,
|
||||||
VK_MEDIA_STOP, VK_MENU, VK_MODECHANGE, VK_MULTIPLY, VK_NEXT, VK_NONCONVERT, VK_NUMLOCK,
|
VK_MEDIA_STOP, VK_MENU, VK_MODECHANGE, VK_MULTIPLY, VK_NEXT, VK_NONCONVERT, VK_NUMLOCK,
|
||||||
VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7,
|
VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7,
|
||||||
VK_NUMPAD8, VK_NUMPAD9, VK_OEM_ATTN, VK_OEM_CLEAR, VK_PAUSE, VK_PLAY, VK_PRINT, VK_PRIOR,
|
VK_NUMPAD8, VK_NUMPAD9, VK_OEM_ATTN, VK_OEM_CLEAR, VK_PAUSE, VK_PLAY, VK_PRINT, VK_PRIOR,
|
||||||
|
@ -562,6 +562,30 @@ impl KeyboardState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The same as [Self::get_modifiers()], but it reads the Ctrl and Shift state from a mouse
|
||||||
|
/// event's wParam parameter. Saves two calls to [GetKeyState()].
|
||||||
|
pub(crate) fn get_modifiers_from_mouse_wparam(&self, wparam: WPARAM) -> Modifiers {
|
||||||
|
unsafe {
|
||||||
|
let mut modifiers = Modifiers::empty();
|
||||||
|
for &(vk, modifier, mask) in MODIFIER_MAP {
|
||||||
|
let modifier_active = match modifier {
|
||||||
|
Modifiers::CONTROL => wparam & MK_CONTROL != 0,
|
||||||
|
Modifiers::SHIFT => wparam & MK_SHIFT != 0,
|
||||||
|
_ => GetKeyState(vk) & mask != 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if modifier_active {
|
||||||
|
modifiers |= modifier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.has_altgr && GetKeyState(VK_RMENU) & 0x80 != 0 {
|
||||||
|
modifiers |= Modifiers::ALT_GRAPH;
|
||||||
|
modifiers &= !(Modifiers::CONTROL | Modifiers::ALT);
|
||||||
|
}
|
||||||
|
modifiers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Load a keyboard layout.
|
/// Load a keyboard layout.
|
||||||
///
|
///
|
||||||
/// We need to retain a map of virtual key codes in various modifier
|
/// We need to retain a map of virtual key codes in various modifier
|
||||||
|
|
|
@ -137,13 +137,14 @@ unsafe extern "system" fn wnd_proc(
|
||||||
let y = ((lparam >> 16) & 0xFFFF) as i16 as i32;
|
let y = ((lparam >> 16) & 0xFFFF) as i16 as i32;
|
||||||
|
|
||||||
let physical_pos = PhyPoint { x, y };
|
let physical_pos = PhyPoint { x, y };
|
||||||
|
|
||||||
let logical_pos = physical_pos.to_logical(&window_state.window_info);
|
let logical_pos = physical_pos.to_logical(&window_state.window_info);
|
||||||
|
let event = Event::Mouse(MouseEvent::CursorMoved {
|
||||||
|
position: logical_pos,
|
||||||
|
modifiers: window_state.keyboard_state.get_modifiers_from_mouse_wparam(wparam),
|
||||||
|
});
|
||||||
|
|
||||||
|
window_state.handler.on_event(&mut window, event);
|
||||||
|
|
||||||
window_state.handler.on_event(
|
|
||||||
&mut window,
|
|
||||||
Event::Mouse(MouseEvent::CursorMoved { position: logical_pos }),
|
|
||||||
);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
WM_MOUSEWHEEL => {
|
WM_MOUSEWHEEL => {
|
||||||
|
@ -155,13 +156,13 @@ unsafe extern "system" fn wnd_proc(
|
||||||
let value = value as i32;
|
let value = value as i32;
|
||||||
let value = value as f32 / WHEEL_DELTA as f32;
|
let value = value as f32 / WHEEL_DELTA as f32;
|
||||||
|
|
||||||
window_state.handler.on_event(
|
let event = Event::Mouse(MouseEvent::WheelScrolled {
|
||||||
&mut window,
|
delta: ScrollDelta::Lines { x: 0.0, y: value },
|
||||||
Event::Mouse(MouseEvent::WheelScrolled(ScrollDelta::Lines {
|
modifiers: window_state.keyboard_state.get_modifiers_from_mouse_wparam(wparam),
|
||||||
x: 0.0,
|
});
|
||||||
y: value,
|
|
||||||
})),
|
window_state.handler.on_event(&mut window, event);
|
||||||
);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
WM_LBUTTONDOWN | WM_LBUTTONUP | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_RBUTTONDOWN
|
WM_LBUTTONDOWN | WM_LBUTTONUP | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_RBUTTONDOWN
|
||||||
|
@ -190,7 +191,12 @@ unsafe extern "system" fn wnd_proc(
|
||||||
// Capture the mouse cursor on button down
|
// Capture the mouse cursor on button down
|
||||||
mouse_button_counter = mouse_button_counter.saturating_add(1);
|
mouse_button_counter = mouse_button_counter.saturating_add(1);
|
||||||
SetCapture(hwnd);
|
SetCapture(hwnd);
|
||||||
MouseEvent::ButtonPressed(button)
|
MouseEvent::ButtonPressed {
|
||||||
|
button,
|
||||||
|
modifiers: window_state
|
||||||
|
.keyboard_state
|
||||||
|
.get_modifiers_from_mouse_wparam(wparam),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
WM_LBUTTONUP | WM_MBUTTONUP | WM_RBUTTONUP | WM_XBUTTONUP => {
|
WM_LBUTTONUP | WM_MBUTTONUP | WM_RBUTTONUP | WM_XBUTTONUP => {
|
||||||
// Release the mouse cursor capture when all buttons are released
|
// Release the mouse cursor capture when all buttons are released
|
||||||
|
@ -199,7 +205,12 @@ unsafe extern "system" fn wnd_proc(
|
||||||
ReleaseCapture();
|
ReleaseCapture();
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseEvent::ButtonReleased(button)
|
MouseEvent::ButtonReleased {
|
||||||
|
button,
|
||||||
|
modifiers: window_state
|
||||||
|
.keyboard_state
|
||||||
|
.get_modifiers_from_mouse_wparam(wparam),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
|
|
@ -362,7 +362,7 @@ fn hardware_keycode_to_code(hw_keycode: u16) -> Code {
|
||||||
|
|
||||||
// Extracts the keyboard modifiers from, e.g., the `state` field of
|
// Extracts the keyboard modifiers from, e.g., the `state` field of
|
||||||
// `xcb::xproto::ButtonPressEvent`
|
// `xcb::xproto::ButtonPressEvent`
|
||||||
fn key_mods(mods: u16) -> Modifiers {
|
pub(super) fn key_mods(mods: u16) -> Modifiers {
|
||||||
let mut ret = Modifiers::default();
|
let mut ret = Modifiers::default();
|
||||||
let mut key_masks = [
|
let mut key_masks = [
|
||||||
(xproto::MOD_MASK_SHIFT, Modifiers::SHIFT),
|
(xproto::MOD_MASK_SHIFT, Modifiers::SHIFT),
|
||||||
|
|
|
@ -16,7 +16,7 @@ use crate::{
|
||||||
WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy,
|
WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::keyboard::{convert_key_press_event, convert_key_release_event};
|
use super::keyboard::{convert_key_press_event, convert_key_release_event, key_mods};
|
||||||
|
|
||||||
#[cfg(feature = "opengl")]
|
#[cfg(feature = "opengl")]
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -580,7 +580,10 @@ impl Window {
|
||||||
|
|
||||||
handler.on_event(
|
handler.on_event(
|
||||||
&mut crate::Window::new(self),
|
&mut crate::Window::new(self),
|
||||||
Event::Mouse(MouseEvent::CursorMoved { position: logical_pos }),
|
Event::Mouse(MouseEvent::CursorMoved {
|
||||||
|
position: logical_pos,
|
||||||
|
modifiers: key_mods(event.state()),
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -593,26 +596,29 @@ impl Window {
|
||||||
4 => {
|
4 => {
|
||||||
handler.on_event(
|
handler.on_event(
|
||||||
&mut crate::Window::new(self),
|
&mut crate::Window::new(self),
|
||||||
Event::Mouse(MouseEvent::WheelScrolled(ScrollDelta::Lines {
|
Event::Mouse(MouseEvent::WheelScrolled {
|
||||||
x: 0.0,
|
delta: ScrollDelta::Lines { x: 0.0, y: 1.0 },
|
||||||
y: 1.0,
|
modifiers: key_mods(event.state()),
|
||||||
})),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
5 => {
|
5 => {
|
||||||
handler.on_event(
|
handler.on_event(
|
||||||
&mut crate::Window::new(self),
|
&mut crate::Window::new(self),
|
||||||
Event::Mouse(MouseEvent::WheelScrolled(ScrollDelta::Lines {
|
Event::Mouse(MouseEvent::WheelScrolled {
|
||||||
x: 0.0,
|
delta: ScrollDelta::Lines { x: 0.0, y: -1.0 },
|
||||||
y: -1.0,
|
modifiers: key_mods(event.state()),
|
||||||
})),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
detail => {
|
detail => {
|
||||||
let button_id = mouse_id(detail);
|
let button_id = mouse_id(detail);
|
||||||
handler.on_event(
|
handler.on_event(
|
||||||
&mut crate::Window::new(self),
|
&mut crate::Window::new(self),
|
||||||
Event::Mouse(MouseEvent::ButtonPressed(button_id)),
|
Event::Mouse(MouseEvent::ButtonPressed {
|
||||||
|
button: button_id,
|
||||||
|
modifiers: key_mods(event.state()),
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -626,7 +632,10 @@ impl Window {
|
||||||
let button_id = mouse_id(detail);
|
let button_id = mouse_id(detail);
|
||||||
handler.on_event(
|
handler.on_event(
|
||||||
&mut crate::Window::new(self),
|
&mut crate::Window::new(self),
|
||||||
Event::Mouse(MouseEvent::ButtonReleased(button_id)),
|
Event::Mouse(MouseEvent::ButtonReleased {
|
||||||
|
button: button_id,
|
||||||
|
modifiers: key_mods(event.state()),
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue