Partially replace custom AppKit bindings with icrate's autogenerated bindings (#2982)
* Refactor winit-specific cursor logic out of appkit module * Add relevant AppKit features that we depend on * Use icrate's NSImageRep and NSBitmapImageRep * Use icrate's NSImage * Use icrate's NSCursor * Use icrate's NSAppearance * Use icrate's NSScreen * Use icrate's NSButton * Use icrate's NSAppKitVersionNumber * Use icrate's NSTextInputContext * Use icrate's NSColor * Use icrate's NSEvent * Use icrate's NSMenu and NSMenuItem * Use icrate's NSPasteboard * Use icrate's NSResponder * Use icrate's NSTextInputClient * Use icrate's NSView
This commit is contained in:
parent
7d5bee767c
commit
674657efb6
32 changed files with 547 additions and 1362 deletions
|
|
@ -1,10 +1,17 @@
|
|||
#![allow(clippy::unnecessary_cast)]
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::ptr;
|
||||
|
||||
use icrate::AppKit::{
|
||||
NSCursor, NSEvent, NSEventPhaseBegan, NSEventPhaseCancelled, NSEventPhaseChanged,
|
||||
NSEventPhaseEnded, NSEventPhaseMayBegin, NSResponder, NSTextInputClient, NSTrackingRectTag,
|
||||
NSView,
|
||||
};
|
||||
use icrate::Foundation::{
|
||||
NSArray, NSAttributedString, NSAttributedStringKey, NSCopying, NSMutableAttributedString,
|
||||
NSObject, NSObjectProtocol, NSPoint, NSRange, NSRect, NSSize, NSString, NSUInteger,
|
||||
MainThreadMarker, NSArray, NSAttributedString, NSAttributedStringKey, NSCopying,
|
||||
NSMutableAttributedString, NSObject, NSObjectProtocol, NSPoint, NSRange, NSRect, NSSize,
|
||||
NSString, NSUInteger,
|
||||
};
|
||||
use objc2::rc::{Id, WeakId};
|
||||
use objc2::runtime::{AnyObject, Sel};
|
||||
|
|
@ -12,11 +19,10 @@ use objc2::{
|
|||
class, declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass,
|
||||
};
|
||||
|
||||
use super::cursor::{default_cursor, invisible_cursor};
|
||||
use super::event::{lalt_pressed, ralt_pressed};
|
||||
use super::{
|
||||
appkit::{
|
||||
NSApp, NSCursor, NSEvent, NSEventPhase, NSResponder, NSTextInputClient, NSTrackingRectTag,
|
||||
NSView,
|
||||
},
|
||||
appkit::NSApp,
|
||||
event::{code_to_key, code_to_location},
|
||||
};
|
||||
use crate::{
|
||||
|
|
@ -48,7 +54,7 @@ impl Default for CursorState {
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
visible: true,
|
||||
cursor: Default::default(),
|
||||
cursor: default_cursor(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -150,7 +156,7 @@ declare_class!(
|
|||
unsafe impl ClassType for WinitView {
|
||||
#[inherits(NSResponder, NSObject)]
|
||||
type Super = NSView;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
type Mutability = mutability::MainThreadOnly;
|
||||
const NAME: &'static str = "WinitView";
|
||||
}
|
||||
|
||||
|
|
@ -167,7 +173,8 @@ declare_class!(
|
|||
}
|
||||
|
||||
let rect = self.frame();
|
||||
let tracking_rect = self.add_tracking_rect(rect, false);
|
||||
let tracking_rect = unsafe { self.addTrackingRect_owner_userData_assumeInside(rect, self, ptr::null_mut(), false) };
|
||||
assert_ne!(tracking_rect, 0, "failed adding tracking rect");
|
||||
self.ivars().tracking_rect.set(Some(tracking_rect));
|
||||
}
|
||||
|
||||
|
|
@ -179,7 +186,8 @@ declare_class!(
|
|||
}
|
||||
|
||||
let rect = self.frame();
|
||||
let tracking_rect = self.add_tracking_rect(rect, false);
|
||||
let tracking_rect = unsafe { self.addTrackingRect_owner_userData_assumeInside(rect, self, ptr::null_mut(), false) };
|
||||
assert_ne!(tracking_rect, 0, "failed adding tracking rect");
|
||||
self.ivars().tracking_rect.set(Some(tracking_rect));
|
||||
|
||||
// Emit resize event here rather than from windowDidResize because:
|
||||
|
|
@ -227,9 +235,9 @@ declare_class!(
|
|||
let cursor_state = self.ivars().cursor_state.borrow();
|
||||
// We correctly invoke `addCursorRect` only from inside `resetCursorRects`
|
||||
if cursor_state.visible {
|
||||
self.addCursorRect(bounds, &cursor_state.cursor);
|
||||
self.addCursorRect_cursor(bounds, &cursor_state.cursor);
|
||||
} else {
|
||||
self.addCursorRect(bounds, &NSCursor::invisible());
|
||||
self.addCursorRect_cursor(bounds, &invisible_cursor());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -295,7 +303,7 @@ declare_class!(
|
|||
self.queue_event(WindowEvent::Ime(Ime::Enabled));
|
||||
}
|
||||
|
||||
if self.hasMarkedText() {
|
||||
if unsafe { self.hasMarkedText() } {
|
||||
self.ivars().ime_state.set(ImeState::Preedit);
|
||||
} else {
|
||||
// In case the preedit was cleared, set IME into the Ground state.
|
||||
|
|
@ -388,7 +396,7 @@ declare_class!(
|
|||
let is_control = string.chars().next().map_or(false, |c| c.is_control());
|
||||
|
||||
// Commit only if we have marked text.
|
||||
if self.hasMarkedText() && self.is_ime_enabled() && !is_control {
|
||||
if unsafe { self.hasMarkedText() } && self.is_ime_enabled() && !is_control {
|
||||
self.queue_event(WindowEvent::Ime(Ime::Preedit(String::new(), None)));
|
||||
self.queue_event(WindowEvent::Ime(Ime::Commit(string)));
|
||||
self.ivars().ime_state.set(ImeState::Commited);
|
||||
|
|
@ -409,7 +417,7 @@ declare_class!(
|
|||
|
||||
self.ivars().forward_key_to_app.set(true);
|
||||
|
||||
if self.hasMarkedText() && self.ivars().ime_state.get() == ImeState::Preedit {
|
||||
if unsafe { self.hasMarkedText() } && self.ivars().ime_state.get() == ImeState::Preedit {
|
||||
// Leave preedit so that we also report the key-up for this key.
|
||||
self.ivars().ime_state.set(ImeState::Ground);
|
||||
}
|
||||
|
|
@ -467,7 +475,7 @@ declare_class!(
|
|||
};
|
||||
|
||||
if !had_ime_input || self.ivars().forward_key_to_app.get() {
|
||||
let key_event = create_key_event(&event, true, event.is_a_repeat(), None);
|
||||
let key_event = create_key_event(&event, true, unsafe { event.isARepeat() }, None);
|
||||
self.queue_event(WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
event: key_event,
|
||||
|
|
@ -536,7 +544,7 @@ declare_class!(
|
|||
.expect("could not find current event");
|
||||
|
||||
self.update_modifiers(&event, false);
|
||||
let event = create_key_event(&event, true, event.is_a_repeat(), None);
|
||||
let event = create_key_event(&event, true, unsafe { event.isARepeat() }, None);
|
||||
|
||||
self.queue_event(WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
|
|
@ -633,8 +641,8 @@ declare_class!(
|
|||
self.mouse_motion(event);
|
||||
|
||||
let delta = {
|
||||
let (x, y) = (event.scrollingDeltaX(), event.scrollingDeltaY());
|
||||
if event.hasPreciseScrollingDeltas() {
|
||||
let (x, y) = unsafe { (event.scrollingDeltaX(), event.scrollingDeltaY()) };
|
||||
if unsafe { event.hasPreciseScrollingDeltas() } {
|
||||
let delta = LogicalPosition::new(x, y).to_physical(self.scale_factor());
|
||||
MouseScrollDelta::PixelDelta(delta)
|
||||
} else {
|
||||
|
|
@ -646,18 +654,19 @@ declare_class!(
|
|||
// be mutually exclusive anyhow, which is why the API is rather incoherent). If no momentum
|
||||
// phase is recorded (or rather, the started/ended cases of the momentum phase) then we
|
||||
// report the touch phase.
|
||||
let phase = match event.momentumPhase() {
|
||||
NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => {
|
||||
#[allow(non_upper_case_globals)]
|
||||
let phase = match unsafe { event.momentumPhase() } {
|
||||
NSEventPhaseMayBegin | NSEventPhaseBegan => {
|
||||
TouchPhase::Started
|
||||
}
|
||||
NSEventPhase::NSEventPhaseEnded | NSEventPhase::NSEventPhaseCancelled => {
|
||||
NSEventPhaseEnded | NSEventPhaseCancelled => {
|
||||
TouchPhase::Ended
|
||||
}
|
||||
_ => match event.phase() {
|
||||
NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => {
|
||||
_ => match unsafe { event.phase() } {
|
||||
NSEventPhaseMayBegin | NSEventPhaseBegan => {
|
||||
TouchPhase::Started
|
||||
}
|
||||
NSEventPhase::NSEventPhaseEnded | NSEventPhase::NSEventPhaseCancelled => {
|
||||
NSEventPhaseEnded | NSEventPhaseCancelled => {
|
||||
TouchPhase::Ended
|
||||
}
|
||||
_ => TouchPhase::Moved,
|
||||
|
|
@ -678,17 +687,18 @@ declare_class!(
|
|||
fn magnify_with_event(&self, event: &NSEvent) {
|
||||
trace_scope!("magnifyWithEvent:");
|
||||
|
||||
let phase = match event.phase() {
|
||||
NSEventPhase::NSEventPhaseBegan => TouchPhase::Started,
|
||||
NSEventPhase::NSEventPhaseChanged => TouchPhase::Moved,
|
||||
NSEventPhase::NSEventPhaseCancelled => TouchPhase::Cancelled,
|
||||
NSEventPhase::NSEventPhaseEnded => TouchPhase::Ended,
|
||||
#[allow(non_upper_case_globals)]
|
||||
let phase = match unsafe { event.phase() } {
|
||||
NSEventPhaseBegan => TouchPhase::Started,
|
||||
NSEventPhaseChanged => TouchPhase::Moved,
|
||||
NSEventPhaseCancelled => TouchPhase::Cancelled,
|
||||
NSEventPhaseEnded => TouchPhase::Ended,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
self.queue_event(WindowEvent::TouchpadMagnify {
|
||||
device_id: DEVICE_ID,
|
||||
delta: event.magnification(),
|
||||
delta: unsafe { event.magnification() },
|
||||
phase,
|
||||
});
|
||||
}
|
||||
|
|
@ -706,17 +716,18 @@ declare_class!(
|
|||
fn rotate_with_event(&self, event: &NSEvent) {
|
||||
trace_scope!("rotateWithEvent:");
|
||||
|
||||
let phase = match event.phase() {
|
||||
NSEventPhase::NSEventPhaseBegan => TouchPhase::Started,
|
||||
NSEventPhase::NSEventPhaseChanged => TouchPhase::Moved,
|
||||
NSEventPhase::NSEventPhaseCancelled => TouchPhase::Cancelled,
|
||||
NSEventPhase::NSEventPhaseEnded => TouchPhase::Ended,
|
||||
#[allow(non_upper_case_globals)]
|
||||
let phase = match unsafe { event.phase() } {
|
||||
NSEventPhaseBegan => TouchPhase::Started,
|
||||
NSEventPhaseChanged => TouchPhase::Moved,
|
||||
NSEventPhaseCancelled => TouchPhase::Cancelled,
|
||||
NSEventPhaseEnded => TouchPhase::Ended,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
self.queue_event(WindowEvent::TouchpadRotate {
|
||||
device_id: DEVICE_ID,
|
||||
delta: event.rotation(),
|
||||
delta: unsafe { event.rotation() },
|
||||
phase,
|
||||
});
|
||||
}
|
||||
|
|
@ -729,8 +740,8 @@ declare_class!(
|
|||
|
||||
self.queue_event(WindowEvent::TouchpadPressure {
|
||||
device_id: DEVICE_ID,
|
||||
pressure: event.pressure(),
|
||||
stage: event.stage() as i64,
|
||||
pressure: unsafe { event.pressure() },
|
||||
stage: unsafe { event.stage() } as i64,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -753,7 +764,8 @@ declare_class!(
|
|||
|
||||
impl WinitView {
|
||||
pub(super) fn new(window: &WinitWindow, accepts_first_mouse: bool) -> Id<Self> {
|
||||
let this = Self::alloc().set_ivars(ViewState {
|
||||
let mtm = MainThreadMarker::from(window);
|
||||
let this = mtm.alloc().set_ivars(ViewState {
|
||||
accepts_first_mouse,
|
||||
_ns_window: WeakId::new(&window.retain()),
|
||||
..Default::default()
|
||||
|
|
@ -902,8 +914,8 @@ impl WinitView {
|
|||
// later will work though, since the flags are attached to the event and contain valid
|
||||
// information.
|
||||
'send_event: {
|
||||
if is_flags_changed_event && ns_event.key_code() != 0 {
|
||||
let scancode = ns_event.key_code();
|
||||
if is_flags_changed_event && unsafe { ns_event.keyCode() } != 0 {
|
||||
let scancode = unsafe { ns_event.keyCode() };
|
||||
let physical_key = PhysicalKey::from_scancode(scancode as u32);
|
||||
|
||||
// We'll correct the `is_press` later.
|
||||
|
|
@ -1014,7 +1026,7 @@ impl WinitView {
|
|||
}
|
||||
|
||||
fn mouse_motion(&self, event: &NSEvent) {
|
||||
let window_point = event.locationInWindow();
|
||||
let window_point = unsafe { event.locationInWindow() };
|
||||
let view_point = self.convertPoint_fromView(window_point, None);
|
||||
let view_rect = self.frame();
|
||||
|
||||
|
|
@ -1023,7 +1035,7 @@ impl WinitView {
|
|||
|| view_point.x > view_rect.size.width
|
||||
|| view_point.y > view_rect.size.height
|
||||
{
|
||||
let mouse_buttons_down = NSEvent::pressedMouseButtons();
|
||||
let mouse_buttons_down = unsafe { NSEvent::pressedMouseButtons() };
|
||||
if mouse_buttons_down == 0 {
|
||||
// Point is outside of the client area (view) and no buttons are pressed
|
||||
return;
|
||||
|
|
@ -1050,7 +1062,7 @@ fn mouse_button(event: &NSEvent) -> MouseButton {
|
|||
// For the other events, it's always set to 0.
|
||||
// MacOS only defines the left, right and middle buttons, 3..=31 are left as generic buttons,
|
||||
// but 3 and 4 are very commonly used as Back and Forward by hardware vendors and applications.
|
||||
match event.buttonNumber() {
|
||||
match unsafe { event.buttonNumber() } {
|
||||
0 => MouseButton::Left,
|
||||
1 => MouseButton::Right,
|
||||
2 => MouseButton::Middle,
|
||||
|
|
@ -1066,30 +1078,35 @@ fn mouse_button(event: &NSEvent) -> MouseButton {
|
|||
fn replace_event(event: &NSEvent, option_as_alt: OptionAsAlt) -> Id<NSEvent> {
|
||||
let ev_mods = event_mods(event).state;
|
||||
let ignore_alt_characters = match option_as_alt {
|
||||
OptionAsAlt::OnlyLeft if event.lalt_pressed() => true,
|
||||
OptionAsAlt::OnlyRight if event.ralt_pressed() => true,
|
||||
OptionAsAlt::OnlyLeft if lalt_pressed(event) => true,
|
||||
OptionAsAlt::OnlyRight if ralt_pressed(event) => true,
|
||||
OptionAsAlt::Both if ev_mods.alt_key() => true,
|
||||
_ => false,
|
||||
} && !ev_mods.control_key()
|
||||
&& !ev_mods.super_key();
|
||||
|
||||
if ignore_alt_characters {
|
||||
let ns_chars = event
|
||||
.charactersIgnoringModifiers()
|
||||
.expect("expected characters to be non-null");
|
||||
let ns_chars = unsafe {
|
||||
event
|
||||
.charactersIgnoringModifiers()
|
||||
.expect("expected characters to be non-null")
|
||||
};
|
||||
|
||||
NSEvent::keyEventWithType(
|
||||
event.type_(),
|
||||
event.locationInWindow(),
|
||||
event.modifierFlags(),
|
||||
event.timestamp(),
|
||||
event.window_number(),
|
||||
None,
|
||||
&ns_chars,
|
||||
&ns_chars,
|
||||
event.is_a_repeat(),
|
||||
event.key_code(),
|
||||
)
|
||||
unsafe {
|
||||
NSEvent::keyEventWithType_location_modifierFlags_timestamp_windowNumber_context_characters_charactersIgnoringModifiers_isARepeat_keyCode(
|
||||
event.r#type(),
|
||||
event.locationInWindow(),
|
||||
event.modifierFlags(),
|
||||
event.timestamp(),
|
||||
event.windowNumber(),
|
||||
None,
|
||||
&ns_chars,
|
||||
&ns_chars,
|
||||
event.isARepeat(),
|
||||
event.keyCode(),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
} else {
|
||||
event.copy()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue