Update to objc2 v0.6 (#4092)
* Use available! macro * Use objc2-core-foundation and objc2-core-graphics * Use MainThreadBound instead of StaticMainThreadBound hack
This commit is contained in:
parent
f5dcd2aabe
commit
953d9b4268
25 changed files with 787 additions and 1103 deletions
|
|
@ -6,10 +6,12 @@ use std::ptr;
|
|||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use core_graphics::display::CGDisplay;
|
||||
use objc2::rc::{autoreleasepool, Retained};
|
||||
use objc2::runtime::{AnyObject, ProtocolObject};
|
||||
use objc2::{declare_class, msg_send_id, mutability, sel, ClassType, DeclaredClass};
|
||||
use objc2::{
|
||||
available, define_class, msg_send, sel, ClassType, DefinedClass, MainThreadMarker,
|
||||
MainThreadOnly, Message,
|
||||
};
|
||||
use objc2_app_kit::{
|
||||
NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSAppearanceCustomization,
|
||||
NSAppearanceNameAqua, NSApplication, NSApplicationPresentationOptions, NSBackingStoreType,
|
||||
|
|
@ -19,11 +21,17 @@ use objc2_app_kit::{
|
|||
NSWindowOcclusionState, NSWindowOrderingMode, NSWindowSharingType, NSWindowStyleMask,
|
||||
NSWindowTabbingMode, NSWindowTitleVisibility, NSWindowToolbarStyle,
|
||||
};
|
||||
use objc2_core_foundation::{CGFloat, CGPoint};
|
||||
use objc2_core_graphics::{
|
||||
CGAcquireDisplayFadeReservation, CGAssociateMouseAndMouseCursorPosition, CGDisplayCapture,
|
||||
CGDisplayFade, CGDisplayRelease, CGDisplaySetDisplayMode, CGReleaseDisplayFadeReservation,
|
||||
CGRestorePermanentDisplayConfiguration, CGShieldingWindowLevel, CGWarpMouseCursorPosition,
|
||||
};
|
||||
use objc2_foundation::{
|
||||
ns_string, CGFloat, MainThreadMarker, NSArray, NSCopying, NSDictionary, NSEdgeInsets,
|
||||
NSKeyValueChangeKey, NSKeyValueChangeNewKey, NSKeyValueChangeOldKey,
|
||||
NSKeyValueObservingOptions, NSNotificationCenter, NSObject, NSObjectNSDelayedPerforming,
|
||||
NSObjectNSKeyValueObserverRegistration, NSObjectProtocol, NSPoint, NSRect, NSSize, NSString,
|
||||
ns_string, NSArray, NSDictionary, NSEdgeInsets, NSKeyValueChangeKey, NSKeyValueChangeNewKey,
|
||||
NSKeyValueChangeOldKey, NSKeyValueObservingOptions, NSNotificationCenter, NSObject,
|
||||
NSObjectNSDelayedPerforming, NSObjectNSKeyValueObserverRegistration, NSObjectProtocol, NSPoint,
|
||||
NSRect, NSSize, NSString,
|
||||
};
|
||||
use tracing::{trace, warn};
|
||||
|
||||
|
|
@ -31,6 +39,7 @@ use super::app_state::AppState;
|
|||
use super::cursor::cursor_from_icon;
|
||||
use super::monitor::{self, flip_window_screen_coordinates, get_display_id};
|
||||
use super::observer::RunLoop;
|
||||
use super::util::cgerr;
|
||||
use super::view::WinitView;
|
||||
use super::window::{window_id, WinitPanel, WinitWindow};
|
||||
use super::{ffi, Fullscreen, MonitorHandle};
|
||||
|
|
@ -133,30 +142,24 @@ pub(crate) struct State {
|
|||
is_borderless_game: Cell<bool>,
|
||||
}
|
||||
|
||||
declare_class!(
|
||||
define_class!(
|
||||
#[unsafe(super(NSObject))]
|
||||
#[thread_kind = MainThreadOnly]
|
||||
#[name = "WinitWindowDelegate"]
|
||||
#[ivars = State]
|
||||
pub(crate) struct WindowDelegate;
|
||||
|
||||
unsafe impl ClassType for WindowDelegate {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::MainThreadOnly;
|
||||
const NAME: &'static str = "WinitWindowDelegate";
|
||||
}
|
||||
|
||||
impl DeclaredClass for WindowDelegate {
|
||||
type Ivars = State;
|
||||
}
|
||||
|
||||
unsafe impl NSObjectProtocol for WindowDelegate {}
|
||||
|
||||
unsafe impl NSWindowDelegate for WindowDelegate {
|
||||
#[method(windowShouldClose:)]
|
||||
#[unsafe(method(windowShouldClose:))]
|
||||
fn window_should_close(&self, _: Option<&AnyObject>) -> bool {
|
||||
trace_scope!("windowShouldClose:");
|
||||
self.queue_event(WindowEvent::CloseRequested);
|
||||
false
|
||||
}
|
||||
|
||||
#[method(windowWillClose:)]
|
||||
#[unsafe(method(windowWillClose:))]
|
||||
fn window_will_close(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowWillClose:");
|
||||
// `setDelegate:` retains the previous value and then autoreleases it
|
||||
|
|
@ -168,14 +171,14 @@ declare_class!(
|
|||
self.queue_event(WindowEvent::Destroyed);
|
||||
}
|
||||
|
||||
#[method(windowDidResize:)]
|
||||
#[unsafe(method(windowDidResize:))]
|
||||
fn window_did_resize(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidResize:");
|
||||
// NOTE: WindowEvent::SurfaceResized is reported using NSViewFrameDidChangeNotification.
|
||||
self.emit_move_event();
|
||||
}
|
||||
|
||||
#[method(windowWillStartLiveResize:)]
|
||||
#[unsafe(method(windowWillStartLiveResize:))]
|
||||
fn window_will_start_live_resize(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowWillStartLiveResize:");
|
||||
|
||||
|
|
@ -183,20 +186,20 @@ declare_class!(
|
|||
self.set_resize_increments_inner(increments);
|
||||
}
|
||||
|
||||
#[method(windowDidEndLiveResize:)]
|
||||
#[unsafe(method(windowDidEndLiveResize:))]
|
||||
fn window_did_end_live_resize(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidEndLiveResize:");
|
||||
self.set_resize_increments_inner(NSSize::new(1., 1.));
|
||||
}
|
||||
|
||||
// This won't be triggered if the move was part of a resize.
|
||||
#[method(windowDidMove:)]
|
||||
#[unsafe(method(windowDidMove:))]
|
||||
fn window_did_move(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidMove:");
|
||||
self.emit_move_event();
|
||||
}
|
||||
|
||||
#[method(windowDidChangeBackingProperties:)]
|
||||
#[unsafe(method(windowDidChangeBackingProperties:))]
|
||||
fn window_did_change_backing_properties(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidChangeBackingProperties:");
|
||||
let scale_factor = self.scale_factor();
|
||||
|
|
@ -212,7 +215,7 @@ declare_class!(
|
|||
});
|
||||
}
|
||||
|
||||
#[method(windowDidBecomeKey:)]
|
||||
#[unsafe(method(windowDidBecomeKey:))]
|
||||
fn window_did_become_key(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidBecomeKey:");
|
||||
// TODO: center the cursor if the window had mouse grab when it
|
||||
|
|
@ -220,7 +223,7 @@ declare_class!(
|
|||
self.queue_event(WindowEvent::Focused(true));
|
||||
}
|
||||
|
||||
#[method(windowDidResignKey:)]
|
||||
#[unsafe(method(windowDidResignKey:))]
|
||||
fn window_did_resign_key(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidResignKey:");
|
||||
// It happens rather often, e.g. when the user is Cmd+Tabbing, that the
|
||||
|
|
@ -236,7 +239,7 @@ declare_class!(
|
|||
}
|
||||
|
||||
/// Invoked when before enter fullscreen
|
||||
#[method(windowWillEnterFullScreen:)]
|
||||
#[unsafe(method(windowWillEnterFullScreen:))]
|
||||
fn window_will_enter_fullscreen(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowWillEnterFullScreen:");
|
||||
|
||||
|
|
@ -246,7 +249,7 @@ declare_class!(
|
|||
// Exclusive mode sets the state in `set_fullscreen` as the user
|
||||
// can't enter exclusive mode by other means (like the
|
||||
// fullscreen button on the window decorations)
|
||||
Some(Fullscreen::Exclusive(_, _)) => (),
|
||||
Some(Fullscreen::Exclusive(..)) => (),
|
||||
// `window_will_enter_fullscreen` was triggered and we're already
|
||||
// in fullscreen, so we must've reached here by `set_fullscreen`
|
||||
// as it updates the state
|
||||
|
|
@ -262,14 +265,14 @@ declare_class!(
|
|||
}
|
||||
|
||||
/// Invoked when before exit fullscreen
|
||||
#[method(windowWillExitFullScreen:)]
|
||||
#[unsafe(method(windowWillExitFullScreen:))]
|
||||
fn window_will_exit_fullscreen(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowWillExitFullScreen:");
|
||||
|
||||
self.ivars().in_fullscreen_transition.set(true);
|
||||
}
|
||||
|
||||
#[method(window:willUseFullScreenPresentationOptions:)]
|
||||
#[unsafe(method(window:willUseFullScreenPresentationOptions:))]
|
||||
fn window_will_use_fullscreen_presentation_options(
|
||||
&self,
|
||||
_: Option<&AnyObject>,
|
||||
|
|
@ -286,17 +289,17 @@ declare_class!(
|
|||
// user-provided options are ignored in exclusive fullscreen.
|
||||
let mut options = proposed_options;
|
||||
let fullscreen = self.ivars().fullscreen.borrow();
|
||||
if let Some(Fullscreen::Exclusive(_, _)) = &*fullscreen {
|
||||
options = NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar;
|
||||
if let Some(Fullscreen::Exclusive(..)) = &*fullscreen {
|
||||
options = NSApplicationPresentationOptions::FullScreen
|
||||
| NSApplicationPresentationOptions::HideDock
|
||||
| NSApplicationPresentationOptions::HideMenuBar;
|
||||
}
|
||||
|
||||
options
|
||||
}
|
||||
|
||||
/// Invoked when entered fullscreen
|
||||
#[method(windowDidEnterFullScreen:)]
|
||||
#[unsafe(method(windowDidEnterFullScreen:))]
|
||||
fn window_did_enter_fullscreen(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidEnterFullScreen:");
|
||||
self.ivars().initial_fullscreen.set(false);
|
||||
|
|
@ -307,7 +310,7 @@ declare_class!(
|
|||
}
|
||||
|
||||
/// Invoked when exited fullscreen
|
||||
#[method(windowDidExitFullScreen:)]
|
||||
#[unsafe(method(windowDidExitFullScreen:))]
|
||||
fn window_did_exit_fullscreen(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidExitFullScreen:");
|
||||
|
||||
|
|
@ -334,7 +337,7 @@ declare_class!(
|
|||
/// due to being in the midst of handling some other animation or user gesture.
|
||||
/// This method indicates that there was an error, and you should clean up any
|
||||
/// work you may have done to prepare to enter full-screen mode.
|
||||
#[method(windowDidFailToEnterFullScreen:)]
|
||||
#[unsafe(method(windowDidFailToEnterFullScreen:))]
|
||||
fn window_did_fail_to_enter_fullscreen(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidFailToEnterFullScreen:");
|
||||
self.ivars().in_fullscreen_transition.set(false);
|
||||
|
|
@ -353,14 +356,14 @@ declare_class!(
|
|||
}
|
||||
|
||||
// Invoked when the occlusion state of the window changes
|
||||
#[method(windowDidChangeOcclusionState:)]
|
||||
#[unsafe(method(windowDidChangeOcclusionState:))]
|
||||
fn window_did_change_occlusion_state(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidChangeOcclusionState:");
|
||||
let visible = self.window().occlusionState().contains(NSWindowOcclusionState::Visible);
|
||||
self.queue_event(WindowEvent::Occluded(!visible));
|
||||
}
|
||||
|
||||
#[method(windowDidChangeScreen:)]
|
||||
#[unsafe(method(windowDidChangeScreen:))]
|
||||
fn window_did_change_screen(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidChangeScreen:");
|
||||
let is_simple_fullscreen = self.ivars().is_simple_fullscreen.get();
|
||||
|
|
@ -374,44 +377,49 @@ declare_class!(
|
|||
|
||||
unsafe impl NSDraggingDestination for WindowDelegate {
|
||||
/// Invoked when the dragged image enters destination bounds or frame
|
||||
#[method(draggingEntered:)]
|
||||
#[unsafe(method(draggingEntered:))]
|
||||
fn dragging_entered(&self, sender: &ProtocolObject<dyn NSDraggingInfo>) -> bool {
|
||||
trace_scope!("draggingEntered:");
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
let pb = unsafe { sender.draggingPasteboard() };
|
||||
let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }).unwrap();
|
||||
let filenames: Retained<NSArray<NSString>> = unsafe { Retained::cast(filenames) };
|
||||
let filenames = pb
|
||||
.propertyListForType(unsafe { NSFilenamesPboardType })
|
||||
.unwrap()
|
||||
.downcast::<NSArray>()
|
||||
.unwrap();
|
||||
let paths = filenames
|
||||
.into_iter()
|
||||
.map(|file| PathBuf::from(file.to_string()))
|
||||
.map(|file| PathBuf::from(file.downcast::<NSString>().unwrap().to_string()))
|
||||
.collect();
|
||||
|
||||
let dl = unsafe { sender.draggingLocation() };
|
||||
let dl = self.view().convertPoint_fromView(dl, None);
|
||||
let position = LogicalPosition::<f64>::from((dl.x, dl.y)).to_physical(self.scale_factor());
|
||||
|
||||
let position =
|
||||
LogicalPosition::<f64>::from((dl.x, dl.y)).to_physical(self.scale_factor());
|
||||
|
||||
self.queue_event(WindowEvent::DragEntered { paths, position });
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[method(wantsPeriodicDraggingUpdates)]
|
||||
#[unsafe(method(wantsPeriodicDraggingUpdates))]
|
||||
fn wants_periodic_dragging_updates(&self) -> bool {
|
||||
trace_scope!("wantsPeriodicDraggingUpdates:");
|
||||
true
|
||||
}
|
||||
|
||||
/// Invoked periodically as the image is held within the destination area, allowing modification of the dragging operation or mouse-pointer position.
|
||||
#[method(draggingUpdated:)]
|
||||
/// Invoked periodically as the image is held within the destination area, allowing
|
||||
/// modification of the dragging operation or mouse-pointer position.
|
||||
#[unsafe(method(draggingUpdated:))]
|
||||
fn dragging_updated(&self, sender: &ProtocolObject<dyn NSDraggingInfo>) -> bool {
|
||||
trace_scope!("draggingUpdated:");
|
||||
|
||||
let dl = unsafe { sender.draggingLocation() };
|
||||
let dl = self.view().convertPoint_fromView(dl, None);
|
||||
let position = LogicalPosition::<f64>::from((dl.x, dl.y)).to_physical(self.scale_factor());
|
||||
let position =
|
||||
LogicalPosition::<f64>::from((dl.x, dl.y)).to_physical(self.scale_factor());
|
||||
|
||||
self.queue_event(WindowEvent::DragMoved { position });
|
||||
|
||||
|
|
@ -419,30 +427,34 @@ declare_class!(
|
|||
}
|
||||
|
||||
/// Invoked when the image is released
|
||||
#[method(prepareForDragOperation:)]
|
||||
#[unsafe(method(prepareForDragOperation:))]
|
||||
fn prepare_for_drag_operation(&self, _sender: &NSObject) -> bool {
|
||||
trace_scope!("prepareForDragOperation:");
|
||||
true
|
||||
}
|
||||
|
||||
/// Invoked after the released image has been removed from the screen
|
||||
#[method(performDragOperation:)]
|
||||
#[unsafe(method(performDragOperation:))]
|
||||
fn perform_drag_operation(&self, sender: &ProtocolObject<dyn NSDraggingInfo>) -> bool {
|
||||
trace_scope!("performDragOperation:");
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
let pb = unsafe { sender.draggingPasteboard() };
|
||||
let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }).unwrap();
|
||||
let filenames: Retained<NSArray<NSString>> = unsafe { Retained::cast(filenames) };
|
||||
let filenames = pb
|
||||
.propertyListForType(unsafe { NSFilenamesPboardType })
|
||||
.unwrap()
|
||||
.downcast::<NSArray>()
|
||||
.unwrap();
|
||||
let paths = filenames
|
||||
.into_iter()
|
||||
.map(|file| PathBuf::from(file.to_string()))
|
||||
.map(|file| PathBuf::from(file.downcast::<NSString>().unwrap().to_string()))
|
||||
.collect();
|
||||
|
||||
let dl = unsafe { sender.draggingLocation() };
|
||||
let dl = self.view().convertPoint_fromView(dl, None);
|
||||
let position = LogicalPosition::<f64>::from((dl.x, dl.y)).to_physical(self.scale_factor());
|
||||
let position =
|
||||
LogicalPosition::<f64>::from((dl.x, dl.y)).to_physical(self.scale_factor());
|
||||
|
||||
self.queue_event(WindowEvent::DragDropped { paths, position });
|
||||
|
||||
|
|
@ -450,29 +462,29 @@ declare_class!(
|
|||
}
|
||||
|
||||
/// Invoked when the dragging operation is complete
|
||||
#[method(concludeDragOperation:)]
|
||||
#[unsafe(method(concludeDragOperation:))]
|
||||
fn conclude_drag_operation(&self, _sender: Option<&NSObject>) {
|
||||
trace_scope!("concludeDragOperation:");
|
||||
}
|
||||
|
||||
/// Invoked when the dragging operation is cancelled
|
||||
#[method(draggingExited:)]
|
||||
fn dragging_exited(&self, info: Option<&ProtocolObject<dyn NSDraggingInfo>>) {
|
||||
#[unsafe(method(draggingExited:))]
|
||||
fn dragging_exited(&self, sender: Option<&ProtocolObject<dyn NSDraggingInfo>>) {
|
||||
trace_scope!("draggingExited:");
|
||||
|
||||
let position = info.map(|info| {
|
||||
let dl = unsafe { info.draggingLocation() };
|
||||
let position = sender.map(|sender| {
|
||||
let dl = unsafe { sender.draggingLocation() };
|
||||
let dl = self.view().convertPoint_fromView(dl, None);
|
||||
LogicalPosition::<f64>::from((dl.x, dl.y)).to_physical(self.scale_factor())
|
||||
});
|
||||
|
||||
self.queue_event(WindowEvent::DragLeft { position } );
|
||||
self.queue_event(WindowEvent::DragLeft { position });
|
||||
}
|
||||
}
|
||||
|
||||
// Key-Value Observing
|
||||
unsafe impl WindowDelegate {
|
||||
#[method(observeValueForKeyPath:ofObject:change:context:)]
|
||||
/// Key-Value Observing
|
||||
impl WindowDelegate {
|
||||
#[unsafe(method(observeValueForKeyPath:ofObject:change:context:))]
|
||||
fn observe_value(
|
||||
&self,
|
||||
key_path: Option<&NSString>,
|
||||
|
|
@ -488,19 +500,15 @@ declare_class!(
|
|||
"requested a change dictionary in `addObserver`, but none was provided",
|
||||
);
|
||||
let old = change
|
||||
.get(unsafe { NSKeyValueChangeOldKey })
|
||||
.objectForKey(unsafe { NSKeyValueChangeOldKey })
|
||||
.expect("requested change dictionary did not contain `NSKeyValueChangeOldKey`");
|
||||
let new = change
|
||||
.get(unsafe { NSKeyValueChangeNewKey })
|
||||
.objectForKey(unsafe { NSKeyValueChangeNewKey })
|
||||
.expect("requested change dictionary did not contain `NSKeyValueChangeNewKey`");
|
||||
|
||||
// SAFETY: The value of `effectiveAppearance` is `NSAppearance`
|
||||
let old: *const AnyObject = old;
|
||||
let old: *const NSAppearance = old.cast();
|
||||
let old: &NSAppearance = unsafe { &*old };
|
||||
let new: *const AnyObject = new;
|
||||
let new: *const NSAppearance = new.cast();
|
||||
let new: &NSAppearance = unsafe { &*new };
|
||||
// The value of `effectiveAppearance` is `NSAppearance`
|
||||
let old = old.downcast::<NSAppearance>().unwrap();
|
||||
let new = new.downcast::<NSAppearance>().unwrap();
|
||||
|
||||
trace!(old = %unsafe { old.name() }, new = %unsafe { new.name() }, "effectiveAppearance changed");
|
||||
|
||||
|
|
@ -511,8 +519,8 @@ declare_class!(
|
|||
return;
|
||||
}
|
||||
|
||||
let old = appearance_to_theme(old);
|
||||
let new = appearance_to_theme(new);
|
||||
let old = appearance_to_theme(&old);
|
||||
let new = appearance_to_theme(&new);
|
||||
// Check that the theme changed in Winit's terms (the theme might have changed on
|
||||
// other parameters, such as level of contrast, but the event should not be emitted
|
||||
// in those cases).
|
||||
|
|
@ -627,11 +635,11 @@ fn new_window(
|
|||
masks |= NSWindowStyleMask::NonactivatingPanel;
|
||||
|
||||
let window: Option<Retained<WinitPanel>> = unsafe {
|
||||
msg_send_id![
|
||||
msg_send![
|
||||
super(mtm.alloc().set_ivars(())),
|
||||
initWithContentRect: frame,
|
||||
styleMask: masks,
|
||||
backing: NSBackingStoreType::NSBackingStoreBuffered,
|
||||
backing: NSBackingStoreType::Buffered,
|
||||
defer: false,
|
||||
]
|
||||
};
|
||||
|
|
@ -639,11 +647,11 @@ fn new_window(
|
|||
window?.as_super().as_super().retain()
|
||||
} else {
|
||||
let window: Option<Retained<WinitWindow>> = unsafe {
|
||||
msg_send_id![
|
||||
msg_send![
|
||||
super(mtm.alloc().set_ivars(())),
|
||||
initWithContentRect: frame,
|
||||
styleMask: masks,
|
||||
backing: NSBackingStoreType::NSBackingStoreBuffered,
|
||||
backing: NSBackingStoreType::Buffered,
|
||||
defer: false,
|
||||
]
|
||||
};
|
||||
|
|
@ -665,22 +673,22 @@ fn new_window(
|
|||
}
|
||||
|
||||
if attrs.content_protected {
|
||||
window.setSharingType(NSWindowSharingType::NSWindowSharingNone);
|
||||
window.setSharingType(NSWindowSharingType::None);
|
||||
}
|
||||
|
||||
if attrs.platform_specific.titlebar_transparent {
|
||||
window.setTitlebarAppearsTransparent(true);
|
||||
}
|
||||
if attrs.platform_specific.title_hidden {
|
||||
window.setTitleVisibility(NSWindowTitleVisibility::NSWindowTitleHidden);
|
||||
window.setTitleVisibility(NSWindowTitleVisibility::Hidden);
|
||||
}
|
||||
if attrs.platform_specific.titlebar_buttons_hidden {
|
||||
for titlebar_button in &[
|
||||
#[allow(deprecated)]
|
||||
NSWindowFullScreenButton,
|
||||
NSWindowButton::NSWindowMiniaturizeButton,
|
||||
NSWindowButton::NSWindowCloseButton,
|
||||
NSWindowButton::NSWindowZoomButton,
|
||||
NSWindowButton::MiniaturizeButton,
|
||||
NSWindowButton::CloseButton,
|
||||
NSWindowButton::ZoomButton,
|
||||
] {
|
||||
if let Some(button) = window.standardWindowButton(*titlebar_button) {
|
||||
button.setHidden(true);
|
||||
|
|
@ -700,7 +708,7 @@ fn new_window(
|
|||
}
|
||||
|
||||
if !attrs.enabled_buttons.contains(WindowButtons::MAXIMIZE) {
|
||||
if let Some(button) = window.standardWindowButton(NSWindowButton::NSWindowZoomButton) {
|
||||
if let Some(button) = window.standardWindowButton(NSWindowButton::ZoomButton) {
|
||||
button.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
|
@ -762,10 +770,7 @@ fn new_window(
|
|||
}
|
||||
|
||||
// register for drag and drop operations.
|
||||
window
|
||||
.registerForDraggedTypes(&NSArray::from_id_slice(&[
|
||||
unsafe { NSFilenamesPboardType }.copy()
|
||||
]));
|
||||
window.registerForDraggedTypes(&NSArray::from_slice(&[unsafe { NSFilenamesPboardType }]));
|
||||
|
||||
Some(window)
|
||||
})
|
||||
|
|
@ -793,9 +798,7 @@ impl WindowDelegate {
|
|||
// SAFETY: We know that there are no parent -> child -> parent cycles since the only
|
||||
// place in `winit` where we allow making a window a child window is
|
||||
// right here, just after it's been created.
|
||||
unsafe {
|
||||
parent.addChildWindow_ordered(&window, NSWindowOrderingMode::NSWindowAbove)
|
||||
};
|
||||
unsafe { parent.addChildWindow_ordered(&window, NSWindowOrderingMode::Above) };
|
||||
},
|
||||
Some(raw) => panic!("invalid raw window handle {raw:?} on macOS"),
|
||||
None => (),
|
||||
|
|
@ -836,7 +839,7 @@ impl WindowDelegate {
|
|||
saved_style: Cell::new(None),
|
||||
is_borderless_game: Cell::new(attrs.platform_specific.borderless_game),
|
||||
});
|
||||
let delegate: Retained<WindowDelegate> = unsafe { msg_send_id![super(delegate), init] };
|
||||
let delegate: Retained<WindowDelegate> = unsafe { msg_send![super(delegate), init] };
|
||||
|
||||
window.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
|
||||
|
||||
|
|
@ -847,8 +850,7 @@ impl WindowDelegate {
|
|||
window.addObserver_forKeyPath_options_context(
|
||||
&delegate,
|
||||
ns_string!("effectiveAppearance"),
|
||||
NSKeyValueObservingOptions::NSKeyValueObservingOptionNew
|
||||
| NSKeyValueObservingOptions::NSKeyValueObservingOptionOld,
|
||||
NSKeyValueObservingOptions::New | NSKeyValueObservingOptions::Old,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
|
|
@ -892,8 +894,8 @@ impl WindowDelegate {
|
|||
|
||||
#[track_caller]
|
||||
pub(super) fn view(&self) -> Retained<WinitView> {
|
||||
// SAFETY: The view inside WinitWindow is always `WinitView`
|
||||
unsafe { Retained::cast(self.window().contentView().unwrap()) }
|
||||
// The view inside WinitWindow should always be set and be `WinitView`.
|
||||
self.window().contentView().unwrap().downcast().unwrap()
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
|
|
@ -1213,8 +1215,7 @@ impl WindowDelegate {
|
|||
// We edit the button directly instead of using `NSResizableWindowMask`,
|
||||
// since that mask also affect the resizability of the window (which is
|
||||
// controllable by other means in `winit`).
|
||||
if let Some(button) = self.window().standardWindowButton(NSWindowButton::NSWindowZoomButton)
|
||||
{
|
||||
if let Some(button) = self.window().standardWindowButton(NSWindowButton::ZoomButton) {
|
||||
button.setEnabled(buttons.contains(WindowButtons::MAXIMIZE));
|
||||
}
|
||||
}
|
||||
|
|
@ -1227,7 +1228,7 @@ impl WindowDelegate {
|
|||
}
|
||||
if self
|
||||
.window()
|
||||
.standardWindowButton(NSWindowButton::NSWindowZoomButton)
|
||||
.standardWindowButton(NSWindowButton::ZoomButton)
|
||||
.map(|b| b.isEnabled())
|
||||
.unwrap_or(true)
|
||||
{
|
||||
|
|
@ -1266,8 +1267,9 @@ impl WindowDelegate {
|
|||
};
|
||||
|
||||
// TODO: Do this for real https://stackoverflow.com/a/40922095/5435443
|
||||
CGDisplay::associate_mouse_and_mouse_cursor_position(associate_mouse_cursor)
|
||||
.map_err(|status| os_error!(format!("CGError {status}")).into())
|
||||
cgerr(unsafe { CGAssociateMouseAndMouseCursorPosition(associate_mouse_cursor) })?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -1289,14 +1291,12 @@ impl WindowDelegate {
|
|||
let content_rect = self.window().contentRectForFrameRect(self.window().frame());
|
||||
let window_position = flip_window_screen_coordinates(content_rect);
|
||||
let cursor_position = cursor_position.to_logical::<CGFloat>(self.scale_factor());
|
||||
let point = core_graphics::display::CGPoint {
|
||||
let point = CGPoint {
|
||||
x: window_position.x + cursor_position.x,
|
||||
y: window_position.y + cursor_position.y,
|
||||
};
|
||||
CGDisplay::warp_mouse_cursor_position(point)
|
||||
.map_err(|status| os_error!(format!("CGError {status}")))?;
|
||||
CGDisplay::associate_mouse_and_mouse_cursor_position(true)
|
||||
.map_err(|status| os_error!(format!("CGError {status}")))?;
|
||||
cgerr(unsafe { CGWarpMouseCursorPosition(point) })?;
|
||||
cgerr(unsafe { CGAssociateMouseAndMouseCursorPosition(true) })?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1498,10 +1498,8 @@ impl WindowDelegate {
|
|||
unsafe {
|
||||
// Fade to black (and wait for the fade to complete) to hide the
|
||||
// flicker from capturing the display and switching display mode
|
||||
if ffi::CGAcquireDisplayFadeReservation(5.0, &mut fade_token)
|
||||
== ffi::kCGErrorSuccess
|
||||
{
|
||||
ffi::CGDisplayFade(
|
||||
if cgerr(CGAcquireDisplayFadeReservation(5.0, &mut fade_token)).is_ok() {
|
||||
CGDisplayFade(
|
||||
fade_token,
|
||||
0.3,
|
||||
ffi::kCGDisplayBlendNormal,
|
||||
|
|
@ -1509,11 +1507,11 @@ impl WindowDelegate {
|
|||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
ffi::TRUE,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
assert_eq!(ffi::CGDisplayCapture(display_id), ffi::kCGErrorSuccess);
|
||||
cgerr(CGDisplayCapture(display_id)).unwrap();
|
||||
}
|
||||
|
||||
let video_mode =
|
||||
|
|
@ -1523,17 +1521,13 @@ impl WindowDelegate {
|
|||
};
|
||||
|
||||
unsafe {
|
||||
let result = ffi::CGDisplaySetDisplayMode(
|
||||
display_id,
|
||||
video_mode.native_mode.0,
|
||||
std::ptr::null(),
|
||||
);
|
||||
assert!(result == ffi::kCGErrorSuccess, "failed to set video mode");
|
||||
cgerr(CGDisplaySetDisplayMode(display_id, Some(&video_mode.native_mode.0), None))
|
||||
.expect("failed to set video mode");
|
||||
|
||||
// After the display has been configured, fade back in
|
||||
// asynchronously
|
||||
if fade_token != ffi::kCGDisplayFadeReservationInvalidToken {
|
||||
ffi::CGDisplayFade(
|
||||
CGDisplayFade(
|
||||
fade_token,
|
||||
0.6,
|
||||
ffi::kCGDisplayBlendSolidColor,
|
||||
|
|
@ -1541,9 +1535,9 @@ impl WindowDelegate {
|
|||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
ffi::FALSE,
|
||||
false,
|
||||
);
|
||||
ffi::CGReleaseDisplayFadeReservation(fade_token);
|
||||
CGReleaseDisplayFadeReservation(fade_token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1575,8 +1569,8 @@ impl WindowDelegate {
|
|||
// `window:willUseFullScreenPresentationOptions` because for some reason
|
||||
// the menu bar remains interactable despite being hidden.
|
||||
if self.is_borderless_game() && matches!(fullscreen, Fullscreen::Borderless(_)) {
|
||||
let presentation_options = NSApplicationPresentationOptions::NSApplicationPresentationHideDock
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar;
|
||||
let presentation_options = NSApplicationPresentationOptions::HideDock
|
||||
| NSApplicationPresentationOptions::HideMenuBar;
|
||||
app.setPresentationOptions(presentation_options);
|
||||
}
|
||||
|
||||
|
|
@ -1601,20 +1595,19 @@ impl WindowDelegate {
|
|||
// delegate in `window:willUseFullScreenPresentationOptions:`.
|
||||
self.ivars().save_presentation_opts.set(Some(app.presentationOptions()));
|
||||
|
||||
let presentation_options =
|
||||
NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar;
|
||||
let presentation_options = NSApplicationPresentationOptions::FullScreen
|
||||
| NSApplicationPresentationOptions::HideDock
|
||||
| NSApplicationPresentationOptions::HideMenuBar;
|
||||
app.setPresentationOptions(presentation_options);
|
||||
|
||||
let window_level = unsafe { ffi::CGShieldingWindowLevel() } as NSWindowLevel + 1;
|
||||
let window_level = unsafe { CGShieldingWindowLevel() } as NSWindowLevel + 1;
|
||||
self.window().setLevel(window_level);
|
||||
},
|
||||
(Some(Fullscreen::Exclusive(ref monitor, _)), Some(Fullscreen::Borderless(_))) => {
|
||||
let presentation_options = self.ivars().save_presentation_opts.get().unwrap_or(
|
||||
NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideMenuBar
|
||||
NSApplicationPresentationOptions::FullScreen
|
||||
| NSApplicationPresentationOptions::AutoHideDock
|
||||
| NSApplicationPresentationOptions::AutoHideMenuBar,
|
||||
);
|
||||
app.setPresentationOptions(presentation_options);
|
||||
|
||||
|
|
@ -1726,8 +1719,8 @@ impl WindowDelegate {
|
|||
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
let ns_request_type = request_type.map(|ty| match ty {
|
||||
UserAttentionType::Critical => NSRequestUserAttentionType::NSCriticalRequest,
|
||||
UserAttentionType::Informational => NSRequestUserAttentionType::NSInformationalRequest,
|
||||
UserAttentionType::Critical => NSRequestUserAttentionType::CriticalRequest,
|
||||
UserAttentionType::Informational => NSRequestUserAttentionType::InformationalRequest,
|
||||
});
|
||||
if let Some(ty) = ns_request_type {
|
||||
NSApplication::sharedApplication(mtm).requestUserAttention(ty);
|
||||
|
|
@ -1787,7 +1780,7 @@ impl WindowDelegate {
|
|||
let mtm = MainThreadMarker::from(self);
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
|
||||
if app.respondsToSelector(sel!(effectiveAppearance)) {
|
||||
if available!(macos = 10.14) {
|
||||
Some(super::window_delegate::appearance_to_theme(&app.effectiveAppearance()))
|
||||
} else {
|
||||
Some(Theme::Light)
|
||||
|
|
@ -1802,9 +1795,9 @@ impl WindowDelegate {
|
|||
#[inline]
|
||||
pub fn set_content_protected(&self, protected: bool) {
|
||||
self.window().setSharingType(if protected {
|
||||
NSWindowSharingType::NSWindowSharingNone
|
||||
NSWindowSharingType::None
|
||||
} else {
|
||||
NSWindowSharingType::NSWindowSharingReadOnly
|
||||
NSWindowSharingType::ReadOnly
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -1821,8 +1814,8 @@ fn restore_and_release_display(monitor: &MonitorHandle) {
|
|||
let available_monitors = monitor::available_monitors();
|
||||
if available_monitors.contains(monitor) {
|
||||
unsafe {
|
||||
ffi::CGRestorePermanentDisplayConfiguration();
|
||||
assert_eq!(ffi::CGDisplayRelease(monitor.native_identifier()), ffi::kCGErrorSuccess);
|
||||
CGRestorePermanentDisplayConfiguration();
|
||||
cgerr(CGDisplayRelease(monitor.native_identifier())).unwrap();
|
||||
};
|
||||
} else {
|
||||
warn!(
|
||||
|
|
@ -1867,9 +1860,8 @@ impl WindowExtMacOS for WindowDelegate {
|
|||
self.ivars().is_simple_fullscreen.set(true);
|
||||
|
||||
// Simulate pre-Lion fullscreen by hiding the dock and menu bar
|
||||
let presentation_options =
|
||||
NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideMenuBar;
|
||||
let presentation_options = NSApplicationPresentationOptions::AutoHideDock
|
||||
| NSApplicationPresentationOptions::AutoHideMenuBar;
|
||||
app.setPresentationOptions(presentation_options);
|
||||
|
||||
// Hide the titlebar
|
||||
|
|
@ -1950,10 +1942,14 @@ impl WindowExtMacOS for WindowDelegate {
|
|||
|
||||
#[inline]
|
||||
fn select_tab_at_index(&self, index: usize) {
|
||||
if !available!(macos = 10.13) {
|
||||
tracing::warn!("window tab groups are only available on macOS 10.13+");
|
||||
return;
|
||||
}
|
||||
if let Some(group) = self.window().tabGroup() {
|
||||
if let Some(windows) = unsafe { self.window().tabbedWindows() } {
|
||||
if index < windows.len() {
|
||||
group.setSelectedWindow(Some(&windows[index]));
|
||||
group.setSelectedWindow(Some(&windows.objectAtIndex(index)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2026,9 +2022,9 @@ fn dark_appearance_name() -> &'static NSString {
|
|||
}
|
||||
|
||||
pub fn appearance_to_theme(appearance: &NSAppearance) -> Theme {
|
||||
let best_match = appearance.bestMatchFromAppearancesWithNames(&NSArray::from_id_slice(&[
|
||||
unsafe { NSAppearanceNameAqua.copy() },
|
||||
dark_appearance_name().copy(),
|
||||
let best_match = appearance.bestMatchFromAppearancesWithNames(&NSArray::from_slice(&[
|
||||
unsafe { NSAppearanceNameAqua },
|
||||
dark_appearance_name(),
|
||||
]));
|
||||
if let Some(best_match) = best_match {
|
||||
if *best_match == *dark_appearance_name() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue