winit/src/platform_impl/macos/app.rs
Mads Marquart e9a25a4c91
Replace remaining AppKit bindings with icrate's (#3296)
* Use icrate's window structs and enums

* Properly implement protocols

* Use icrate's NSWindow

We were previously using undocumented methods on `NSWindowTabGroup`

* Use icrate's NSApplication

And clean up some doc comments regarding NSApplication
2023-12-23 23:07:55 +01:00

106 lines
3.8 KiB
Rust

#![allow(clippy::unnecessary_cast)]
use icrate::AppKit::{
NSApplication, NSEvent, NSEventModifierFlagCommand, NSEventTypeKeyUp, NSEventTypeLeftMouseDown,
NSEventTypeLeftMouseDragged, NSEventTypeLeftMouseUp, NSEventTypeMouseMoved,
NSEventTypeOtherMouseDown, NSEventTypeOtherMouseDragged, NSEventTypeOtherMouseUp,
NSEventTypeRightMouseDown, NSEventTypeRightMouseDragged, NSEventTypeRightMouseUp, NSResponder,
};
use icrate::Foundation::NSObject;
use objc2::{declare_class, msg_send, mutability, ClassType, DeclaredClass};
use super::event::flags_contains;
use super::{app_state::AppState, DEVICE_ID};
use crate::event::{DeviceEvent, ElementState, Event};
declare_class!(
pub(super) struct WinitApplication;
unsafe impl ClassType for WinitApplication {
#[inherits(NSResponder, NSObject)]
type Super = NSApplication;
type Mutability = mutability::MainThreadOnly;
const NAME: &'static str = "WinitApplication";
}
impl DeclaredClass for WinitApplication {}
unsafe impl WinitApplication {
// Normally, holding Cmd + any key never sends us a `keyUp` event for that key.
// Overriding `sendEvent:` like this fixes that. (https://stackoverflow.com/a/15294196)
// Fun fact: Firefox still has this bug! (https://bugzilla.mozilla.org/show_bug.cgi?id=1299553)
#[method(sendEvent:)]
fn send_event(&self, event: &NSEvent) {
// For posterity, there are some undocumented event types
// (https://github.com/servo/cocoa-rs/issues/155)
// but that doesn't really matter here.
let event_type = unsafe { event.r#type() };
let modifier_flags = unsafe { event.modifierFlags() };
if event_type == NSEventTypeKeyUp
&& flags_contains(modifier_flags, NSEventModifierFlagCommand)
{
if let Some(key_window) = self.keyWindow() {
key_window.sendEvent(event);
}
} else {
maybe_dispatch_device_event(event);
unsafe { msg_send![super(self), sendEvent: event] }
}
}
}
);
fn maybe_dispatch_device_event(event: &NSEvent) {
let event_type = unsafe { event.r#type() };
#[allow(non_upper_case_globals)]
match event_type {
NSEventTypeMouseMoved
| NSEventTypeLeftMouseDragged
| NSEventTypeOtherMouseDragged
| NSEventTypeRightMouseDragged => {
let delta_x = unsafe { event.deltaX() } as f64;
let delta_y = unsafe { event.deltaY() } as f64;
if delta_x != 0.0 {
queue_device_event(DeviceEvent::Motion {
axis: 0,
value: delta_x,
});
}
if delta_y != 0.0 {
queue_device_event(DeviceEvent::Motion {
axis: 1,
value: delta_y,
})
}
if delta_x != 0.0 || delta_y != 0.0 {
queue_device_event(DeviceEvent::MouseMotion {
delta: (delta_x, delta_y),
});
}
}
NSEventTypeLeftMouseDown | NSEventTypeRightMouseDown | NSEventTypeOtherMouseDown => {
queue_device_event(DeviceEvent::Button {
button: unsafe { event.buttonNumber() } as u32,
state: ElementState::Pressed,
});
}
NSEventTypeLeftMouseUp | NSEventTypeRightMouseUp | NSEventTypeOtherMouseUp => {
queue_device_event(DeviceEvent::Button {
button: unsafe { event.buttonNumber() } as u32,
state: ElementState::Released,
});
}
_ => (),
}
}
fn queue_device_event(event: DeviceEvent) {
let event = Event::DeviceEvent {
device_id: DEVICE_ID,
event,
};
AppState::queue_event(event);
}