6#include <AppKit/AppKit.h>
17#if QT_CONFIG(sessionmanager)
18# include "qcocoasessionmanager.h"
24#include <qguiapplication.h>
25#include <qpa/qwindowsysteminterface.h>
26#include <qwindowdefs.h>
30@implementation QCocoaApplicationDelegate {
31 NSObject <NSApplicationDelegate> *reflectionDelegate;
35+ (instancetype)sharedDelegate
37 static QCocoaApplicationDelegate *shared = nil;
38 static dispatch_once_t onceToken;
39 dispatch_once(&onceToken, ^{
40 shared = [[self alloc] init];
61 if (reflectionDelegate) {
62 [[NSApplication sharedApplication] setDelegate:reflectionDelegate];
63 [reflectionDelegate release];
65 [[NSNotificationCenter defaultCenter] removeObserver:self];
70- (
NSMenu *)applicationDockMenu:(NSApplication *)sender
75 [self.dockMenu.delegate menuWillOpen:self.dockMenu];
76 return [[self.dockMenu retain] autorelease];
80- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
82 if ([reflectionDelegate respondsToSelector:_cmd])
83 return [reflectionDelegate applicationShouldTerminate:sender];
85 if (QGuiApplicationPrivate::instance()->threadData.loadRelaxed()->eventLoops.isEmpty()) {
89 qCDebug(lcQpaApplication) <<
"No running event loops, terminating now";
90 return NSTerminateNow;
93#if QT_CONFIG(sessionmanager)
94 QCocoaSessionManager *cocoaSessionManager = QCocoaSessionManager::instance();
95 cocoaSessionManager->resetCancellation();
96 cocoaSessionManager->appCommitData();
98 if (cocoaSessionManager->wasCanceled()) {
99 qCDebug(lcQpaApplication) <<
"Session management canceled application termination";
100 return NSTerminateCancel;
104 if (!QWindowSystemInterface::handleApplicationTermination<QWindowSystemInterface::SynchronousDelivery>()) {
105 qCDebug(lcQpaApplication) <<
"Application termination canceled";
106 return NSTerminateCancel;
113 qCDebug(lcQpaApplication) <<
"Termination accepted, but returning to runloop for exit through main()";
114 return NSTerminateCancel;
117- (
void)applicationWillFinishLaunching:(NSNotification *)notification
119 if ([reflectionDelegate respondsToSelector:_cmd])
120 [reflectionDelegate applicationWillFinishLaunching:notification];
123
124
125
126
127
128
131
132
133
134
135
136 NSAppleEventManager *eventManager = [NSAppleEventManager sharedAppleEventManager];
137 [eventManager setEventHandler:self
138 andSelector:@selector(getUrl:withReplyEvent:)
139 forEventClass:kInternetEventClass
140 andEventID:kAEGetURL];
144- (
void)removeAppleEventHandlers
146 NSAppleEventManager *eventManager = [NSAppleEventManager sharedAppleEventManager];
147 [eventManager removeEventHandlerForEventClass:kInternetEventClass andEventID:kAEGetURL];
155- (
void)applicationDidFinishLaunching:(NSNotification *)aNotification
157 if ([reflectionDelegate respondsToSelector:_cmd])
158 [reflectionDelegate applicationDidFinishLaunching:aNotification];
162 if (qEnvironmentVariableIsEmpty(
"QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM")) {
163 auto currentApplication = NSRunningApplication.currentApplication;
164 if (!currentApplication.active) {
172 auto frontmostApplication = NSWorkspace.sharedWorkspace.frontmostApplication;
173 qCDebug(lcQpaApplication) <<
"Launched with" << frontmostApplication
174 <<
"as frontmost application. Activating" << currentApplication <<
"instead.";
175 [NSApplication.sharedApplication activateIgnoringOtherApps:YES];
178 if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSSonoma) {
190 [currentApplication activateWithOptions:NSApplicationActivateAllWindows];
194 QCocoaMenuBar::insertWindowMenu();
197- (
void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
202 for (NSString *fileName in filenames) {
203 QString qtFileName = QString::fromNSString(fileName);
209 if (
qApp->arguments().contains(qtFileName))
212 QWindowSystemInterface::handleFileOpenEvent(qtFileName);
215 if ([reflectionDelegate respondsToSelector:_cmd])
216 [reflectionDelegate application:sender openFiles:filenames];
220- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
222 if ([reflectionDelegate respondsToSelector:_cmd])
223 return [reflectionDelegate applicationShouldTerminateAfterLastWindowClosed:sender];
228- (
void)applicationDidBecomeActive:(NSNotification *)notification
230 if (QCocoaWindow::s_applicationActivationObserver) {
231 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:QCocoaWindow::s_applicationActivationObserver];
232 QCocoaWindow::s_applicationActivationObserver = nil;
235 if ([reflectionDelegate respondsToSelector:_cmd])
236 [reflectionDelegate applicationDidBecomeActive:notification];
238 QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationActive);
240 if (QCocoaWindow::s_windowUnderMouse) {
243 QNSView *view = qnsview_cast(QCocoaWindow::s_windowUnderMouse->m_view);
244 [view convertFromScreen:[NSEvent mouseLocation] toWindowPoint:&windowPoint andScreenPoint:&screenPoint];
245 QWindow *windowUnderMouse = QCocoaWindow::s_windowUnderMouse->window();
246 qCInfo(lcQpaMouse) <<
"Application activated with mouse at" << windowPoint <<
"; sending" << QEvent::Enter <<
"to" << windowUnderMouse;
247 QWindowSystemInterface::handleEnterEvent(windowUnderMouse, windowPoint, screenPoint);
251- (
void)applicationDidResignActive:(NSNotification *)notification
253 if ([reflectionDelegate respondsToSelector:_cmd])
254 [reflectionDelegate applicationDidResignActive:notification];
256 QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationInactive);
258 if (QCocoaWindow::s_windowUnderMouse) {
259 QWindow *windowUnderMouse = QCocoaWindow::s_windowUnderMouse->window();
260 qCInfo(lcQpaMouse) <<
"Application deactivated; sending" << QEvent::Leave <<
"to" << windowUnderMouse;
261 QWindowSystemInterface::handleLeaveEvent(windowUnderMouse);
265- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag
267 if ([reflectionDelegate respondsToSelector:_cmd])
268 return [reflectionDelegate applicationShouldHandleReopen:theApplication hasVisibleWindows:flag];
271
272
273
274
275
276
277 QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationActive,
true );
282- (
void)setReflectionDelegate:(NSObject <NSApplicationDelegate> *)oldDelegate
284 [oldDelegate retain];
285 [reflectionDelegate release];
286 reflectionDelegate = oldDelegate;
289- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
291 NSMethodSignature *result = [super methodSignatureForSelector:aSelector];
292 if (!result && reflectionDelegate) {
293 result = [reflectionDelegate methodSignatureForSelector:aSelector];
298- (BOOL)respondsToSelector:(SEL)aSelector
300 return [super respondsToSelector:aSelector] || [reflectionDelegate respondsToSelector:aSelector];
303- (
void)forwardInvocation:(NSInvocation *)invocation
305 SEL invocationSelector = [invocation selector];
306 if ([reflectionDelegate respondsToSelector:invocationSelector])
307 [invocation invokeWithTarget:reflectionDelegate];
309 [self doesNotRecognizeSelector:invocationSelector];
312- (BOOL)application:(NSApplication *)application continueUserActivity:(NSUserActivity *)userActivity
313 restorationHandler:(
void(^)(NSArray<id<NSUserActivityRestoring>> *restorableObjects))restorationHandler
316 if ([reflectionDelegate respondsToSelector:_cmd]
317 && [reflectionDelegate application:application continueUserActivity:userActivity
318 restorationHandler:restorationHandler] == YES) {
322 if (!QGuiApplication::instance())
325 if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
326 QCocoaIntegration *cocoaIntegration = QCocoaIntegration::instance();
327 Q_ASSERT(cocoaIntegration);
328 return cocoaIntegration->services()->handleUrl(QUrl::fromNSURL(userActivity.webpageURL));
334- (
void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
336 Q_UNUSED(replyEvent);
338 NSString *urlString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
339 const QString qurlString = QString::fromNSString(urlString);
341 if (event.eventClass == kInternetEventClass && event.eventID == kAEGetURL) {
343 if (!QGuiApplication::instance())
345 QCocoaIntegration *cocoaIntegration = QCocoaIntegration::instance();
346 Q_ASSERT(cocoaIntegration);
347 if (cocoaIntegration->services()->handleUrl(QUrl(qurlString)))
356 if (
const QUrl url(qurlString); url.isValid())
357 QWindowSystemInterface::handleFileOpenEvent(url);
359 QWindowSystemInterface::handleFileOpenEvent(qurlString);
362- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)application
364 if (@available(macOS 12, *)) {
365 if ([reflectionDelegate respondsToSelector:_cmd])
366 return [reflectionDelegate applicationSupportsSecureRestorableState:application];
380@implementation QCocoaApplicationDelegate (Menus)
382- (BOOL)validateMenuItem:(NSMenuItem*)item
384 qCDebug(lcQpaMenus) <<
"Validating" << item <<
"for" << self;
386 auto *nativeItem = qt_objc_cast<QCocoaNSMenuItem *>(item);
390 auto *platformItem = nativeItem.platformMenuItem;
392 return item.hasSubmenu || (item.enabled && (item.action != @selector(qt_itemFired:)));
395 if (platformItem->menu())
398 return platformItem->isEnabled();
403@implementation QCocoaApplicationDelegate (MenuAPI)
405- (
void)qt_itemFired:(QCocoaNSMenuItem *)item
407 qCDebug(lcQpaMenus) <<
"Activating" << item;
412 auto *nativeItem = qt_objc_cast<QCocoaNSMenuItem *>(item);
413 Q_ASSERT_X(nativeItem, qPrintable(
__FUNCTION__),
"Triggered menu item is not a QCocoaNSMenuItem.");
414 auto *platformItem = nativeItem.platformMenuItem;
417 if (!platformItem || platformItem->menu())
420 QGuiApplicationPrivate::modifier_buttons = QAppleKeyMapper::fromCocoaModifiers([NSEvent modifierFlags]);
422 static QMetaMethod activatedSignal = QMetaMethod::fromSignal(&QCocoaMenuItem::activated);
423 activatedSignal.invoke(platformItem, Qt::QueuedConnection);