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
This commit is contained in:
parent
674657efb6
commit
e9a25a4c91
16 changed files with 367 additions and 951 deletions
|
|
@ -1,7 +1,7 @@
|
|||
#![allow(clippy::unnecessary_cast)]
|
||||
|
||||
use icrate::AppKit::{
|
||||
NSEvent, NSEventModifierFlagCommand, NSEventTypeKeyUp, NSEventTypeLeftMouseDown,
|
||||
NSApplication, NSEvent, NSEventModifierFlagCommand, NSEventTypeKeyUp, NSEventTypeLeftMouseDown,
|
||||
NSEventTypeLeftMouseDragged, NSEventTypeLeftMouseUp, NSEventTypeMouseMoved,
|
||||
NSEventTypeOtherMouseDown, NSEventTypeOtherMouseDragged, NSEventTypeOtherMouseUp,
|
||||
NSEventTypeRightMouseDown, NSEventTypeRightMouseDragged, NSEventTypeRightMouseUp, NSResponder,
|
||||
|
|
@ -9,7 +9,6 @@ use icrate::AppKit::{
|
|||
use icrate::Foundation::NSObject;
|
||||
use objc2::{declare_class, msg_send, mutability, ClassType, DeclaredClass};
|
||||
|
||||
use super::appkit::NSApplication;
|
||||
use super::event::flags_contains;
|
||||
use super::{app_state::AppState, DEVICE_ID};
|
||||
use crate::event::{DeviceEvent, ElementState, Event};
|
||||
|
|
@ -41,7 +40,7 @@ declare_class!(
|
|||
&& flags_contains(modifier_flags, NSEventModifierFlagCommand)
|
||||
{
|
||||
if let Some(key_window) = self.keyWindow() {
|
||||
unsafe { key_window.sendEvent(event) };
|
||||
key_window.sendEvent(event);
|
||||
}
|
||||
} else {
|
||||
maybe_dispatch_device_event(event);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use icrate::Foundation::NSObject;
|
||||
use icrate::AppKit::{NSApplicationActivationPolicy, NSApplicationDelegate};
|
||||
use icrate::Foundation::{MainThreadMarker, NSObject, NSObjectProtocol};
|
||||
use objc2::rc::Id;
|
||||
use objc2::runtime::AnyObject;
|
||||
use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass};
|
||||
|
||||
use super::app_state::AppState;
|
||||
use super::appkit::NSApplicationActivationPolicy;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct State {
|
||||
|
|
@ -18,7 +18,7 @@ declare_class!(
|
|||
|
||||
unsafe impl ClassType for ApplicationDelegate {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
type Mutability = mutability::MainThreadOnly;
|
||||
const NAME: &'static str = "WinitApplicationDelegate";
|
||||
}
|
||||
|
||||
|
|
@ -26,7 +26,9 @@ declare_class!(
|
|||
type Ivars = State;
|
||||
}
|
||||
|
||||
unsafe impl ApplicationDelegate {
|
||||
unsafe impl NSObjectProtocol for ApplicationDelegate {}
|
||||
|
||||
unsafe impl NSApplicationDelegate for ApplicationDelegate {
|
||||
#[method(applicationDidFinishLaunching:)]
|
||||
fn did_finish_launching(&self, _sender: Option<&AnyObject>) {
|
||||
trace_scope!("applicationDidFinishLaunching:");
|
||||
|
|
@ -48,11 +50,12 @@ declare_class!(
|
|||
|
||||
impl ApplicationDelegate {
|
||||
pub(super) fn new(
|
||||
mtm: MainThreadMarker,
|
||||
activation_policy: NSApplicationActivationPolicy,
|
||||
default_menu: bool,
|
||||
activate_ignoring_other_apps: bool,
|
||||
) -> Id<Self> {
|
||||
let this = Self::alloc().set_ivars(State {
|
||||
let this = mtm.alloc().set_ivars(State {
|
||||
activation_policy,
|
||||
default_menu,
|
||||
activate_ignoring_other_apps,
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ use std::{
|
|||
};
|
||||
|
||||
use core_foundation::runloop::{CFRunLoopGetMain, CFRunLoopWakeUp};
|
||||
use icrate::AppKit::{NSApplication, NSApplicationActivationPolicy};
|
||||
use icrate::Foundation::{is_main_thread, MainThreadMarker, NSSize};
|
||||
use objc2::rc::{autoreleasepool, Id};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use super::appkit::{NSApp, NSApplication, NSApplicationActivationPolicy};
|
||||
use super::{
|
||||
event::dummy_event, event_loop::PanicInfo, menu, observer::EventLoopWaker, util::Never,
|
||||
window::WinitWindow,
|
||||
|
|
@ -58,14 +58,14 @@ impl<T> EventLoopHandler<T> {
|
|||
where
|
||||
F: FnOnce(&mut EventLoopHandler<T>, RefMut<'_, dyn FnMut(Event<T>, &RootWindowTarget<T>)>),
|
||||
{
|
||||
// The `NSApp` and our `HANDLER` are global state and so it's possible that
|
||||
// we could get a delegate callback after the application has exit an
|
||||
// `NSApplication` and our `HANDLER` are global state and so it's possible
|
||||
// that we could get a delegate callback after the application has exit an
|
||||
// `EventLoop`. If the loop has been exit then our weak `self.callback`
|
||||
// will fail to upgrade.
|
||||
//
|
||||
// We don't want to panic or output any verbose logging if we fail to
|
||||
// upgrade the weak reference since it might be valid that the application
|
||||
// re-starts the `NSApp` after exiting a Winit `EventLoop`
|
||||
// re-starts the `NSApplication` after exiting a Winit `EventLoop`
|
||||
if let Some(callback) = self.callback.upgrade() {
|
||||
let callback = callback.borrow_mut();
|
||||
(f)(self, callback);
|
||||
|
|
@ -145,9 +145,9 @@ impl Handler {
|
|||
|
||||
/// `true` after `ApplicationDelegate::applicationDidFinishLaunching` called
|
||||
///
|
||||
/// NB: This is global / `NSApp` state and since the app will only be launched
|
||||
/// once but an `EventLoop` may be run more than once then only the first
|
||||
/// `EventLoop` will observe the `NSApp` before it is launched.
|
||||
/// NB: This is global / `NSApplication` state and since the app will only
|
||||
/// be launched once but an `EventLoop` may be run more than once then only
|
||||
/// the first `EventLoop` will observe the application before it is launched.
|
||||
fn is_launched(&self) -> bool {
|
||||
self.launched.load(Ordering::Acquire)
|
||||
}
|
||||
|
|
@ -159,8 +159,8 @@ impl Handler {
|
|||
|
||||
/// `true` if an `EventLoop` is currently running
|
||||
///
|
||||
/// NB: This is global / `NSApp` state and may persist beyond the lifetime of
|
||||
/// a running `EventLoop`.
|
||||
/// NB: This is global / `NSApplication` state and may persist beyond the
|
||||
/// lifetime of a running `EventLoop`.
|
||||
///
|
||||
/// # Caveat
|
||||
/// This is only intended to be called from the main thread
|
||||
|
|
@ -168,7 +168,7 @@ impl Handler {
|
|||
self.running.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Set when an `EventLoop` starts running, after the `NSApp` is launched
|
||||
/// Set when an `EventLoop` starts running, after the `NSApplication` is launched
|
||||
///
|
||||
/// # Caveat
|
||||
/// This is only intended to be called from the main thread
|
||||
|
|
@ -181,8 +181,8 @@ impl Handler {
|
|||
/// Since an `EventLoop` may be run more than once we need make sure to reset the
|
||||
/// `control_flow` state back to `Poll` each time the loop exits.
|
||||
///
|
||||
/// Note: that if the `NSApp` has been launched then that state is preserved, and we won't
|
||||
/// need to re-launch the app if subsequent EventLoops are run.
|
||||
/// Note: that if the `NSApplication` has been launched then that state is preserved,
|
||||
/// and we won't need to re-launch the app if subsequent EventLoops are run.
|
||||
///
|
||||
/// # Caveat
|
||||
/// This is only intended to be called from the main thread
|
||||
|
|
@ -393,7 +393,7 @@ impl AppState {
|
|||
}
|
||||
|
||||
// If `pump_events` is called to progress the event loop then we bootstrap the event
|
||||
// loop via `[NSApp run]` but will use `CFRunLoopRunInMode` for subsequent calls to
|
||||
// loop via `-[NSAppplication run]` but will use `CFRunLoopRunInMode` for subsequent calls to
|
||||
// `pump_events`
|
||||
pub fn request_stop_on_launch() {
|
||||
HANDLER.request_stop_app_on_launch();
|
||||
|
|
@ -461,13 +461,14 @@ impl AppState {
|
|||
activate_ignoring_other_apps: bool,
|
||||
) {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
let app = NSApp();
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
// We need to delay setting the activation policy and activating the app
|
||||
// until `applicationDidFinishLaunching` has been called. Otherwise the
|
||||
// menu bar is initially unresponsive on macOS 10.15.
|
||||
app.setActivationPolicy(activation_policy);
|
||||
|
||||
window_activation_hack(&app);
|
||||
#[allow(deprecated)]
|
||||
app.activateIgnoringOtherApps(activate_ignoring_other_apps);
|
||||
|
||||
HANDLER.set_launched();
|
||||
|
|
@ -475,21 +476,22 @@ impl AppState {
|
|||
if create_default_menu {
|
||||
// The menubar initialization should be before the `NewEvents` event, to allow
|
||||
// overriding of the default menu even if it's created
|
||||
menu::initialize(mtm);
|
||||
menu::initialize(&app);
|
||||
}
|
||||
|
||||
Self::start_running();
|
||||
|
||||
// If the `NSApp` is being launched via `EventLoop::pump_events()` then we'll
|
||||
// If the application is being launched via `EventLoop::pump_events()` then we'll
|
||||
// want to stop the app once it is launched (and return to the external loop)
|
||||
//
|
||||
// In this case we still want to consider Winit's `EventLoop` to be "running",
|
||||
// so we call `start_running()` above.
|
||||
if HANDLER.should_stop_app_on_launch() {
|
||||
// Note: the original idea had been to only stop the underlying `RunLoop`
|
||||
// for the app but that didn't work as expected (`[NSApp run]` effectively
|
||||
// ignored the attempt to stop the RunLoop and re-started it.). So we
|
||||
// return from `pump_events` by stopping the `NSApp`
|
||||
// for the app but that didn't work as expected (`-[NSApplication run]`
|
||||
// effectively ignored the attempt to stop the RunLoop and re-started it).
|
||||
//
|
||||
// So we return from `pump_events` by stopping the application.
|
||||
Self::stop();
|
||||
}
|
||||
}
|
||||
|
|
@ -591,7 +593,8 @@ impl AppState {
|
|||
}
|
||||
|
||||
pub fn stop() {
|
||||
let app = NSApp();
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
autoreleasepool(|_| {
|
||||
app.stop(None);
|
||||
// To stop event loop immediately, we need to post some event here.
|
||||
|
|
|
|||
|
|
@ -1,144 +0,0 @@
|
|||
use icrate::AppKit::{NSAppearance, NSEvent, NSMenu, NSResponder};
|
||||
use icrate::Foundation::{MainThreadMarker, NSArray, NSInteger, NSObject, NSUInteger};
|
||||
use objc2::rc::Id;
|
||||
use objc2::runtime::AnyObject;
|
||||
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
|
||||
use objc2::{Encode, Encoding};
|
||||
|
||||
use super::NSWindow;
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSApplication;
|
||||
|
||||
unsafe impl ClassType for NSApplication {
|
||||
#[inherits(NSObject)]
|
||||
type Super = NSResponder;
|
||||
type Mutability = mutability::MainThreadOnly;
|
||||
}
|
||||
);
|
||||
|
||||
pub(crate) fn NSApp() -> Id<NSApplication> {
|
||||
// TODO: Only allow access from main thread
|
||||
NSApplication::shared(unsafe { MainThreadMarker::new_unchecked() })
|
||||
}
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSApplication {
|
||||
/// This can only be called on the main thread since it may initialize
|
||||
/// the application and since it's parameters may be changed by the main
|
||||
/// thread at any time (hence it is only safe to access on the main thread).
|
||||
pub fn shared(_mtm: MainThreadMarker) -> Id<Self> {
|
||||
let app: Option<_> = unsafe { msg_send_id![Self::class(), sharedApplication] };
|
||||
// SAFETY: `sharedApplication` always initializes the app if it isn't already
|
||||
unsafe { app.unwrap_unchecked() }
|
||||
}
|
||||
|
||||
#[method_id(currentEvent)]
|
||||
pub fn currentEvent(&self) -> Option<Id<NSEvent>>;
|
||||
|
||||
#[method(postEvent:atStart:)]
|
||||
pub fn postEvent_atStart(&self, event: &NSEvent, front_of_queue: bool);
|
||||
|
||||
#[method(presentationOptions)]
|
||||
pub fn presentationOptions(&self) -> NSApplicationPresentationOptions;
|
||||
|
||||
#[method_id(windows)]
|
||||
pub fn windows(&self) -> Id<NSArray<NSWindow>>;
|
||||
|
||||
#[method_id(keyWindow)]
|
||||
pub fn keyWindow(&self) -> Option<Id<NSWindow>>;
|
||||
|
||||
// TODO: NSApplicationDelegate
|
||||
#[method(setDelegate:)]
|
||||
pub fn setDelegate(&self, delegate: &AnyObject);
|
||||
|
||||
#[method(setPresentationOptions:)]
|
||||
pub fn setPresentationOptions(&self, options: NSApplicationPresentationOptions);
|
||||
|
||||
#[method(hide:)]
|
||||
pub fn hide(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(orderFrontCharacterPalette:)]
|
||||
#[allow(dead_code)]
|
||||
pub fn orderFrontCharacterPalette(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(hideOtherApplications:)]
|
||||
pub fn hideOtherApplications(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(stop:)]
|
||||
pub fn stop(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(activateIgnoringOtherApps:)]
|
||||
pub fn activateIgnoringOtherApps(&self, ignore: bool);
|
||||
|
||||
#[method(requestUserAttention:)]
|
||||
pub fn requestUserAttention(&self, type_: NSRequestUserAttentionType) -> NSInteger;
|
||||
|
||||
#[method(setActivationPolicy:)]
|
||||
pub fn setActivationPolicy(&self, policy: NSApplicationActivationPolicy) -> bool;
|
||||
|
||||
#[method(setMainMenu:)]
|
||||
pub fn setMainMenu(&self, menu: &NSMenu);
|
||||
|
||||
#[method(setServicesMenu:)]
|
||||
pub fn setServicesMenu(&self, menu: &NSMenu);
|
||||
|
||||
#[method_id(effectiveAppearance)]
|
||||
pub fn effectiveAppearance(&self) -> Id<NSAppearance>;
|
||||
|
||||
#[method(setAppearance:)]
|
||||
pub fn setAppearance(&self, appearance: Option<&NSAppearance>);
|
||||
|
||||
#[method(run)]
|
||||
pub unsafe fn run(&self);
|
||||
}
|
||||
);
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(isize)] // NSInteger
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSApplicationActivationPolicy {
|
||||
NSApplicationActivationPolicyRegular = 0,
|
||||
NSApplicationActivationPolicyAccessory = 1,
|
||||
NSApplicationActivationPolicyProhibited = 2,
|
||||
NSApplicationActivationPolicyERROR = -1,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSApplicationActivationPolicy {
|
||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct NSApplicationPresentationOptions: NSUInteger {
|
||||
const NSApplicationPresentationDefault = 0;
|
||||
const NSApplicationPresentationAutoHideDock = 1 << 0;
|
||||
const NSApplicationPresentationHideDock = 1 << 1;
|
||||
const NSApplicationPresentationAutoHideMenuBar = 1 << 2;
|
||||
const NSApplicationPresentationHideMenuBar = 1 << 3;
|
||||
const NSApplicationPresentationDisableAppleMenu = 1 << 4;
|
||||
const NSApplicationPresentationDisableProcessSwitching = 1 << 5;
|
||||
const NSApplicationPresentationDisableForceQuit = 1 << 6;
|
||||
const NSApplicationPresentationDisableSessionTermination = 1 << 7;
|
||||
const NSApplicationPresentationDisableHideApplication = 1 << 8;
|
||||
const NSApplicationPresentationDisableMenuBarTransparency = 1 << 9;
|
||||
const NSApplicationPresentationFullScreen = 1 << 10;
|
||||
const NSApplicationPresentationAutoHideToolbar = 1 << 11;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSApplicationPresentationOptions {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
||||
#[repr(usize)] // NSUInteger
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSRequestUserAttentionType {
|
||||
NSCriticalRequest = 0,
|
||||
NSInformationalRequest = 10,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSRequestUserAttentionType {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
//! Safe bindings for the AppKit framework.
|
||||
//!
|
||||
//! These are split out from the rest of `winit` to make safety easier to review.
|
||||
//! In the future, these should probably live in another crate like `cacao`.
|
||||
//!
|
||||
//! TODO: Main thread safety.
|
||||
// Objective-C methods have different conventions, and it's much easier to
|
||||
// understand if we just use the same names
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
#![allow(clippy::enum_variant_names)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
mod application;
|
||||
mod tab_group;
|
||||
mod window;
|
||||
|
||||
pub(crate) use self::application::{
|
||||
NSApp, NSApplication, NSApplicationActivationPolicy, NSApplicationPresentationOptions,
|
||||
NSRequestUserAttentionType,
|
||||
};
|
||||
pub(crate) use self::tab_group::NSWindowTabGroup;
|
||||
pub(crate) use self::window::{
|
||||
NSBackingStoreType, NSWindow, NSWindowButton, NSWindowLevel, NSWindowOcclusionState,
|
||||
NSWindowSharingType, NSWindowStyleMask, NSWindowTabbingMode, NSWindowTitleVisibility,
|
||||
};
|
||||
|
||||
#[link(name = "AppKit", kind = "framework")]
|
||||
extern "C" {}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
use icrate::Foundation::{NSArray, NSObject};
|
||||
use objc2::rc::Id;
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
use super::NSWindow;
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSWindowTabGroup;
|
||||
|
||||
unsafe impl ClassType for NSWindowTabGroup {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSWindowTabGroup {
|
||||
#[method(selectNextTab)]
|
||||
pub fn selectNextTab(&self);
|
||||
|
||||
#[method(selectPreviousTab)]
|
||||
pub fn selectPreviousTab(&self);
|
||||
|
||||
#[method_id(windows)]
|
||||
pub fn tabbedWindows(&self) -> Option<Id<NSArray<NSWindow>>>;
|
||||
|
||||
#[method(setSelectedWindow:)]
|
||||
pub fn setSelectedWindow(&self, window: &NSWindow);
|
||||
}
|
||||
);
|
||||
|
|
@ -1,439 +0,0 @@
|
|||
use icrate::AppKit::{NSButton, NSColor, NSEvent, NSPasteboardType, NSResponder, NSScreen, NSView};
|
||||
use icrate::Foundation::{
|
||||
CGFloat, NSArray, NSInteger, NSObject, NSPoint, NSRect, NSSize, NSString, NSUInteger,
|
||||
};
|
||||
use objc2::encode::{Encode, Encoding};
|
||||
use objc2::rc::Id;
|
||||
use objc2::runtime::AnyObject;
|
||||
use objc2::{extern_class, extern_methods, mutability, ClassType};
|
||||
|
||||
use super::NSWindowTabGroup;
|
||||
|
||||
extern_class!(
|
||||
/// Main-Thread-Only!
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct NSWindow;
|
||||
|
||||
unsafe impl ClassType for NSWindow {
|
||||
#[inherits(NSObject)]
|
||||
type Super = NSResponder;
|
||||
type Mutability = mutability::MainThreadOnly;
|
||||
}
|
||||
);
|
||||
|
||||
// Documented as "Main Thread Only", but:
|
||||
// > Thread safe in that you can create and manage them on a secondary thread.
|
||||
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CocoaFundamentals/AddingBehaviortoaCocoaProgram/AddingBehaviorCocoa.html#//apple_ref/doc/uid/TP40002974-CH5-SW47>
|
||||
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-123364>
|
||||
//
|
||||
// So could in theory be `Send`, and perhaps also `Sync` - but we would like
|
||||
// interior mutability on windows, since that's just much easier, and in that
|
||||
// case, they can't be!
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl NSWindow {
|
||||
#[method(frame)]
|
||||
pub(crate) fn frame(&self) -> NSRect;
|
||||
|
||||
#[method(windowNumber)]
|
||||
pub(crate) fn windowNumber(&self) -> NSInteger;
|
||||
|
||||
#[method(backingScaleFactor)]
|
||||
pub(crate) fn backingScaleFactor(&self) -> CGFloat;
|
||||
|
||||
#[method_id(contentView)]
|
||||
pub(crate) fn contentView(&self) -> Id<NSView>;
|
||||
|
||||
#[method(setContentView:)]
|
||||
pub(crate) fn setContentView(&self, view: &NSView);
|
||||
|
||||
#[method(setInitialFirstResponder:)]
|
||||
pub(crate) fn setInitialFirstResponder(&self, view: &NSView);
|
||||
|
||||
#[method(makeFirstResponder:)]
|
||||
#[must_use]
|
||||
pub(crate) fn makeFirstResponder(&self, responder: Option<&NSResponder>) -> bool;
|
||||
|
||||
#[method(contentRectForFrameRect:)]
|
||||
pub(crate) fn contentRectForFrameRect(&self, windowFrame: NSRect) -> NSRect;
|
||||
|
||||
#[method_id(screen)]
|
||||
pub(crate) fn screen(&self) -> Option<Id<NSScreen>>;
|
||||
|
||||
#[method(setContentSize:)]
|
||||
pub(crate) fn setContentSize(&self, contentSize: NSSize);
|
||||
|
||||
#[method(setFrameTopLeftPoint:)]
|
||||
pub(crate) fn setFrameTopLeftPoint(&self, point: NSPoint);
|
||||
|
||||
#[method(setMinSize:)]
|
||||
pub(crate) fn setMinSize(&self, minSize: NSSize);
|
||||
|
||||
#[method(setMaxSize:)]
|
||||
pub(crate) fn setMaxSize(&self, maxSize: NSSize);
|
||||
|
||||
#[method(setResizeIncrements:)]
|
||||
pub(crate) fn setResizeIncrements(&self, increments: NSSize);
|
||||
|
||||
#[method(contentResizeIncrements)]
|
||||
pub(crate) fn contentResizeIncrements(&self) -> NSSize;
|
||||
|
||||
#[method(setContentResizeIncrements:)]
|
||||
pub(crate) fn setContentResizeIncrements(&self, increments: NSSize);
|
||||
|
||||
#[method(setFrame:display:)]
|
||||
pub(crate) fn setFrame_display(&self, frameRect: NSRect, flag: bool);
|
||||
|
||||
#[method(setMovable:)]
|
||||
pub(crate) fn setMovable(&self, movable: bool);
|
||||
|
||||
#[method(setSharingType:)]
|
||||
pub(crate) fn setSharingType(&self, sharingType: NSWindowSharingType);
|
||||
|
||||
#[method(setTabbingMode:)]
|
||||
pub(crate) fn setTabbingMode(&self, tabbingMode: NSWindowTabbingMode);
|
||||
|
||||
#[method(setOpaque:)]
|
||||
pub(crate) fn setOpaque(&self, opaque: bool);
|
||||
|
||||
#[method(hasShadow)]
|
||||
pub(crate) fn hasShadow(&self) -> bool;
|
||||
|
||||
#[method(setHasShadow:)]
|
||||
pub(crate) fn setHasShadow(&self, has_shadow: bool);
|
||||
|
||||
#[method(setIgnoresMouseEvents:)]
|
||||
pub(crate) fn setIgnoresMouseEvents(&self, ignores: bool);
|
||||
|
||||
#[method(setBackgroundColor:)]
|
||||
pub(crate) fn setBackgroundColor(&self, color: &NSColor);
|
||||
|
||||
#[method(styleMask)]
|
||||
pub(crate) fn styleMask(&self) -> NSWindowStyleMask;
|
||||
|
||||
#[method(setStyleMask:)]
|
||||
pub(crate) fn setStyleMask(&self, mask: NSWindowStyleMask);
|
||||
|
||||
#[method(registerForDraggedTypes:)]
|
||||
pub(crate) fn registerForDraggedTypes(&self, types: &NSArray<NSPasteboardType>);
|
||||
|
||||
#[method(makeKeyAndOrderFront:)]
|
||||
pub(crate) fn makeKeyAndOrderFront(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(orderFront:)]
|
||||
pub(crate) fn orderFront(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(miniaturize:)]
|
||||
pub(crate) fn miniaturize(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(deminiaturize:)]
|
||||
pub(crate) fn deminiaturize(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(toggleFullScreen:)]
|
||||
pub(crate) fn toggleFullScreen(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(orderOut:)]
|
||||
pub(crate) fn orderOut(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(zoom:)]
|
||||
pub(crate) fn zoom(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(selectNextKeyView:)]
|
||||
pub(crate) fn selectNextKeyView(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method(selectPreviousKeyView:)]
|
||||
pub(crate) fn selectPreviousKeyView(&self, sender: Option<&AnyObject>);
|
||||
|
||||
#[method_id(firstResponder)]
|
||||
pub(crate) fn firstResponder(&self) -> Option<Id<NSResponder>>;
|
||||
|
||||
#[method_id(standardWindowButton:)]
|
||||
pub(crate) fn standardWindowButton(&self, kind: NSWindowButton) -> Option<Id<NSButton>>;
|
||||
|
||||
#[method(setTitle:)]
|
||||
pub(crate) fn setTitle(&self, title: &NSString);
|
||||
|
||||
#[method_id(title)]
|
||||
pub(crate) fn title_(&self) -> Id<NSString>;
|
||||
|
||||
#[method(setReleasedWhenClosed:)]
|
||||
pub(crate) fn setReleasedWhenClosed(&self, val: bool);
|
||||
|
||||
#[method(setAcceptsMouseMovedEvents:)]
|
||||
pub(crate) fn setAcceptsMouseMovedEvents(&self, val: bool);
|
||||
|
||||
#[method(setTitlebarAppearsTransparent:)]
|
||||
pub(crate) fn setTitlebarAppearsTransparent(&self, val: bool);
|
||||
|
||||
#[method(setTitleVisibility:)]
|
||||
pub(crate) fn setTitleVisibility(&self, visibility: NSWindowTitleVisibility);
|
||||
|
||||
#[method(setMovableByWindowBackground:)]
|
||||
pub(crate) fn setMovableByWindowBackground(&self, val: bool);
|
||||
|
||||
#[method(setLevel:)]
|
||||
pub(crate) fn setLevel(&self, level: NSWindowLevel);
|
||||
|
||||
#[method(setAllowsAutomaticWindowTabbing:)]
|
||||
pub(crate) fn setAllowsAutomaticWindowTabbing(val: bool);
|
||||
|
||||
#[method(setTabbingIdentifier:)]
|
||||
pub(crate) fn setTabbingIdentifier(&self, identifier: &NSString);
|
||||
|
||||
#[method(setDocumentEdited:)]
|
||||
pub(crate) fn setDocumentEdited(&self, val: bool);
|
||||
|
||||
#[method(occlusionState)]
|
||||
pub(crate) fn occlusionState(&self) -> NSWindowOcclusionState;
|
||||
|
||||
#[method(center)]
|
||||
pub(crate) fn center(&self);
|
||||
|
||||
#[method(isResizable)]
|
||||
pub(crate) fn isResizable(&self) -> bool;
|
||||
|
||||
#[method(isMiniaturizable)]
|
||||
pub(crate) fn isMiniaturizable(&self) -> bool;
|
||||
|
||||
#[method(hasCloseBox)]
|
||||
pub(crate) fn hasCloseBox(&self) -> bool;
|
||||
|
||||
#[method(isMiniaturized)]
|
||||
pub(crate) fn isMiniaturized(&self) -> bool;
|
||||
|
||||
#[method(isVisible)]
|
||||
pub(crate) fn isVisible(&self) -> bool;
|
||||
|
||||
#[method(isKeyWindow)]
|
||||
pub(crate) fn isKeyWindow(&self) -> bool;
|
||||
|
||||
#[method(isZoomed)]
|
||||
pub(crate) fn isZoomed(&self) -> bool;
|
||||
|
||||
#[method(allowsAutomaticWindowTabbing)]
|
||||
pub(crate) fn allowsAutomaticWindowTabbing() -> bool;
|
||||
|
||||
#[method(selectNextTab)]
|
||||
pub(crate) fn selectNextTab(&self);
|
||||
|
||||
#[method_id(tabbingIdentifier)]
|
||||
pub(crate) fn tabbingIdentifier(&self) -> Id<NSString>;
|
||||
|
||||
#[method_id(tabGroup)]
|
||||
pub(crate) fn tabGroup(&self) -> Option<Id<NSWindowTabGroup>>;
|
||||
|
||||
#[method(isDocumentEdited)]
|
||||
pub(crate) fn isDocumentEdited(&self) -> bool;
|
||||
|
||||
#[method(close)]
|
||||
pub(crate) fn close(&self);
|
||||
|
||||
#[method(performWindowDragWithEvent:)]
|
||||
// TODO: Can this actually accept NULL?
|
||||
pub(crate) fn performWindowDragWithEvent(&self, event: Option<&NSEvent>);
|
||||
|
||||
#[method(invalidateCursorRectsForView:)]
|
||||
pub(crate) fn invalidateCursorRectsForView(&self, view: &NSView);
|
||||
|
||||
#[method(setDelegate:)]
|
||||
pub(crate) fn setDelegate(&self, delegate: Option<&NSObject>);
|
||||
|
||||
#[method(sendEvent:)]
|
||||
pub(crate) unsafe fn sendEvent(&self, event: &NSEvent);
|
||||
|
||||
#[method(addChildWindow:ordered:)]
|
||||
pub(crate) unsafe fn addChildWindow(&self, child: &NSWindow, ordered: NSWindowOrderingMode);
|
||||
}
|
||||
);
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(isize)] // NSInteger
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSWindowTitleVisibility {
|
||||
#[doc(alias = "NSWindowTitleVisible")]
|
||||
Visible = 0,
|
||||
#[doc(alias = "NSWindowTitleHidden")]
|
||||
Hidden = 1,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSWindowTitleVisibility {
|
||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(usize)] // NSUInteger
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSWindowButton {
|
||||
#[doc(alias = "NSWindowCloseButton")]
|
||||
Close = 0,
|
||||
#[doc(alias = "NSWindowMiniaturizeButton")]
|
||||
Miniaturize = 1,
|
||||
#[doc(alias = "NSWindowZoomButton")]
|
||||
Zoom = 2,
|
||||
#[doc(alias = "NSWindowToolbarButton")]
|
||||
Toolbar = 3,
|
||||
#[doc(alias = "NSWindowDocumentIconButton")]
|
||||
DocumentIcon = 4,
|
||||
#[doc(alias = "NSWindowDocumentVersionsButton")]
|
||||
DocumentVersions = 6,
|
||||
#[doc(alias = "NSWindowFullScreenButton")]
|
||||
#[deprecated = "Deprecated since macOS 10.12"]
|
||||
FullScreen = 7,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSWindowButton {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
||||
// CGWindowLevel.h
|
||||
//
|
||||
// Note: There are two different things at play in this header:
|
||||
// `CGWindowLevel` and `CGWindowLevelKey`.
|
||||
//
|
||||
// It seems like there was a push towards using "key" values instead of the
|
||||
// raw window level values, and then you were supposed to use
|
||||
// `CGWindowLevelForKey` to get the actual level.
|
||||
//
|
||||
// But the values that `NSWindowLevel` has are compiled in, and as such has
|
||||
// to remain ABI compatible, so they're safe for us to hardcode as well.
|
||||
#[allow(dead_code)]
|
||||
mod window_level {
|
||||
const kCGNumReservedWindowLevels: i32 = 16;
|
||||
const kCGNumReservedBaseWindowLevels: i32 = 5;
|
||||
|
||||
pub const kCGBaseWindowLevel: i32 = i32::MIN;
|
||||
pub const kCGMinimumWindowLevel: i32 = kCGBaseWindowLevel + kCGNumReservedBaseWindowLevels;
|
||||
pub const kCGMaximumWindowLevel: i32 = i32::MAX - kCGNumReservedWindowLevels;
|
||||
|
||||
pub const kCGDesktopWindowLevel: i32 = kCGMinimumWindowLevel + 20;
|
||||
pub const kCGDesktopIconWindowLevel: i32 = kCGDesktopWindowLevel + 20;
|
||||
pub const kCGBackstopMenuLevel: i32 = -20;
|
||||
pub const kCGNormalWindowLevel: i32 = 0;
|
||||
pub const kCGFloatingWindowLevel: i32 = 3;
|
||||
pub const kCGTornOffMenuWindowLevel: i32 = 3;
|
||||
pub const kCGModalPanelWindowLevel: i32 = 8;
|
||||
pub const kCGUtilityWindowLevel: i32 = 19;
|
||||
pub const kCGDockWindowLevel: i32 = 20;
|
||||
pub const kCGMainMenuWindowLevel: i32 = 24;
|
||||
pub const kCGStatusWindowLevel: i32 = 25;
|
||||
pub const kCGPopUpMenuWindowLevel: i32 = 101;
|
||||
pub const kCGOverlayWindowLevel: i32 = 102;
|
||||
pub const kCGHelpWindowLevel: i32 = 200;
|
||||
pub const kCGDraggingWindowLevel: i32 = 500;
|
||||
pub const kCGScreenSaverWindowLevel: i32 = 1000;
|
||||
pub const kCGAssistiveTechHighWindowLevel: i32 = 1500;
|
||||
pub const kCGCursorWindowLevel: i32 = kCGMaximumWindowLevel - 1;
|
||||
}
|
||||
use window_level::*;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[repr(transparent)]
|
||||
pub struct NSWindowLevel(pub NSInteger);
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl NSWindowLevel {
|
||||
#[doc(alias = "BelowNormalWindowLevel")]
|
||||
pub const BELOW_NORMAL: Self = Self((kCGNormalWindowLevel - 1) as _);
|
||||
#[doc(alias = "NSNormalWindowLevel")]
|
||||
pub const Normal: Self = Self(kCGNormalWindowLevel as _);
|
||||
#[doc(alias = "NSFloatingWindowLevel")]
|
||||
pub const Floating: Self = Self(kCGFloatingWindowLevel as _);
|
||||
#[doc(alias = "NSTornOffMenuWindowLevel")]
|
||||
pub const TornOffMenu: Self = Self(kCGTornOffMenuWindowLevel as _);
|
||||
#[doc(alias = "NSModalPanelWindowLevel")]
|
||||
pub const ModalPanel: Self = Self(kCGModalPanelWindowLevel as _);
|
||||
#[doc(alias = "NSMainMenuWindowLevel")]
|
||||
pub const MainMenu: Self = Self(kCGMainMenuWindowLevel as _);
|
||||
#[doc(alias = "NSStatusWindowLevel")]
|
||||
pub const Status: Self = Self(kCGStatusWindowLevel as _);
|
||||
#[doc(alias = "NSPopUpMenuWindowLevel")]
|
||||
pub const PopUpMenu: Self = Self(kCGPopUpMenuWindowLevel as _);
|
||||
#[doc(alias = "NSScreenSaverWindowLevel")]
|
||||
pub const ScreenSaver: Self = Self(kCGScreenSaverWindowLevel as _);
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSWindowLevel {
|
||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct NSWindowOcclusionState: NSUInteger {
|
||||
const NSWindowOcclusionStateVisible = 1 << 1;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSWindowOcclusionState {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct NSWindowStyleMask: NSUInteger {
|
||||
const NSBorderlessWindowMask = 0;
|
||||
const NSTitledWindowMask = 1 << 0;
|
||||
const NSClosableWindowMask = 1 << 1;
|
||||
const NSMiniaturizableWindowMask = 1 << 2;
|
||||
const NSResizableWindowMask = 1 << 3;
|
||||
const NSTexturedBackgroundWindowMask = 1 << 8;
|
||||
const NSUnifiedTitleAndToolbarWindowMask = 1 << 12;
|
||||
const NSFullScreenWindowMask = 1 << 14;
|
||||
const NSFullSizeContentViewWindowMask = 1 << 15;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSWindowStyleMask {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(usize)] // NSUInteger
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSBackingStoreType {
|
||||
NSBackingStoreRetained = 0,
|
||||
NSBackingStoreNonretained = 1,
|
||||
NSBackingStoreBuffered = 2,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSBackingStoreType {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(usize)] // NSUInteger
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSWindowSharingType {
|
||||
NSWindowSharingNone = 0,
|
||||
NSWindowSharingReadOnly = 1,
|
||||
NSWindowSharingReadWrite = 2,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSWindowSharingType {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(isize)] // NSInteger
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSWindowOrderingMode {
|
||||
NSWindowAbove = 1,
|
||||
NSWindowBelow = -1,
|
||||
NSWindowOut = 0,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSWindowOrderingMode {
|
||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(isize)] // NSInteger
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum NSWindowTabbingMode {
|
||||
NSWindowTabbingModeAutomatic = 0,
|
||||
NSWindowTabbingModeDisallowed = 2,
|
||||
NSWindowTabbingModePreferred = 1,
|
||||
}
|
||||
|
||||
unsafe impl Encode for NSWindowTabbingMode {
|
||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
||||
}
|
||||
|
|
@ -17,15 +17,18 @@ use core_foundation::runloop::{
|
|||
kCFRunLoopCommonModes, CFRunLoopAddSource, CFRunLoopGetMain, CFRunLoopSourceContext,
|
||||
CFRunLoopSourceCreate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
|
||||
};
|
||||
use icrate::Foundation::MainThreadMarker;
|
||||
use objc2::rc::{autoreleasepool, Id};
|
||||
use objc2::runtime::NSObjectProtocol;
|
||||
use objc2::{msg_send_id, ClassType};
|
||||
|
||||
use super::{
|
||||
appkit::{NSApp, NSApplication, NSApplicationActivationPolicy, NSWindow},
|
||||
event::dummy_event,
|
||||
use icrate::AppKit::{
|
||||
NSApplication, NSApplicationActivationPolicyAccessory, NSApplicationActivationPolicyProhibited,
|
||||
NSApplicationActivationPolicyRegular, NSWindow,
|
||||
};
|
||||
use icrate::Foundation::{MainThreadMarker, NSObjectProtocol};
|
||||
use objc2::{msg_send_id, ClassType};
|
||||
use objc2::{
|
||||
rc::{autoreleasepool, Id},
|
||||
runtime::ProtocolObject,
|
||||
};
|
||||
|
||||
use super::event::dummy_event;
|
||||
use crate::{
|
||||
error::EventLoopError,
|
||||
event::Event,
|
||||
|
|
@ -126,19 +129,19 @@ impl<T: 'static> EventLoopWindowTarget<T> {
|
|||
|
||||
impl<T> EventLoopWindowTarget<T> {
|
||||
pub(crate) fn hide_application(&self) {
|
||||
NSApplication::shared(self.mtm).hide(None)
|
||||
NSApplication::sharedApplication(self.mtm).hide(None)
|
||||
}
|
||||
|
||||
pub(crate) fn hide_other_applications(&self) {
|
||||
NSApplication::shared(self.mtm).hideOtherApplications(None)
|
||||
NSApplication::sharedApplication(self.mtm).hideOtherApplications(None)
|
||||
}
|
||||
|
||||
pub(crate) fn set_allows_automatic_window_tabbing(&self, enabled: bool) {
|
||||
NSWindow::setAllowsAutomaticWindowTabbing(enabled)
|
||||
NSWindow::setAllowsAutomaticWindowTabbing(enabled, self.mtm)
|
||||
}
|
||||
|
||||
pub(crate) fn allows_automatic_window_tabbing(&self) -> bool {
|
||||
NSWindow::allowsAutomaticWindowTabbing()
|
||||
NSWindow::allowsAutomaticWindowTabbing(self.mtm)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -199,20 +202,20 @@ impl<T> EventLoop<T> {
|
|||
panic!("`winit` requires control over the principal class. You must create the event loop before other parts of your application initialize NSApplication");
|
||||
}
|
||||
|
||||
use NSApplicationActivationPolicy::*;
|
||||
let activation_policy = match attributes.activation_policy {
|
||||
ActivationPolicy::Regular => NSApplicationActivationPolicyRegular,
|
||||
ActivationPolicy::Accessory => NSApplicationActivationPolicyAccessory,
|
||||
ActivationPolicy::Prohibited => NSApplicationActivationPolicyProhibited,
|
||||
};
|
||||
let delegate = ApplicationDelegate::new(
|
||||
mtm,
|
||||
activation_policy,
|
||||
attributes.default_menu,
|
||||
attributes.activate_ignoring_other_apps,
|
||||
);
|
||||
|
||||
autoreleasepool(|_| {
|
||||
app.setDelegate(&delegate);
|
||||
app.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
|
||||
});
|
||||
|
||||
let panic_info: Rc<PanicInfo> = Default::default();
|
||||
|
|
@ -310,7 +313,7 @@ impl<T> EventLoop<T> {
|
|||
|
||||
// While the app is running it's possible that we catch a panic
|
||||
// to avoid unwinding across an objective-c ffi boundary, which
|
||||
// will lead to us stopping the `NSApp` and saving the
|
||||
// will lead to us stopping the `NSApplication` and saving the
|
||||
// `PanicInfo` so that we can resume the unwind at a controlled,
|
||||
// safe point in time.
|
||||
if let Some(panic) = self.panic_info.take() {
|
||||
|
|
@ -358,8 +361,6 @@ impl<T> EventLoop<T> {
|
|||
self._callback = Some(Rc::clone(&callback));
|
||||
|
||||
autoreleasepool(|_| {
|
||||
let app = NSApp();
|
||||
|
||||
// A bit of juggling with the callback references to make sure
|
||||
// that `self.callback` is the only owner of the callback.
|
||||
let weak_cb: Weak<_> = Rc::downgrade(&callback);
|
||||
|
|
@ -380,25 +381,24 @@ impl<T> EventLoop<T> {
|
|||
// catch panics to make sure we can't unwind without clearing the set callback
|
||||
// (which would leave the global `AppState` in an undefined, unsafe state)
|
||||
let catch_result = catch_unwind(AssertUnwindSafe(|| {
|
||||
// As a special case, if the `NSApp` hasn't been launched yet then we at least run
|
||||
// As a special case, if the application hasn't been launched yet then we at least run
|
||||
// the loop until it has fully launched.
|
||||
if !AppState::is_launched() {
|
||||
debug_assert!(!AppState::is_running());
|
||||
|
||||
AppState::request_stop_on_launch();
|
||||
unsafe {
|
||||
app.run();
|
||||
self.app.run();
|
||||
}
|
||||
|
||||
// Note: we dispatch `NewEvents(Init)` + `Resumed` events after the `NSApp` has launched
|
||||
// Note: we dispatch `NewEvents(Init)` + `Resumed` events after the application has launched
|
||||
} else if !AppState::is_running() {
|
||||
// Even though the NSApp may have been launched, it's possible we aren't running
|
||||
// Even though the application may have been launched, it's possible we aren't running
|
||||
// if the `EventLoop` was run before and has since exited. This indicates that
|
||||
// we just starting to re-run the same `EventLoop` again.
|
||||
AppState::start_running(); // Set is_running = true + dispatch `NewEvents(Init)` + `Resumed`
|
||||
} else {
|
||||
// Only run the NSApp for as long as the given `Duration` allows so we
|
||||
// don't block the external loop.
|
||||
// Only run for as long as the given `Duration` allows so we don't block the external loop.
|
||||
match timeout {
|
||||
Some(Duration::ZERO) => {
|
||||
AppState::set_wait_timeout(None);
|
||||
|
|
@ -418,13 +418,13 @@ impl<T> EventLoop<T> {
|
|||
}
|
||||
AppState::set_stop_app_on_redraw_requested(true);
|
||||
unsafe {
|
||||
app.run();
|
||||
self.app.run();
|
||||
}
|
||||
}
|
||||
|
||||
// While the app is running it's possible that we catch a panic
|
||||
// to avoid unwinding across an objective-c ffi boundary, which
|
||||
// will lead to us stopping the `NSApp` and saving the
|
||||
// will lead to us stopping the application and saving the
|
||||
// `PanicInfo` so that we can resume the unwind at a controlled,
|
||||
// safe point in time.
|
||||
if let Some(panic) = self.panic_info.take() {
|
||||
|
|
@ -462,6 +462,7 @@ impl<T> EventLoop<T> {
|
|||
/// happens, stops the `sharedApplication`
|
||||
#[inline]
|
||||
pub fn stop_app_on_panic<F: FnOnce() -> R + UnwindSafe, R>(
|
||||
mtm: MainThreadMarker,
|
||||
panic_info: Weak<PanicInfo>,
|
||||
f: F,
|
||||
) -> Option<R> {
|
||||
|
|
@ -476,7 +477,7 @@ pub fn stop_app_on_panic<F: FnOnce() -> R + UnwindSafe, R>(
|
|||
let panic_info = panic_info.upgrade().unwrap();
|
||||
panic_info.set_panic(e);
|
||||
}
|
||||
let app = NSApp();
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
app.stop(None);
|
||||
// Posting a dummy event to get `stop` to take effect immediately.
|
||||
// See: https://stackoverflow.com/questions/48041279/stopping-the-nsapplication-main-event-loop/48064752#48064752
|
||||
|
|
|
|||
|
|
@ -210,3 +210,45 @@ extern "C" {
|
|||
unicodeString: *mut UniChar,
|
||||
) -> OSStatus;
|
||||
}
|
||||
|
||||
// CGWindowLevel.h
|
||||
//
|
||||
// Note: There are two different things at play in this header:
|
||||
// `CGWindowLevel` and `CGWindowLevelKey`.
|
||||
//
|
||||
// It seems like there was a push towards using "key" values instead of the
|
||||
// raw window level values, and then you were supposed to use
|
||||
// `CGWindowLevelForKey` to get the actual level.
|
||||
//
|
||||
// But the values that `NSWindowLevel` has are compiled in, and as such has
|
||||
// to remain ABI compatible, so they're safe for us to hardcode as well.
|
||||
#[allow(dead_code, non_upper_case_globals)]
|
||||
mod window_level {
|
||||
const kCGNumReservedWindowLevels: i32 = 16;
|
||||
const kCGNumReservedBaseWindowLevels: i32 = 5;
|
||||
|
||||
pub const kCGBaseWindowLevel: i32 = i32::MIN;
|
||||
pub const kCGMinimumWindowLevel: i32 = kCGBaseWindowLevel + kCGNumReservedBaseWindowLevels;
|
||||
pub const kCGMaximumWindowLevel: i32 = i32::MAX - kCGNumReservedWindowLevels;
|
||||
|
||||
pub const kCGDesktopWindowLevel: i32 = kCGMinimumWindowLevel + 20;
|
||||
pub const kCGDesktopIconWindowLevel: i32 = kCGDesktopWindowLevel + 20;
|
||||
pub const kCGBackstopMenuLevel: i32 = -20;
|
||||
pub const kCGNormalWindowLevel: i32 = 0;
|
||||
pub const kCGFloatingWindowLevel: i32 = 3;
|
||||
pub const kCGTornOffMenuWindowLevel: i32 = 3;
|
||||
pub const kCGModalPanelWindowLevel: i32 = 8;
|
||||
pub const kCGUtilityWindowLevel: i32 = 19;
|
||||
pub const kCGDockWindowLevel: i32 = 20;
|
||||
pub const kCGMainMenuWindowLevel: i32 = 24;
|
||||
pub const kCGStatusWindowLevel: i32 = 25;
|
||||
pub const kCGPopUpMenuWindowLevel: i32 = 101;
|
||||
pub const kCGOverlayWindowLevel: i32 = 102;
|
||||
pub const kCGHelpWindowLevel: i32 = 200;
|
||||
pub const kCGDraggingWindowLevel: i32 = 500;
|
||||
pub const kCGScreenSaverWindowLevel: i32 = 1000;
|
||||
pub const kCGAssistiveTechHighWindowLevel: i32 = 1500;
|
||||
pub const kCGCursorWindowLevel: i32 = kCGMaximumWindowLevel - 1;
|
||||
}
|
||||
|
||||
pub use window_level::*;
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
use icrate::AppKit::{
|
||||
NSEventModifierFlagCommand, NSEventModifierFlagOption, NSEventModifierFlags, NSMenu, NSMenuItem,
|
||||
NSApplication, NSEventModifierFlagCommand, NSEventModifierFlagOption, NSEventModifierFlags,
|
||||
NSMenu, NSMenuItem,
|
||||
};
|
||||
use icrate::Foundation::{ns_string, MainThreadMarker, NSProcessInfo, NSString};
|
||||
use objc2::rc::Id;
|
||||
use objc2::runtime::Sel;
|
||||
use objc2::sel;
|
||||
|
||||
use super::appkit::NSApp;
|
||||
|
||||
struct KeyEquivalent<'a> {
|
||||
key: &'a NSString,
|
||||
masks: Option<NSEventModifierFlags>,
|
||||
}
|
||||
|
||||
pub fn initialize(mtm: MainThreadMarker) {
|
||||
pub fn initialize(app: &NSApplication) {
|
||||
let mtm = MainThreadMarker::from(app);
|
||||
let menubar = NSMenu::new(mtm);
|
||||
let app_menu_item = NSMenuItem::new(mtm);
|
||||
menubar.addItem(&app_menu_item);
|
||||
|
|
@ -96,9 +96,8 @@ pub fn initialize(mtm: MainThreadMarker) {
|
|||
app_menu.addItem(&quit_item);
|
||||
app_menu_item.setSubmenu(Some(&app_menu));
|
||||
|
||||
let app = NSApp();
|
||||
app.setServicesMenu(&services_menu);
|
||||
app.setMainMenu(&menubar);
|
||||
unsafe { app.setServicesMenu(Some(&services_menu)) };
|
||||
app.setMainMenu(Some(&menubar));
|
||||
}
|
||||
|
||||
fn menu_item(
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ mod util;
|
|||
mod app;
|
||||
mod app_delegate;
|
||||
mod app_state;
|
||||
mod appkit;
|
||||
mod cursor;
|
||||
mod event;
|
||||
mod event_loop;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ use core_foundation::runloop::{
|
|||
CFRunLoopObserverRef, CFRunLoopRef, CFRunLoopTimerCreate, CFRunLoopTimerInvalidate,
|
||||
CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate,
|
||||
};
|
||||
use icrate::Foundation::MainThreadMarker;
|
||||
|
||||
unsafe fn control_flow_handler<F>(panic_info: *mut c_void, f: F)
|
||||
where
|
||||
|
|
@ -36,7 +37,8 @@ where
|
|||
// However we want to keep that weak reference around after the function.
|
||||
std::mem::forget(info_from_raw);
|
||||
|
||||
stop_app_on_panic(Weak::clone(&panic_info), move || {
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
stop_app_on_panic(mtm, Weak::clone(&panic_info), move || {
|
||||
let _ = &panic_info;
|
||||
f(panic_info.0)
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ use std::collections::{HashMap, VecDeque};
|
|||
use std::ptr;
|
||||
|
||||
use icrate::AppKit::{
|
||||
NSCursor, NSEvent, NSEventPhaseBegan, NSEventPhaseCancelled, NSEventPhaseChanged,
|
||||
NSEventPhaseEnded, NSEventPhaseMayBegin, NSResponder, NSTextInputClient, NSTrackingRectTag,
|
||||
NSView,
|
||||
NSApplication, NSCursor, NSEvent, NSEventPhaseBegan, NSEventPhaseCancelled,
|
||||
NSEventPhaseChanged, NSEventPhaseEnded, NSEventPhaseMayBegin, NSResponder, NSTextInputClient,
|
||||
NSTrackingRectTag, NSView,
|
||||
};
|
||||
use icrate::Foundation::{
|
||||
MainThreadMarker, NSArray, NSAttributedString, NSAttributedStringKey, NSCopying,
|
||||
|
|
@ -20,11 +20,8 @@ use objc2::{
|
|||
};
|
||||
|
||||
use super::cursor::{default_cursor, invisible_cursor};
|
||||
use super::event::{code_to_key, code_to_location};
|
||||
use super::event::{lalt_pressed, ralt_pressed};
|
||||
use super::{
|
||||
appkit::NSApp,
|
||||
event::{code_to_key, code_to_location},
|
||||
};
|
||||
use crate::{
|
||||
dpi::{LogicalPosition, LogicalSize},
|
||||
event::{
|
||||
|
|
@ -537,9 +534,10 @@ declare_class!(
|
|||
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=300620#c6
|
||||
#[method(cancelOperation:)]
|
||||
fn cancel_operation(&self, _sender: Option<&AnyObject>) {
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
trace_scope!("cancelOperation:");
|
||||
|
||||
let event = NSApp()
|
||||
let event = NSApplication::sharedApplication(mtm)
|
||||
.currentEvent()
|
||||
.expect("could not find current event");
|
||||
|
||||
|
|
|
|||
|
|
@ -29,26 +29,29 @@ use crate::{
|
|||
},
|
||||
};
|
||||
use core_graphics::display::{CGDisplay, CGPoint};
|
||||
use icrate::AppKit::NSWindowAbove;
|
||||
use icrate::AppKit::{
|
||||
NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSColor,
|
||||
NSFilenamesPboardType, NSResponder, NSScreen, NSView,
|
||||
NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSApplication,
|
||||
NSApplicationPresentationAutoHideDock, NSApplicationPresentationAutoHideMenuBar,
|
||||
NSApplicationPresentationFullScreen, NSApplicationPresentationHideDock,
|
||||
NSApplicationPresentationHideMenuBar, NSApplicationPresentationOptions, NSBackingStoreBuffered,
|
||||
NSColor, NSCriticalRequest, NSFilenamesPboardType, NSInformationalRequest, NSResponder,
|
||||
NSScreen, NSView, NSWindow, NSWindowAbove, NSWindowCloseButton, NSWindowFullScreenButton,
|
||||
NSWindowLevel, NSWindowMiniaturizeButton, NSWindowSharingNone, NSWindowSharingReadOnly,
|
||||
NSWindowStyleMask, NSWindowStyleMaskBorderless, NSWindowStyleMaskClosable,
|
||||
NSWindowStyleMaskFullSizeContentView, NSWindowStyleMaskMiniaturizable,
|
||||
NSWindowStyleMaskResizable, NSWindowStyleMaskTitled, NSWindowTabbingModePreferred,
|
||||
NSWindowTitleHidden, NSWindowZoomButton,
|
||||
};
|
||||
use icrate::Foundation::{
|
||||
CGFloat, MainThreadBound, MainThreadMarker, NSArray, NSCopying, NSInteger, NSObject, NSPoint,
|
||||
NSRect, NSSize, NSString,
|
||||
CGFloat, MainThreadBound, MainThreadMarker, NSArray, NSCopying, NSObject, NSPoint, NSRect,
|
||||
NSSize, NSString,
|
||||
};
|
||||
use objc2::rc::{autoreleasepool, Id};
|
||||
use objc2::{declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass};
|
||||
|
||||
use super::appkit::{
|
||||
NSApp, NSApplicationPresentationOptions, NSBackingStoreType, NSRequestUserAttentionType,
|
||||
NSWindow, NSWindowButton, NSWindowLevel, NSWindowSharingType, NSWindowStyleMask,
|
||||
NSWindowTabbingMode, NSWindowTitleVisibility,
|
||||
};
|
||||
use super::cursor::cursor_from_icon;
|
||||
use super::ffi::CGSMainConnectionID;
|
||||
use super::ffi::CGSSetWindowBackgroundBlurRadius;
|
||||
use super::ffi::kCGFloatingWindowLevel;
|
||||
use super::ffi::{kCGNormalWindowLevel, CGSMainConnectionID, CGSSetWindowBackgroundBlurRadius};
|
||||
use super::monitor::get_display_id;
|
||||
|
||||
pub(crate) struct Window {
|
||||
|
|
@ -319,31 +322,31 @@ impl WinitWindow {
|
|||
// if decorations is set to false, ignore pl_attrs
|
||||
//
|
||||
// if the titlebar is hidden, ignore other pl_attrs
|
||||
NSWindowStyleMask::NSBorderlessWindowMask
|
||||
| NSWindowStyleMask::NSResizableWindowMask
|
||||
| NSWindowStyleMask::NSMiniaturizableWindowMask
|
||||
NSWindowStyleMaskBorderless
|
||||
| NSWindowStyleMaskResizable
|
||||
| NSWindowStyleMaskMiniaturizable
|
||||
} else {
|
||||
// default case, resizable window with titlebar and titlebar buttons
|
||||
NSWindowStyleMask::NSClosableWindowMask
|
||||
| NSWindowStyleMask::NSMiniaturizableWindowMask
|
||||
| NSWindowStyleMask::NSResizableWindowMask
|
||||
| NSWindowStyleMask::NSTitledWindowMask
|
||||
NSWindowStyleMaskClosable
|
||||
| NSWindowStyleMaskMiniaturizable
|
||||
| NSWindowStyleMaskResizable
|
||||
| NSWindowStyleMaskTitled
|
||||
};
|
||||
|
||||
if !attrs.resizable {
|
||||
masks &= !NSWindowStyleMask::NSResizableWindowMask;
|
||||
masks &= !NSWindowStyleMaskResizable;
|
||||
}
|
||||
|
||||
if !attrs.enabled_buttons.contains(WindowButtons::MINIMIZE) {
|
||||
masks &= !NSWindowStyleMask::NSMiniaturizableWindowMask;
|
||||
masks &= !NSWindowStyleMaskMiniaturizable;
|
||||
}
|
||||
|
||||
if !attrs.enabled_buttons.contains(WindowButtons::CLOSE) {
|
||||
masks &= !NSWindowStyleMask::NSClosableWindowMask;
|
||||
masks &= !NSWindowStyleMaskClosable;
|
||||
}
|
||||
|
||||
if pl_attrs.fullsize_content_view {
|
||||
masks |= NSWindowStyleMask::NSFullSizeContentViewWindowMask;
|
||||
masks |= NSWindowStyleMaskFullSizeContentView;
|
||||
}
|
||||
|
||||
let state = SharedState {
|
||||
|
|
@ -359,7 +362,7 @@ impl WinitWindow {
|
|||
super(this),
|
||||
initWithContentRect: frame,
|
||||
styleMask: masks,
|
||||
backing: NSBackingStoreType::NSBackingStoreBuffered,
|
||||
backing: NSBackingStoreBuffered,
|
||||
defer: false,
|
||||
]
|
||||
};
|
||||
|
|
@ -368,7 +371,7 @@ impl WinitWindow {
|
|||
// It is very important for correct memory management that we
|
||||
// disable the extra release that would otherwise happen when
|
||||
// calling `close` on the window.
|
||||
this.setReleasedWhenClosed(false);
|
||||
unsafe { this.setReleasedWhenClosed(false) };
|
||||
|
||||
let resize_increments = match attrs
|
||||
.resize_increments
|
||||
|
|
@ -387,26 +390,26 @@ impl WinitWindow {
|
|||
|
||||
if let Some(identifier) = pl_attrs.tabbing_identifier {
|
||||
this.setTabbingIdentifier(&NSString::from_str(&identifier));
|
||||
this.setTabbingMode(NSWindowTabbingMode::NSWindowTabbingModePreferred);
|
||||
this.setTabbingMode(NSWindowTabbingModePreferred);
|
||||
}
|
||||
|
||||
if attrs.content_protected {
|
||||
this.setSharingType(NSWindowSharingType::NSWindowSharingNone);
|
||||
this.setSharingType(NSWindowSharingNone);
|
||||
}
|
||||
|
||||
if pl_attrs.titlebar_transparent {
|
||||
this.setTitlebarAppearsTransparent(true);
|
||||
}
|
||||
if pl_attrs.title_hidden {
|
||||
this.setTitleVisibility(NSWindowTitleVisibility::Hidden);
|
||||
this.setTitleVisibility(NSWindowTitleHidden);
|
||||
}
|
||||
if pl_attrs.titlebar_buttons_hidden {
|
||||
for titlebar_button in &[
|
||||
#[allow(deprecated)]
|
||||
NSWindowButton::FullScreen,
|
||||
NSWindowButton::Miniaturize,
|
||||
NSWindowButton::Close,
|
||||
NSWindowButton::Zoom,
|
||||
NSWindowFullScreenButton,
|
||||
NSWindowMiniaturizeButton,
|
||||
NSWindowCloseButton,
|
||||
NSWindowZoomButton,
|
||||
] {
|
||||
if let Some(button) = this.standardWindowButton(*titlebar_button) {
|
||||
button.setHidden(true);
|
||||
|
|
@ -418,7 +421,7 @@ impl WinitWindow {
|
|||
}
|
||||
|
||||
if !attrs.enabled_buttons.contains(WindowButtons::MAXIMIZE) {
|
||||
if let Some(button) = this.standardWindowButton(NSWindowButton::Zoom) {
|
||||
if let Some(button) = this.standardWindowButton(NSWindowZoomButton) {
|
||||
button.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
|
@ -449,11 +452,9 @@ impl WinitWindow {
|
|||
))
|
||||
})?;
|
||||
|
||||
// TODO(madsmtm): Remove this
|
||||
let window: Id<icrate::AppKit::NSWindow> = unsafe { Id::cast(this.clone()) };
|
||||
// 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, NSWindowAbove) };
|
||||
unsafe { parent.addChildWindow_ordered(&this, NSWindowAbove) };
|
||||
}
|
||||
Some(raw) => panic!("Invalid raw window handle {raw:?} on macOS"),
|
||||
None => (),
|
||||
|
|
@ -477,12 +478,12 @@ impl WinitWindow {
|
|||
}
|
||||
|
||||
// Configure the new view as the "key view" for the window
|
||||
this.setContentView(&view);
|
||||
this.setInitialFirstResponder(&view);
|
||||
this.setContentView(Some(&view));
|
||||
this.setInitialFirstResponder(Some(&view));
|
||||
|
||||
if attrs.transparent {
|
||||
this.setOpaque(false);
|
||||
this.setBackgroundColor(unsafe { &NSColor::clearColor() });
|
||||
this.setBackgroundColor(Some(unsafe { &NSColor::clearColor() }));
|
||||
}
|
||||
|
||||
if attrs.blur {
|
||||
|
|
@ -505,13 +506,13 @@ impl WinitWindow {
|
|||
|
||||
match attrs.preferred_theme {
|
||||
Some(theme) => {
|
||||
set_ns_theme(Some(theme));
|
||||
set_ns_theme(Some(theme), mtm);
|
||||
let mut state = this.lock_shared_state("WinitWindow::new");
|
||||
state.current_theme = Some(theme);
|
||||
}
|
||||
None => {
|
||||
let mut state = this.lock_shared_state("WinitWindow::new");
|
||||
state.current_theme = Some(get_ns_theme());
|
||||
state.current_theme = Some(get_ns_theme(mtm));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -543,9 +544,10 @@ impl WinitWindow {
|
|||
Ok((this, delegate))
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub(super) fn view(&self) -> Id<WinitView> {
|
||||
// SAFETY: The view inside WinitWindow is always `WinitView`
|
||||
unsafe { Id::cast(self.contentView()) }
|
||||
unsafe { Id::cast(self.contentView().unwrap()) }
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
|
|
@ -560,7 +562,7 @@ impl WinitWindow {
|
|||
self.setStyleMask(mask);
|
||||
// If we don't do this, key handling will break
|
||||
// (at least until the window is clicked again/etc.)
|
||||
let _ = self.makeFirstResponder(Some(&self.contentView()));
|
||||
let _ = self.makeFirstResponder(Some(&self.contentView().unwrap()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -581,7 +583,7 @@ impl WinitWindow {
|
|||
// NOTE: in general we want to specify the blur radius, but the choice of 80
|
||||
// should be a reasonable default.
|
||||
let radius = if blur { 80 } else { 0 };
|
||||
let window_number = self.windowNumber();
|
||||
let window_number = unsafe { self.windowNumber() };
|
||||
unsafe {
|
||||
CGSSetWindowBackgroundBlurRadius(CGSMainConnectionID(), window_number, radius);
|
||||
}
|
||||
|
|
@ -634,7 +636,7 @@ impl WinitWindow {
|
|||
|
||||
#[inline]
|
||||
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
||||
let frame = self.contentView().frame();
|
||||
let frame = self.contentView().unwrap().frame();
|
||||
let logical: LogicalSize<f64> = (frame.size.width as f64, frame.size.height as f64).into();
|
||||
let scale_factor = self.scale_factor();
|
||||
logical.to_physical(scale_factor)
|
||||
|
|
@ -756,9 +758,9 @@ impl WinitWindow {
|
|||
if !fullscreen {
|
||||
let mut mask = self.styleMask();
|
||||
if resizable {
|
||||
mask |= NSWindowStyleMask::NSResizableWindowMask;
|
||||
mask |= NSWindowStyleMaskResizable;
|
||||
} else {
|
||||
mask &= !NSWindowStyleMask::NSResizableWindowMask;
|
||||
mask &= !NSWindowStyleMaskResizable;
|
||||
}
|
||||
self.set_style_mask(mask);
|
||||
}
|
||||
|
|
@ -775,15 +777,15 @@ impl WinitWindow {
|
|||
let mut mask = self.styleMask();
|
||||
|
||||
if buttons.contains(WindowButtons::CLOSE) {
|
||||
mask |= NSWindowStyleMask::NSClosableWindowMask;
|
||||
mask |= NSWindowStyleMaskClosable;
|
||||
} else {
|
||||
mask &= !NSWindowStyleMask::NSClosableWindowMask;
|
||||
mask &= !NSWindowStyleMaskClosable;
|
||||
}
|
||||
|
||||
if buttons.contains(WindowButtons::MINIMIZE) {
|
||||
mask |= NSWindowStyleMask::NSMiniaturizableWindowMask;
|
||||
mask |= NSWindowStyleMaskMiniaturizable;
|
||||
} else {
|
||||
mask &= !NSWindowStyleMask::NSMiniaturizableWindowMask;
|
||||
mask &= !NSWindowStyleMaskMiniaturizable;
|
||||
}
|
||||
|
||||
// This must happen before the button's "enabled" status has been set,
|
||||
|
|
@ -793,7 +795,7 @@ impl WinitWindow {
|
|||
// 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.standardWindowButton(NSWindowButton::Zoom) {
|
||||
if let Some(button) = self.standardWindowButton(NSWindowZoomButton) {
|
||||
button.setEnabled(buttons.contains(WindowButtons::MAXIMIZE));
|
||||
}
|
||||
}
|
||||
|
|
@ -805,7 +807,7 @@ impl WinitWindow {
|
|||
buttons |= WindowButtons::MINIMIZE;
|
||||
}
|
||||
if self
|
||||
.standardWindowButton(NSWindowButton::Zoom)
|
||||
.standardWindowButton(NSWindowZoomButton)
|
||||
.map(|b| b.isEnabled())
|
||||
.unwrap_or(true)
|
||||
{
|
||||
|
|
@ -879,8 +881,11 @@ impl WinitWindow {
|
|||
|
||||
#[inline]
|
||||
pub fn drag_window(&self) -> Result<(), ExternalError> {
|
||||
let event = NSApp().currentEvent();
|
||||
self.performWindowDragWithEvent(event.as_deref());
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
let event = NSApplication::sharedApplication(mtm)
|
||||
.currentEvent()
|
||||
.unwrap();
|
||||
self.performWindowDragWithEvent(&event);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -903,9 +908,8 @@ impl WinitWindow {
|
|||
// we make it resizable temporalily.
|
||||
let curr_mask = self.styleMask();
|
||||
|
||||
let required =
|
||||
NSWindowStyleMask::NSTitledWindowMask | NSWindowStyleMask::NSResizableWindowMask;
|
||||
let needs_temp_mask = !curr_mask.contains(required);
|
||||
let required = NSWindowStyleMaskTitled | NSWindowStyleMaskResizable;
|
||||
let needs_temp_mask = !mask_contains(curr_mask, required);
|
||||
if needs_temp_mask {
|
||||
self.set_style_mask(required);
|
||||
}
|
||||
|
|
@ -926,9 +930,9 @@ impl WinitWindow {
|
|||
.take()
|
||||
.unwrap_or_else(|| self.styleMask());
|
||||
if shared_state.resizable {
|
||||
base_mask | NSWindowStyleMask::NSResizableWindowMask
|
||||
base_mask | NSWindowStyleMaskResizable
|
||||
} else {
|
||||
base_mask & !NSWindowStyleMask::NSResizableWindowMask
|
||||
base_mask & !NSWindowStyleMaskResizable
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -959,7 +963,7 @@ impl WinitWindow {
|
|||
if minimized {
|
||||
self.miniaturize(Some(self));
|
||||
} else {
|
||||
self.deminiaturize(Some(self));
|
||||
unsafe { self.deminiaturize(Some(self)) };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -989,10 +993,7 @@ impl WinitWindow {
|
|||
return;
|
||||
}
|
||||
|
||||
if self
|
||||
.styleMask()
|
||||
.contains(NSWindowStyleMask::NSResizableWindowMask)
|
||||
{
|
||||
if mask_contains(self.styleMask(), NSWindowStyleMaskResizable) {
|
||||
drop(shared_state);
|
||||
// Just use the native zoom if resizable
|
||||
self.zoom(None);
|
||||
|
|
@ -1023,6 +1024,8 @@ impl WinitWindow {
|
|||
#[inline]
|
||||
pub(crate) fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
|
||||
let mut shared_state_lock = self.lock_shared_state("set_fullscreen");
|
||||
if shared_state_lock.is_simple_fullscreen {
|
||||
return;
|
||||
|
|
@ -1085,7 +1088,6 @@ impl WinitWindow {
|
|||
let mut fade_token = ffi::kCGDisplayFadeReservationInvalidToken;
|
||||
|
||||
if matches!(old_fullscreen, Some(Fullscreen::Borderless(_))) {
|
||||
let app = NSApp();
|
||||
let mut shared_state_lock = self.lock_shared_state("set_fullscreen");
|
||||
shared_state_lock.save_presentation_opts = Some(app.presentationOptions());
|
||||
}
|
||||
|
|
@ -1143,7 +1145,7 @@ impl WinitWindow {
|
|||
// Window level must be restored from `CGShieldingWindowLevel()
|
||||
// + 1` back to normal in order for `toggleFullScreen` to do
|
||||
// anything
|
||||
window.setLevel(NSWindowLevel::Normal);
|
||||
window.setLevel(kCGNormalWindowLevel as NSWindowLevel);
|
||||
window.toggleFullScreen(None);
|
||||
}
|
||||
|
||||
|
|
@ -1153,9 +1155,8 @@ impl WinitWindow {
|
|||
// set a normal style temporarily. The previous state will be
|
||||
// restored in `WindowDelegate::window_did_exit_fullscreen`.
|
||||
let curr_mask = self.styleMask();
|
||||
let required = NSWindowStyleMask::NSTitledWindowMask
|
||||
| NSWindowStyleMask::NSResizableWindowMask;
|
||||
if !curr_mask.contains(required) {
|
||||
let required = NSWindowStyleMaskTitled | NSWindowStyleMaskResizable;
|
||||
if !mask_contains(curr_mask, required) {
|
||||
self.set_style_mask(required);
|
||||
self.lock_shared_state("set_fullscreen").saved_style = Some(curr_mask);
|
||||
}
|
||||
|
|
@ -1184,30 +1185,27 @@ impl WinitWindow {
|
|||
// of the menu bar, and this looks broken, so we must make sure
|
||||
// that the menu bar is disabled. This is done in the window
|
||||
// delegate in `window:willUseFullScreenPresentationOptions:`.
|
||||
let app = NSApp();
|
||||
self.lock_shared_state("set_fullscreen")
|
||||
.save_presentation_opts = Some(app.presentationOptions());
|
||||
|
||||
let presentation_options =
|
||||
NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar;
|
||||
let presentation_options = NSApplicationPresentationFullScreen
|
||||
| NSApplicationPresentationHideDock
|
||||
| NSApplicationPresentationHideMenuBar;
|
||||
app.setPresentationOptions(presentation_options);
|
||||
|
||||
let window_level =
|
||||
NSWindowLevel(unsafe { ffi::CGShieldingWindowLevel() } as NSInteger + 1);
|
||||
let window_level = unsafe { ffi::CGShieldingWindowLevel() } as NSWindowLevel + 1;
|
||||
self.setLevel(window_level);
|
||||
}
|
||||
(Some(Fullscreen::Exclusive(ref video_mode)), Some(Fullscreen::Borderless(_))) => {
|
||||
let presentation_options = self
|
||||
.lock_shared_state("set_fullscreen")
|
||||
.save_presentation_opts
|
||||
.unwrap_or_else(|| {
|
||||
NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideMenuBar
|
||||
});
|
||||
NSApp().setPresentationOptions(presentation_options);
|
||||
.unwrap_or(
|
||||
NSApplicationPresentationFullScreen
|
||||
| NSApplicationPresentationAutoHideDock
|
||||
| NSApplicationPresentationAutoHideMenuBar,
|
||||
);
|
||||
app.setPresentationOptions(presentation_options);
|
||||
|
||||
unsafe {
|
||||
ffi::CGRestorePermanentDisplayConfiguration();
|
||||
|
|
@ -1219,7 +1217,7 @@ impl WinitWindow {
|
|||
|
||||
// Restore the normal window level following the Borderless fullscreen
|
||||
// `CGShieldingWindowLevel() + 1` hack.
|
||||
self.setLevel(NSWindowLevel::Normal);
|
||||
self.setLevel(kCGNormalWindowLevel as NSWindowLevel);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
|
@ -1247,15 +1245,15 @@ impl WinitWindow {
|
|||
|
||||
let new_mask = {
|
||||
let mut new_mask = if decorations {
|
||||
NSWindowStyleMask::NSClosableWindowMask
|
||||
| NSWindowStyleMask::NSMiniaturizableWindowMask
|
||||
| NSWindowStyleMask::NSResizableWindowMask
|
||||
| NSWindowStyleMask::NSTitledWindowMask
|
||||
NSWindowStyleMaskClosable
|
||||
| NSWindowStyleMaskMiniaturizable
|
||||
| NSWindowStyleMaskResizable
|
||||
| NSWindowStyleMaskTitled
|
||||
} else {
|
||||
NSWindowStyleMask::NSBorderlessWindowMask | NSWindowStyleMask::NSResizableWindowMask
|
||||
NSWindowStyleMaskBorderless | NSWindowStyleMaskResizable
|
||||
};
|
||||
if !resizable {
|
||||
new_mask &= !NSWindowStyleMask::NSResizableWindowMask;
|
||||
new_mask &= !NSWindowStyleMaskResizable;
|
||||
}
|
||||
new_mask
|
||||
};
|
||||
|
|
@ -1270,9 +1268,9 @@ impl WinitWindow {
|
|||
#[inline]
|
||||
pub fn set_window_level(&self, level: WindowLevel) {
|
||||
let level = match level {
|
||||
WindowLevel::AlwaysOnTop => NSWindowLevel::Floating,
|
||||
WindowLevel::AlwaysOnBottom => NSWindowLevel::BELOW_NORMAL,
|
||||
WindowLevel::Normal => NSWindowLevel::Normal,
|
||||
WindowLevel::AlwaysOnTop => kCGFloatingWindowLevel as NSWindowLevel,
|
||||
WindowLevel::AlwaysOnBottom => (kCGNormalWindowLevel - 1) as NSWindowLevel,
|
||||
WindowLevel::Normal => kCGNormalWindowLevel as NSWindowLevel,
|
||||
};
|
||||
self.setLevel(level);
|
||||
}
|
||||
|
|
@ -1307,23 +1305,26 @@ impl WinitWindow {
|
|||
|
||||
#[inline]
|
||||
pub fn focus_window(&self) {
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
let is_minimized = self.isMiniaturized();
|
||||
let is_visible = self.isVisible();
|
||||
|
||||
if !is_minimized && is_visible {
|
||||
NSApp().activateIgnoringOtherApps(true);
|
||||
#[allow(deprecated)]
|
||||
NSApplication::sharedApplication(mtm).activateIgnoringOtherApps(true);
|
||||
self.makeKeyAndOrderFront(None);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
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 => NSCriticalRequest,
|
||||
UserAttentionType::Informational => NSInformationalRequest,
|
||||
});
|
||||
if let Some(ty) = ns_request_type {
|
||||
NSApp().requestUserAttention(ty);
|
||||
NSApplication::sharedApplication(mtm).requestUserAttention(ty);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1355,7 +1356,7 @@ impl WinitWindow {
|
|||
pub fn raw_window_handle_rwh_04(&self) -> rwh_04::RawWindowHandle {
|
||||
let mut window_handle = rwh_04::AppKitHandle::empty();
|
||||
window_handle.ns_window = self as *const Self as *mut _;
|
||||
window_handle.ns_view = Id::as_ptr(&self.contentView()) as *mut _;
|
||||
window_handle.ns_view = Id::as_ptr(&self.contentView().unwrap()) as *mut _;
|
||||
rwh_04::RawWindowHandle::AppKit(window_handle)
|
||||
}
|
||||
|
||||
|
|
@ -1364,7 +1365,7 @@ impl WinitWindow {
|
|||
pub fn raw_window_handle_rwh_05(&self) -> rwh_05::RawWindowHandle {
|
||||
let mut window_handle = rwh_05::AppKitWindowHandle::empty();
|
||||
window_handle.ns_window = self as *const Self as *mut _;
|
||||
window_handle.ns_view = Id::as_ptr(&self.contentView()) as *mut _;
|
||||
window_handle.ns_view = Id::as_ptr(&self.contentView().unwrap()) as *mut _;
|
||||
rwh_05::RawWindowHandle::AppKit(window_handle)
|
||||
}
|
||||
|
||||
|
|
@ -1378,7 +1379,7 @@ impl WinitWindow {
|
|||
#[inline]
|
||||
pub fn raw_window_handle_rwh_06(&self) -> rwh_06::RawWindowHandle {
|
||||
let window_handle = rwh_06::AppKitWindowHandle::new({
|
||||
let ptr = Id::as_ptr(&self.contentView()) as *mut _;
|
||||
let ptr = Id::as_ptr(&self.contentView().unwrap()) as *mut _;
|
||||
std::ptr::NonNull::new(ptr).expect("Id<T> should never be null")
|
||||
});
|
||||
rwh_06::RawWindowHandle::AppKit(window_handle)
|
||||
|
|
@ -1414,21 +1415,24 @@ impl WinitWindow {
|
|||
}
|
||||
|
||||
pub fn set_theme(&self, theme: Option<Theme>) {
|
||||
set_ns_theme(theme);
|
||||
self.lock_shared_state("set_theme").current_theme = theme.or_else(|| Some(get_ns_theme()));
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
set_ns_theme(theme, mtm);
|
||||
self.lock_shared_state("set_theme").current_theme =
|
||||
theme.or_else(|| Some(get_ns_theme(mtm)));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_content_protected(&self, protected: bool) {
|
||||
self.setSharingType(if protected {
|
||||
NSWindowSharingType::NSWindowSharingNone
|
||||
NSWindowSharingNone
|
||||
} else {
|
||||
NSWindowSharingType::NSWindowSharingReadOnly
|
||||
NSWindowSharingReadOnly
|
||||
})
|
||||
}
|
||||
|
||||
pub fn title(&self) -> String {
|
||||
self.title_().to_string()
|
||||
let window: &NSWindow = self;
|
||||
window.title().to_string()
|
||||
}
|
||||
|
||||
pub fn reset_dead_keys(&self) {
|
||||
|
|
@ -1445,9 +1449,10 @@ impl WindowExtMacOS for WinitWindow {
|
|||
|
||||
#[inline]
|
||||
fn set_simple_fullscreen(&self, fullscreen: bool) -> bool {
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
let mut shared_state_lock = self.lock_shared_state("set_simple_fullscreen");
|
||||
|
||||
let app = NSApp();
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
let is_native_fullscreen = shared_state_lock.fullscreen.is_some();
|
||||
let is_simple_fullscreen = shared_state_lock.is_simple_fullscreen;
|
||||
|
||||
|
|
@ -1475,20 +1480,19 @@ impl WindowExtMacOS for WinitWindow {
|
|||
|
||||
// Simulate pre-Lion fullscreen by hiding the dock and menu bar
|
||||
let presentation_options =
|
||||
NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideMenuBar;
|
||||
NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar;
|
||||
app.setPresentationOptions(presentation_options);
|
||||
|
||||
// Hide the titlebar
|
||||
self.toggle_style_mask(NSWindowStyleMask::NSTitledWindowMask, false);
|
||||
self.toggle_style_mask(NSWindowStyleMaskTitled, false);
|
||||
|
||||
// Set the window frame to the screen frame size
|
||||
let screen = self.screen().expect("expected screen to be available");
|
||||
self.setFrame_display(screen.frame(), true);
|
||||
|
||||
// Fullscreen windows can't be resized, minimized, or moved
|
||||
self.toggle_style_mask(NSWindowStyleMask::NSMiniaturizableWindowMask, false);
|
||||
self.toggle_style_mask(NSWindowStyleMask::NSResizableWindowMask, false);
|
||||
self.toggle_style_mask(NSWindowStyleMaskMiniaturizable, false);
|
||||
self.toggle_style_mask(NSWindowStyleMaskResizable, false);
|
||||
self.setMovable(false);
|
||||
|
||||
true
|
||||
|
|
@ -1537,24 +1541,20 @@ impl WindowExtMacOS for WinitWindow {
|
|||
|
||||
#[inline]
|
||||
fn select_next_tab(&self) {
|
||||
if let Some(group) = self.tabGroup() {
|
||||
group.selectNextTab();
|
||||
}
|
||||
self.selectNextTab(None)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn select_previous_tab(&self) {
|
||||
if let Some(group) = self.tabGroup() {
|
||||
group.selectPreviousTab()
|
||||
}
|
||||
unsafe { self.selectPreviousTab(None) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn select_tab_at_index(&self, index: usize) {
|
||||
if let Some(group) = self.tabGroup() {
|
||||
if let Some(windows) = group.tabbedWindows() {
|
||||
if let Some(windows) = unsafe { self.tabbedWindows() } {
|
||||
if index < windows.len() {
|
||||
group.setSelectedWindow(&windows[index]);
|
||||
group.setSelectedWindow(Some(&windows[index]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1562,8 +1562,7 @@ impl WindowExtMacOS for WinitWindow {
|
|||
|
||||
#[inline]
|
||||
fn num_tabs(&self) -> usize {
|
||||
self.tabGroup()
|
||||
.and_then(|group| group.tabbedWindows())
|
||||
unsafe { self.tabbedWindows() }
|
||||
.map(|windows| windows.len())
|
||||
.unwrap_or(1)
|
||||
}
|
||||
|
|
@ -1587,8 +1586,12 @@ impl WindowExtMacOS for WinitWindow {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn get_ns_theme() -> Theme {
|
||||
let app = NSApp();
|
||||
fn mask_contains(mask: NSWindowStyleMask, value: NSWindowStyleMask) -> bool {
|
||||
mask & value == value
|
||||
}
|
||||
|
||||
pub(super) fn get_ns_theme(mtm: MainThreadMarker) -> Theme {
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
let has_theme: bool = unsafe { msg_send![&app, respondsToSelector: sel!(effectiveAppearance)] };
|
||||
if !has_theme {
|
||||
return Theme::Light;
|
||||
|
|
@ -1606,8 +1609,8 @@ pub(super) fn get_ns_theme() -> Theme {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_ns_theme(theme: Option<Theme>) {
|
||||
let app = NSApp();
|
||||
fn set_ns_theme(theme: Option<Theme>, mtm: MainThreadMarker) {
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
let has_theme: bool = unsafe { msg_send![&app, respondsToSelector: sel!(effectiveAppearance)] };
|
||||
if has_theme {
|
||||
let appearance = theme.map(|t| {
|
||||
|
|
|
|||
|
|
@ -2,15 +2,18 @@
|
|||
use std::cell::Cell;
|
||||
use std::ptr;
|
||||
|
||||
use icrate::AppKit::{NSFilenamesPboardType, NSPasteboard};
|
||||
use icrate::Foundation::{NSArray, NSObject, NSSize, NSString};
|
||||
use icrate::AppKit::{
|
||||
NSApplicationPresentationFullScreen, NSApplicationPresentationHideDock,
|
||||
NSApplicationPresentationHideMenuBar, NSApplicationPresentationOptions, NSDraggingDestination,
|
||||
NSFilenamesPboardType, NSPasteboard, NSWindowDelegate, NSWindowOcclusionStateVisible,
|
||||
};
|
||||
use icrate::Foundation::{MainThreadMarker, NSArray, NSObject, NSObjectProtocol, NSSize, NSString};
|
||||
use objc2::rc::{autoreleasepool, Id};
|
||||
use objc2::runtime::AnyObject;
|
||||
use objc2::runtime::{AnyObject, ProtocolObject};
|
||||
use objc2::{
|
||||
class, declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass,
|
||||
};
|
||||
|
||||
use super::appkit::{NSApplicationPresentationOptions, NSWindowOcclusionState};
|
||||
use super::{
|
||||
app_state::AppState,
|
||||
util,
|
||||
|
|
@ -43,7 +46,7 @@ declare_class!(
|
|||
|
||||
unsafe impl ClassType for WinitWindowDelegate {
|
||||
type Super = NSObject;
|
||||
type Mutability = mutability::InteriorMutable;
|
||||
type Mutability = mutability::MainThreadOnly;
|
||||
const NAME: &'static str = "WinitWindowDelegate";
|
||||
}
|
||||
|
||||
|
|
@ -51,8 +54,9 @@ declare_class!(
|
|||
type Ivars = State;
|
||||
}
|
||||
|
||||
// NSWindowDelegate + NSDraggingDestination protocols
|
||||
unsafe impl WinitWindowDelegate {
|
||||
unsafe impl NSObjectProtocol for WinitWindowDelegate {}
|
||||
|
||||
unsafe impl NSWindowDelegate for WinitWindowDelegate {
|
||||
#[method(windowShouldClose:)]
|
||||
fn window_should_close(&self, _: Option<&AnyObject>) -> bool {
|
||||
trace_scope!("windowShouldClose:");
|
||||
|
|
@ -135,64 +139,6 @@ declare_class!(
|
|||
self.queue_event(WindowEvent::Focused(false));
|
||||
}
|
||||
|
||||
/// Invoked when the dragged image enters destination bounds or frame
|
||||
#[method(draggingEntered:)]
|
||||
fn dragging_entered(&self, sender: &NSObject) -> bool {
|
||||
trace_scope!("draggingEntered:");
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
let pb: Id<NSPasteboard> = unsafe { msg_send_id![sender, draggingPasteboard] };
|
||||
let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }).unwrap();
|
||||
let filenames: Id<NSArray<NSString>> = unsafe { Id::cast(filenames) };
|
||||
|
||||
filenames.into_iter().for_each(|file| {
|
||||
let path = PathBuf::from(file.to_string());
|
||||
self.queue_event(WindowEvent::HoveredFile(path));
|
||||
});
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Invoked when the image is released
|
||||
#[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:)]
|
||||
fn perform_drag_operation(&self, sender: &NSObject) -> bool {
|
||||
trace_scope!("performDragOperation:");
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
let pb: Id<NSPasteboard> = unsafe { msg_send_id![sender, draggingPasteboard] };
|
||||
let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }).unwrap();
|
||||
let filenames: Id<NSArray<NSString>> = unsafe { Id::cast(filenames) };
|
||||
|
||||
filenames.into_iter().for_each(|file| {
|
||||
let path = PathBuf::from(file.to_string());
|
||||
self.queue_event(WindowEvent::DroppedFile(path));
|
||||
});
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Invoked when the dragging operation is complete
|
||||
#[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, _sender: Option<&NSObject>) {
|
||||
trace_scope!("draggingExited:");
|
||||
self.queue_event(WindowEvent::HoveredFileCancelled);
|
||||
}
|
||||
|
||||
/// Invoked when before enter fullscreen
|
||||
#[method(windowWillEnterFullScreen:)]
|
||||
fn window_will_enter_fullscreen(&self, _: Option<&AnyObject>) {
|
||||
|
|
@ -256,9 +202,9 @@ declare_class!(
|
|||
.window
|
||||
.lock_shared_state("window_will_use_fullscreen_presentation_options");
|
||||
if let Some(Fullscreen::Exclusive(_)) = shared_state.fullscreen {
|
||||
options = NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock
|
||||
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar;
|
||||
options = NSApplicationPresentationFullScreen
|
||||
| NSApplicationPresentationHideDock
|
||||
| NSApplicationPresentationHideMenuBar;
|
||||
}
|
||||
|
||||
options
|
||||
|
|
@ -343,42 +289,9 @@ declare_class!(
|
|||
#[method(windowDidChangeOcclusionState:)]
|
||||
fn window_did_change_occlusion_state(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidChangeOcclusionState:");
|
||||
self.queue_event(WindowEvent::Occluded(
|
||||
!self
|
||||
.ivars()
|
||||
.window
|
||||
.occlusionState()
|
||||
.contains(NSWindowOcclusionState::NSWindowOcclusionStateVisible),
|
||||
))
|
||||
}
|
||||
|
||||
// Observe theme change
|
||||
#[method(effectiveAppearanceDidChange:)]
|
||||
fn effective_appearance_did_change(&self, sender: Option<&AnyObject>) {
|
||||
trace_scope!("Triggered `effectiveAppearanceDidChange:`");
|
||||
unsafe {
|
||||
msg_send![
|
||||
self,
|
||||
performSelectorOnMainThread: sel!(effectiveAppearanceDidChangedOnMainThread:),
|
||||
withObject: sender,
|
||||
waitUntilDone: false,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[method(effectiveAppearanceDidChangedOnMainThread:)]
|
||||
fn effective_appearance_did_changed_on_main_thread(&self, _: Option<&AnyObject>) {
|
||||
let theme = get_ns_theme();
|
||||
let mut shared_state = self
|
||||
.ivars()
|
||||
.window
|
||||
.lock_shared_state("effective_appearance_did_change");
|
||||
let current_theme = shared_state.current_theme;
|
||||
shared_state.current_theme = Some(theme);
|
||||
drop(shared_state);
|
||||
if current_theme != Some(theme) {
|
||||
self.queue_event(WindowEvent::ThemeChanged(theme));
|
||||
}
|
||||
let visible = self.ivars().window.occlusionState() & NSWindowOcclusionStateVisible
|
||||
== NSWindowOcclusionStateVisible;
|
||||
self.queue_event(WindowEvent::Occluded(!visible));
|
||||
}
|
||||
|
||||
#[method(windowDidChangeScreen:)]
|
||||
|
|
@ -396,12 +309,109 @@ declare_class!(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl NSDraggingDestination for WinitWindowDelegate {
|
||||
/// Invoked when the dragged image enters destination bounds or frame
|
||||
#[method(draggingEntered:)]
|
||||
fn dragging_entered(&self, sender: &NSObject) -> bool {
|
||||
trace_scope!("draggingEntered:");
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
let pb: Id<NSPasteboard> = unsafe { msg_send_id![sender, draggingPasteboard] };
|
||||
let filenames = pb
|
||||
.propertyListForType(unsafe { NSFilenamesPboardType })
|
||||
.unwrap();
|
||||
let filenames: Id<NSArray<NSString>> = unsafe { Id::cast(filenames) };
|
||||
|
||||
filenames.into_iter().for_each(|file| {
|
||||
let path = PathBuf::from(file.to_string());
|
||||
self.queue_event(WindowEvent::HoveredFile(path));
|
||||
});
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Invoked when the image is released
|
||||
#[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:)]
|
||||
fn perform_drag_operation(&self, sender: &NSObject) -> bool {
|
||||
trace_scope!("performDragOperation:");
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
let pb: Id<NSPasteboard> = unsafe { msg_send_id![sender, draggingPasteboard] };
|
||||
let filenames = pb
|
||||
.propertyListForType(unsafe { NSFilenamesPboardType })
|
||||
.unwrap();
|
||||
let filenames: Id<NSArray<NSString>> = unsafe { Id::cast(filenames) };
|
||||
|
||||
filenames.into_iter().for_each(|file| {
|
||||
let path = PathBuf::from(file.to_string());
|
||||
self.queue_event(WindowEvent::DroppedFile(path));
|
||||
});
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Invoked when the dragging operation is complete
|
||||
#[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, _sender: Option<&NSObject>) {
|
||||
trace_scope!("draggingExited:");
|
||||
self.queue_event(WindowEvent::HoveredFileCancelled);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl WinitWindowDelegate {
|
||||
// Observe theme change
|
||||
#[method(effectiveAppearanceDidChange:)]
|
||||
fn effective_appearance_did_change(&self, sender: Option<&AnyObject>) {
|
||||
trace_scope!("Triggered `effectiveAppearanceDidChange:`");
|
||||
unsafe {
|
||||
msg_send![
|
||||
self,
|
||||
performSelectorOnMainThread: sel!(effectiveAppearanceDidChangedOnMainThread:),
|
||||
withObject: sender,
|
||||
waitUntilDone: false,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[method(effectiveAppearanceDidChangedOnMainThread:)]
|
||||
fn effective_appearance_did_changed_on_main_thread(&self, _: Option<&AnyObject>) {
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
let theme = get_ns_theme(mtm);
|
||||
let mut shared_state = self
|
||||
.ivars()
|
||||
.window
|
||||
.lock_shared_state("effective_appearance_did_change");
|
||||
let current_theme = shared_state.current_theme;
|
||||
shared_state.current_theme = Some(theme);
|
||||
drop(shared_state);
|
||||
if current_theme != Some(theme) {
|
||||
self.queue_event(WindowEvent::ThemeChanged(theme));
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
impl WinitWindowDelegate {
|
||||
pub fn new(window: &WinitWindow, initial_fullscreen: bool) -> Id<Self> {
|
||||
let mtm = MainThreadMarker::from(window);
|
||||
let scale_factor = window.scale_factor();
|
||||
let this = Self::alloc().set_ivars(State {
|
||||
let this = mtm.alloc().set_ivars(State {
|
||||
window: window.retain(),
|
||||
initial_fullscreen: Cell::new(initial_fullscreen),
|
||||
previous_position: Cell::new(None),
|
||||
|
|
@ -412,7 +422,7 @@ impl WinitWindowDelegate {
|
|||
if scale_factor != 1.0 {
|
||||
this.queue_static_scale_factor_changed_event();
|
||||
}
|
||||
this.ivars().window.setDelegate(Some(&this));
|
||||
window.setDelegate(Some(ProtocolObject::from_ref(&*this)));
|
||||
|
||||
// Enable theme change event
|
||||
let notification_center: Id<AnyObject> =
|
||||
|
|
@ -467,7 +477,7 @@ impl WinitWindowDelegate {
|
|||
}
|
||||
|
||||
fn view_size(&self) -> LogicalSize<f64> {
|
||||
let size = self.ivars().window.contentView().frame().size;
|
||||
let size = self.ivars().window.contentView().unwrap().frame().size;
|
||||
LogicalSize::new(size.width as f64, size.height as f64)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue