mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 22:53:15 -04:00
internal(macOS) use NSTrackingArea instead of trackingRect (#4514)
This commit is contained in:
@@ -51,6 +51,7 @@ objc2-app-kit = { workspace = true, features = [
|
|||||||
"NSScreen",
|
"NSScreen",
|
||||||
"NSTextInputClient",
|
"NSTextInputClient",
|
||||||
"NSTextInputContext",
|
"NSTextInputContext",
|
||||||
|
"NSTrackingArea",
|
||||||
"NSToolbar",
|
"NSToolbar",
|
||||||
"NSView",
|
"NSView",
|
||||||
"NSWindow",
|
"NSWindow",
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
#![allow(clippy::unnecessary_cast)]
|
#![allow(clippy::unnecessary_cast)]
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
use std::ptr;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use dpi::{LogicalPosition, LogicalSize};
|
use dpi::{LogicalPosition, LogicalSize};
|
||||||
use objc2::rc::Retained;
|
use objc2::rc::Retained;
|
||||||
use objc2::runtime::{AnyObject, Sel};
|
use objc2::runtime::{AnyObject, Sel};
|
||||||
use objc2::{DefinedClass, MainThreadMarker, define_class, msg_send};
|
use objc2::{AnyThread, DefinedClass, MainThreadMarker, define_class, msg_send};
|
||||||
use objc2_app_kit::{
|
use objc2_app_kit::{
|
||||||
NSApplication, NSCursor, NSEvent, NSEventPhase, NSResponder, NSTextInputClient,
|
NSApplication, NSCursor, NSEvent, NSEventPhase, NSResponder, NSTextInputClient, NSTrackingArea,
|
||||||
NSTrackingRectTag, NSView, NSWindow,
|
NSTrackingAreaOptions, NSView, NSWindow,
|
||||||
};
|
};
|
||||||
use objc2_core_foundation::CGRect;
|
use objc2_core_foundation::CGRect;
|
||||||
use objc2_foundation::{
|
use objc2_foundation::{
|
||||||
@@ -119,7 +118,6 @@ pub struct ViewState {
|
|||||||
ime_size: Cell<NSSize>,
|
ime_size: Cell<NSSize>,
|
||||||
modifiers: Cell<Modifiers>,
|
modifiers: Cell<Modifiers>,
|
||||||
phys_modifiers: RefCell<HashMap<Key, ModLocationMask>>,
|
phys_modifiers: RefCell<HashMap<Key, ModLocationMask>>,
|
||||||
tracking_rect: Cell<Option<NSTrackingRectTag>>,
|
|
||||||
ime_state: Cell<ImeState>,
|
ime_state: Cell<ImeState>,
|
||||||
input_source: RefCell<String>,
|
input_source: RefCell<String>,
|
||||||
|
|
||||||
@@ -131,7 +129,6 @@ pub struct ViewState {
|
|||||||
/// True if the current key event should be forwarded
|
/// True if the current key event should be forwarded
|
||||||
/// to the application, even during IME
|
/// to the application, even during IME
|
||||||
forward_key_to_app: Cell<bool>,
|
forward_key_to_app: Cell<bool>,
|
||||||
|
|
||||||
marked_text: RefCell<Retained<NSMutableAttributedString>>,
|
marked_text: RefCell<Retained<NSMutableAttributedString>>,
|
||||||
accepts_first_mouse: bool,
|
accepts_first_mouse: bool,
|
||||||
|
|
||||||
@@ -153,41 +150,17 @@ define_class!(
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(method(viewDidMoveToWindow))]
|
|
||||||
fn view_did_move_to_window(&self) {
|
|
||||||
trace_scope!("viewDidMoveToWindow");
|
|
||||||
if let Some(tracking_rect) = self.ivars().tracking_rect.take() {
|
|
||||||
self.removeTrackingRect(tracking_rect);
|
|
||||||
}
|
|
||||||
|
|
||||||
let rect = self.frame();
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not a normal method on `NSView`, it's triggered by `NSViewFrameDidChangeNotification`.
|
// Not a normal method on `NSView`, it's triggered by `NSViewFrameDidChangeNotification`.
|
||||||
#[unsafe(method(viewFrameDidChangeNotification:))]
|
#[unsafe(method(viewFrameDidChangeNotification:))]
|
||||||
fn frame_did_change(&self, _notification: Option<&AnyObject>) {
|
fn frame_did_change(&self, _notification: Option<&AnyObject>) {
|
||||||
trace_scope!("NSViewFrameDidChangeNotification");
|
trace_scope!("NSViewFrameDidChangeNotification");
|
||||||
if let Some(tracking_rect) = self.ivars().tracking_rect.take() {
|
|
||||||
self.removeTrackingRect(tracking_rect);
|
|
||||||
}
|
|
||||||
|
|
||||||
let rect = self.frame();
|
|
||||||
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:
|
// Emit resize event here rather than from windowDidResize because:
|
||||||
// 1. When a new window is created as a tab, the frame size may change without a window
|
// 1. When a new window is created as a tab, the frame size may change without a window
|
||||||
// resize occurring.
|
// resize occurring.
|
||||||
// 2. Even when a window resize does occur on a new tabbed window, it contains the wrong
|
// 2. Even when a window resize does occur on a new tabbed window, it contains the wrong
|
||||||
// size (includes tab height).
|
// size (includes tab height).
|
||||||
|
let rect = self.frame();
|
||||||
let logical_size = LogicalSize::new(rect.size.width as f64, rect.size.height as f64);
|
let logical_size = LogicalSize::new(rect.size.width as f64, rect.size.height as f64);
|
||||||
let size = logical_size.to_physical::<u32>(self.scale_factor());
|
let size = logical_size.to_physical::<u32>(self.scale_factor());
|
||||||
self.queue_event(WindowEvent::SurfaceResized(size));
|
self.queue_event(WindowEvent::SurfaceResized(size));
|
||||||
@@ -808,7 +781,6 @@ impl WinitView {
|
|||||||
ime_size: Default::default(),
|
ime_size: Default::default(),
|
||||||
modifiers: Default::default(),
|
modifiers: Default::default(),
|
||||||
phys_modifiers: Default::default(),
|
phys_modifiers: Default::default(),
|
||||||
tracking_rect: Default::default(),
|
|
||||||
ime_state: Default::default(),
|
ime_state: Default::default(),
|
||||||
input_source: Default::default(),
|
input_source: Default::default(),
|
||||||
ime_capabilities: Default::default(),
|
ime_capabilities: Default::default(),
|
||||||
@@ -818,9 +790,52 @@ impl WinitView {
|
|||||||
option_as_alt: Cell::new(option_as_alt),
|
option_as_alt: Cell::new(option_as_alt),
|
||||||
});
|
});
|
||||||
let this: Retained<Self> = unsafe { msg_send![super(this), init] };
|
let this: Retained<Self> = unsafe { msg_send![super(this), init] };
|
||||||
|
|
||||||
*this.ivars().input_source.borrow_mut() = this.current_input_source();
|
*this.ivars().input_source.borrow_mut() = this.current_input_source();
|
||||||
|
|
||||||
|
// `MouseEnteredAndExited` enables receiving events through `mouseEntered:` and
|
||||||
|
// `mouseExited:`.
|
||||||
|
//
|
||||||
|
// `MouseMoved` enables receiving events through `mouseMoved:`
|
||||||
|
//
|
||||||
|
// We do not set `CursorUpdate` because it is part of the "flexible" alternative to
|
||||||
|
// `cursorRect` based cursor image updates, and we currently still use
|
||||||
|
// `cursorRect`s. We also can't really switch to this approach because "The
|
||||||
|
// cursorUpdate(with:) message is not sent when the NSTrackingCursorUpdate option is
|
||||||
|
// specified along with [`ActiveAlways`]."
|
||||||
|
//
|
||||||
|
// `ActiveAlways` indicates we want to receive events when the window is not
|
||||||
|
// focused ("key window" in Cocoa terms), which matches the behavior on other
|
||||||
|
// platforms.
|
||||||
|
//
|
||||||
|
// We do not set `AssumeInside` because we want to avoid emitting `Left` events without a
|
||||||
|
// correspondering `Entered` to our consumers, and not setting this flag tells AppKit to
|
||||||
|
// handle this for us by synthesizing entry and exit events in some cases.
|
||||||
|
//
|
||||||
|
// `InVisibleRect` instructs the tracking area's `owner` (our `NSView`) to ignore the value
|
||||||
|
// we provide in `rect` and keep the tracking area's bounds up to date with the
|
||||||
|
// current view bounds automatically.
|
||||||
|
//
|
||||||
|
// We do not set `EnabledDuringMouseDrag` to match the platform behavior on Windows
|
||||||
|
// and Wayland, since neither emit events while being dragged over with an empty
|
||||||
|
// cursor without focus.
|
||||||
|
//
|
||||||
|
// See also https://developer.apple.com/documentation/appkit/nstrackingareaoptions.
|
||||||
|
|
||||||
|
// Safety: the type of `owner` should be `NSView` and is.
|
||||||
|
// The type of `user_info` is irrelevant because it is None.
|
||||||
|
this.addTrackingArea(&*unsafe {
|
||||||
|
NSTrackingArea::initWithRect_options_owner_userInfo(
|
||||||
|
NSTrackingArea::alloc(),
|
||||||
|
NSRect::ZERO,
|
||||||
|
NSTrackingAreaOptions::MouseEnteredAndExited
|
||||||
|
| NSTrackingAreaOptions::MouseMoved
|
||||||
|
| NSTrackingAreaOptions::ActiveAlways
|
||||||
|
| NSTrackingAreaOptions::InVisibleRect,
|
||||||
|
Some(&this),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -156,6 +156,8 @@ pub enum WindowEvent {
|
|||||||
Ime(Ime),
|
Ime(Ime),
|
||||||
|
|
||||||
/// The pointer has moved on the window.
|
/// The pointer has moved on the window.
|
||||||
|
///
|
||||||
|
/// Should be emitted regardless of window focus.
|
||||||
PointerMoved {
|
PointerMoved {
|
||||||
device_id: Option<DeviceId>,
|
device_id: Option<DeviceId>,
|
||||||
|
|
||||||
@@ -184,6 +186,8 @@ pub enum WindowEvent {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/// The pointer has entered the window.
|
/// The pointer has entered the window.
|
||||||
|
///
|
||||||
|
/// Should be emitted regardless of window focus.
|
||||||
PointerEntered {
|
PointerEntered {
|
||||||
device_id: Option<DeviceId>,
|
device_id: Option<DeviceId>,
|
||||||
|
|
||||||
@@ -209,6 +213,8 @@ pub enum WindowEvent {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/// The pointer has left the window.
|
/// The pointer has left the window.
|
||||||
|
///
|
||||||
|
/// Should be emitted regardless of window focus.
|
||||||
PointerLeft {
|
PointerLeft {
|
||||||
device_id: Option<DeviceId>,
|
device_id: Option<DeviceId>,
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ changelog entry.
|
|||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Updated `windows-sys` to `v0.61`.
|
- Updated `windows-sys` to `v0.61`.
|
||||||
|
- On older macOS versions (tested up to 12.7.6), applications now receive mouse movement events for unfocused windows, matching the behavior on other platforms.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user