Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qcocoawindow.mm
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <AppKit/AppKit.h>
5#include <QuartzCore/QuartzCore.h>
6
7#include "qcocoawindow.h"
8#include "qcocoaintegration.h"
9#include "qcocoascreen.h"
10#include "qnswindowdelegate.h"
12#ifndef QT_NO_OPENGL
13#include "qcocoaglcontext.h"
14#endif
15#include "qcocoahelpers.h"
17#include "qnsview.h"
18#include "qnswindow.h"
19#include <QtCore/qfileinfo.h>
20#include <QtCore/private/qcore_mac_p.h>
21#include <qwindow.h>
22#include <private/qwindow_p.h>
23#include <qpa/qwindowsysteminterface.h>
24#include <qpa/qplatformscreen.h>
25#include <QtGui/private/qcoregraphics_p.h>
26#include <QtGui/private/qhighdpiscaling_p.h>
27
28#include <QDebug>
29
30#include <vector>
31
33
34enum {
37};
38
39Q_LOGGING_CATEGORY(lcCocoaNotifications, "qt.qpa.cocoa.notifications");
40
42{
43 static const QLatin1StringView notificationHandlerPrefix(Q_NOTIFICATION_PREFIX);
44
45 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
46
47 const QMetaObject *metaObject = QMetaType(qRegisterMetaType<QCocoaWindow*>()).metaObject();
49
50 for (int i = 0; i < metaObject->methodCount(); ++i) {
51 QMetaMethod method = metaObject->method(i);
52 const QString methodTag = QString::fromLatin1(method.tag());
53 if (!methodTag.startsWith(notificationHandlerPrefix))
54 continue;
55
56 const QString notificationName = methodTag.mid(notificationHandlerPrefix.size());
57 [center addObserverForName:notificationName.toNSString() object:nil queue:nil
58 usingBlock:^(NSNotification *notification) {
59
60 QVarLengthArray<QCocoaWindow *, 32> cocoaWindows;
61 if ([notification.object isKindOfClass:[NSWindow class]]) {
62 NSWindow *nsWindow = notification.object;
64 if (QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window->handle()))
65 if (cocoaWindow->nativeWindow() == nsWindow)
66 cocoaWindows += cocoaWindow;
67 }
68 } else if ([notification.object isKindOfClass:[NSView class]]) {
69 if (QNSView *qnsView = qnsview_cast(notification.object))
70 cocoaWindows += qnsView.platformWindow;
71 } else {
72 qCWarning(lcCocoaNotifications) << "Unhandled notification"
73 << notification.name << "for" << notification.object;
74 return;
75 }
76
77 if (lcCocoaNotifications().isDebugEnabled() && !cocoaWindows.isEmpty()) {
78 QVector<QCocoaWindow *> debugWindows;
79 for (QCocoaWindow *cocoaWindow : cocoaWindows)
80 debugWindows += cocoaWindow;
81 qCDebug(lcCocoaNotifications) << "Forwarding" << qPrintable(notificationName) <<
82 "to" << debugWindows;
83 }
84
85 // FIXME: Could be a foreign window, look up by iterating top level QWindows
86
87 for (QCocoaWindow *cocoaWindow : cocoaWindows) {
88 if (!method.invoke(cocoaWindow, Qt::DirectConnection)) {
89 qCWarning(lcQpaWindow) << "Failed to invoke NSNotification callback for"
90 << notification.name << "on" << cocoaWindow;
91 }
92 }
93 }];
94 }
95}
97
98const int QCocoaWindow::NoAlertRequest = -1;
99QPointer<QCocoaWindow> QCocoaWindow::s_windowUnderMouse;
100
102{
103 qCDebug(lcQpaWindow) << "QCocoaWindow::QCocoaWindow" << window();
104
105 if (nativeHandle) {
106 m_view = reinterpret_cast<NSView *>(nativeHandle);
107 [m_view retain];
108 }
109}
110
112{
113 qCDebug(lcQpaWindow) << "QCocoaWindow::initialize" << window();
114
116
117 if (!m_view)
118 m_view = [[QNSView alloc] initWithCocoaWindow:this];
119
120 if (!isForeignWindow()) {
121 // Compute the initial geometry based on the geometry set on the
122 // QWindow. This geometry has already been reflected to the
123 // QPlatformWindow in the constructor, so to ensure that the
124 // resulting setGeometry call does not think the geometry has
125 // already been applied, we reset the QPlatformWindow's view
126 // of the geometry first.
131
133
134 } else {
135 // Pick up essential foreign window state
136 QPlatformWindow::setGeometry(QRectF::fromCGRect(m_view.frame).toRect());
137 }
138
140
141 m_initialized = true;
142}
143
144const NSNotificationName QCocoaWindowWillReleaseQNSViewNotification = @"QCocoaWindowWillReleaseQNSViewNotification";
145
147{
148 qCDebug(lcQpaWindow) << "QCocoaWindow::~QCocoaWindow" << window();
149
151 [m_nsWindow makeFirstResponder:nil];
152 [m_nsWindow setContentView:nil];
153
154 // Remove from superview only if we have a Qt window parent,
155 // as we don't want to affect window container foreign windows.
157 [m_view removeFromSuperview];
158
159 // Make sure to disconnect observer in all case if view is valid
160 // to avoid notifications received when deleting when using Qt::AA_NativeWindows attribute
161 if (!isForeignWindow())
162 [[NSNotificationCenter defaultCenter] removeObserver:m_view];
163
164#if QT_CONFIG(vulkan)
165 if (QCocoaIntegration *cocoaIntegration = QCocoaIntegration::instance()) {
166 auto vulcanInstance = cocoaIntegration->getCocoaVulkanInstance();
167 if (vulcanInstance)
168 vulcanInstance->destroySurface(m_vulkanSurface);
169 }
170#endif
171
172 // Must send notification before calling release, as doing it from
173 // [QNSView dealloc] would mean that any weak references to the view
174 // would already return nil.
175 [NSNotificationCenter.defaultCenter
177 object:m_view];
178
179 [m_view release];
180 [m_nsWindow closeAndRelease];
181}
182
184{
185 return window()->requestedFormat();
186}
187
189{
190 qCDebug(lcQpaWindow) << "QCocoaWindow::setGeometry" << window() << rectIn;
191
192 QBoolBlocker inSetGeometry(m_inSetGeometry, true);
193
194 QRect rect = rectIn;
195 // This means it is a call from QWindow::setFramePosition() and
196 // the coordinates include the frame (size is still the contents rectangle).
197 if (qt_window_private(const_cast<QWindow *>(window()))->positionPolicy
199 const QMargins margins = frameMargins();
200 rect.moveTopLeft(rect.topLeft() + QPoint(margins.left(), margins.top()));
201 }
202 if (geometry() == rect)
203 return;
204
206}
207
209{
210 return ![m_view isKindOfClass:[QNSView class]];
211}
212
214{
215 // QWindows that are embedded in a NSView hierarchy may be considered
216 // top-level from Qt's point of view but are not from Cocoa's point
217 // of view. Embedded QWindows get global (screen) geometry.
218 if (isEmbedded()) {
219 NSPoint windowPoint = [m_view convertPoint:NSMakePoint(0, 0) toView:nil];
220 NSRect screenRect = [[m_view window] convertRectToScreen:NSMakeRect(windowPoint.x, windowPoint.y, 1, 1)];
221 NSPoint screenPoint = screenRect.origin;
222 QPoint position = QCocoaScreen::mapFromNative(screenPoint).toPoint();
223 QSize size = QRectF::fromCGRect(NSRectToCGRect([m_view bounds])).toRect().size();
224 return QRect(position, size);
225 }
226
228}
229
239{
240 if (!isContentView())
241 return QRect();
242
243 // We only persist the normal the geometry when going into
244 // fullscreen and maximized states. For all other cases we
245 // can just report the geometry as is.
246
248 return geometry();
249
250 return m_normalGeometry;
251}
252
254{
255 if (!isContentView())
256 return;
257
259 return;
260
262}
263
265{
266 qCDebug(lcQpaWindow) << "QCocoaWindow::setCocoaGeometry" << window() << rect;
268
270
271 if (isEmbedded()) {
272 if (!isForeignWindow()) {
273 [m_view setFrame:NSMakeRect(0, 0, rect.width(), rect.height())];
274 }
275 return;
276 }
277
278 if (isContentView()) {
279 NSRect bounds = QCocoaScreen::mapToNative(rect);
280 [m_view.window setFrame:[m_view.window frameRectForContentRect:bounds] display:YES animate:NO];
281 } else {
282 [m_view setFrame:NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height())];
283 }
284
285 // will call QPlatformWindow::setGeometry(rect) during resize confirmation (see qnsview.mm)
286}
287
289{
290 switch (NSApp.currentEvent.type) {
291 case NSEventTypeLeftMouseDown:
292 case NSEventTypeRightMouseDown:
293 case NSEventTypeOtherMouseDown:
294 case NSEventTypeMouseMoved:
295 case NSEventTypeLeftMouseDragged:
296 case NSEventTypeRightMouseDragged:
297 case NSEventTypeOtherMouseDragged:
298 // The documentation only describes starting a system move
299 // based on mouse down events, but move events also work.
300 [m_view.window performWindowDragWithEvent:NSApp.currentEvent];
301 return true;
302 default:
303 return false;
304 }
305}
306
307void QCocoaWindow::setVisible(bool visible)
308{
309 qCDebug(lcQpaWindow) << "QCocoaWindow::setVisible" << window() << visible;
310
311 // Our implementation of setVisible below is not idempotent, as for
312 // modal windows it calls beginSheet/endSheet or starts/ends modal
313 // sessions. However we can't simply guard for m_view.hidden already
314 // having the right state, as the behavior of this function differs
315 // based on whether the window has been initialized or not, as
316 // handleGeometryChange will bail out if the window is still
317 // initializing. Since we know we'll get a second setVisible
318 // call after creation, we can check for that case specifically,
319 // which means we can then safely guard on m_view.hidden changing.
320
321 if (!m_initialized) {
322 qCDebug(lcQpaWindow) << "Window still initializing, skipping setting visibility";
323 return; // We'll get another setVisible call after create is done
324 }
325
326 if (visible == !m_view.hidden && (!isContentView() || visible == m_view.window.visible)) {
327 qCDebug(lcQpaWindow) << "No change in visible status. Ignoring.";
328 return;
329 }
330
331 if (m_inSetVisible) {
332 qCWarning(lcQpaWindow) << "Already setting window visible!";
333 return;
334 }
335
336 QScopedValueRollback<bool> rollback(m_inSetVisible, true);
337
339 QCocoaWindow *parentCocoaWindow = nullptr;
340 if (window()->transientParent())
341 parentCocoaWindow = static_cast<QCocoaWindow *>(window()->transientParent()->handle());
342
343 auto eventDispatcher = [] {
344 return static_cast<QCocoaEventDispatcherPrivate *>(QObjectPrivate::get(qApp->eventDispatcher()));
345 };
346
347 if (visible) {
348 // We need to recreate if the modality has changed as the style mask will need updating
350
351 // We didn't send geometry changes during creation, as that would have confused
352 // Qt, which expects a show-event to be sent before any resize events. But now
353 // that the window is made visible, we know that the show-event has been sent
354 // so we can send the geometry change. FIXME: Get rid of this workaround.
356
357 if (parentCocoaWindow) {
358 // The parent window might have moved while this window was hidden,
359 // update the window geometry if there is a parent.
361
362 if (window()->type() == Qt::Popup) {
363 // QTBUG-30266: a window should not be resizable while a transient popup is open
364 // Since this isn't a native popup, the window manager doesn't close the popup when you click outside
365 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
366 NSUInteger parentStyleMask = nativeParentWindow.styleMask;
367 if ((m_resizableTransientParent = (parentStyleMask & NSWindowStyleMaskResizable))
368 && !(nativeParentWindow.styleMask & NSWindowStyleMaskFullScreen))
369 nativeParentWindow.styleMask &= ~NSWindowStyleMaskResizable;
370 }
371
372 }
373
374 // Make the NSView visible first, before showing the NSWindow (in case of top level windows)
375 m_view.hidden = NO;
376
377 if (isContentView()) {
379
380 // setWindowState might have been called while the window was hidden and
381 // will not change the NSWindow state in that case. Sync up here:
382 applyWindowState(window()->windowStates());
383
385 if (parentCocoaWindow && (window()->modality() == Qt::WindowModal || window()->type() == Qt::Sheet)) {
386 // Show the window as a sheet
387 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
388 if (!nativeParentWindow.attachedSheet)
389 [nativeParentWindow beginSheet:m_view.window completionHandler:nil];
390 else
391 [nativeParentWindow beginCriticalSheet:m_view.window completionHandler:nil];
392 } else if (window()->modality() == Qt::ApplicationModal) {
393 // Show the window as application modal
394 eventDispatcher()->beginModalSession(window());
395 } else if (m_view.window.canBecomeKeyWindow) {
396 bool shouldBecomeKeyNow = !NSApp.modalWindow
397 || m_view.window.worksWhenModal
398 || !NSApp.modalWindow.visible;
399
400 // Panels with becomesKeyOnlyIfNeeded set should not activate until a view
401 // with needsPanelToBecomeKey, for example a line edit, is clicked.
402 if ([m_view.window isKindOfClass:[NSPanel class]])
403 shouldBecomeKeyNow &= !(static_cast<NSPanel*>(m_view.window).becomesKeyOnlyIfNeeded);
404
405 if (shouldBecomeKeyNow)
406 [m_view.window makeKeyAndOrderFront:nil];
407 else
408 [m_view.window orderFront:nil];
409 } else {
410 [m_view.window orderFront:nil];
411 }
412 }
413 }
414 } else {
415 // Window not visible, hide it
416 if (isContentView()) {
417 if (eventDispatcher()->hasModalSession()) {
418 eventDispatcher()->endModalSession(window());
419 } else {
420 if ([m_view.window isSheet]) {
421 Q_ASSERT_X(parentCocoaWindow, "QCocoaWindow", "Window modal dialog has no transient parent.");
422 [parentCocoaWindow->nativeWindow() endSheet:m_view.window];
423 }
424 }
425
426 // Note: We do not guard the order out by checking NSWindow.visible, as AppKit will
427 // in some cases, such as when hiding the application, order out and make a window
428 // invisible, but keep it in a list of "hidden windows", that it then restores again
429 // when the application is unhidden. We need to call orderOut explicitly, to bring
430 // the window out of this "hidden list".
431 [m_view.window orderOut:nil];
432
433 if (m_view.window == [NSApp keyWindow] && !eventDispatcher()->hasModalSession()) {
434 // Probably because we call runModalSession: outside [NSApp run] in QCocoaEventDispatcher
435 // (e.g., when show()-ing a modal QDialog instead of exec()-ing it), it can happen that
436 // the current NSWindow is still key after being ordered out. Then, after checking we
437 // don't have any other modal session left, it's safe to make the main window key again.
438 NSWindow *mainWindow = [NSApp mainWindow];
439 if (mainWindow && [mainWindow canBecomeKeyWindow])
440 [mainWindow makeKeyWindow];
441 }
442 }
443
444 // AppKit will in some cases set up the key view loop for child views, even if we
445 // don't set autorecalculatesKeyViewLoop, nor call recalculateKeyViewLoop ourselves.
446 // When a child window is promoted to a top level, AppKit will maintain the key view
447 // loop between the views, even if these views now cross NSWindows, even after we
448 // explicitly call recalculateKeyViewLoop. When the top level is then hidden, AppKit
449 // will complain when -[NSView _setHidden:setNeedsDisplay:] tries to transfer first
450 // responder by reading the nextValidKeyView, and it turns out to live in a different
451 // window. We mitigate this by a last second reset of the first responder, which is
452 // what AppKit also falls back to. It's unclear if the original situation of views
453 // having their nextKeyView pointing to views in other windows is kosher or not.
454 if (m_view.window.firstResponder == m_view && m_view.nextValidKeyView
455 && m_view.nextValidKeyView.window != m_view.window) {
456 qCDebug(lcQpaWindow) << "Detected nextValidKeyView" << m_view.nextValidKeyView
457 << "in different window" << m_view.nextValidKeyView.window
458 << "Resetting" << m_view.window << "first responder to nil.";
459 [m_view.window makeFirstResponder:nil];
460 }
461
462 m_view.hidden = YES;
463
464 if (parentCocoaWindow && window()->type() == Qt::Popup) {
465 NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
467 && !(nativeParentWindow.styleMask & NSWindowStyleMaskFullScreen))
468 // A window should not be resizable while a transient popup is open
469 nativeParentWindow.styleMask |= NSWindowStyleMaskResizable;
470 }
471 }
472}
473
475{
477
478 NSInteger windowLevel = NSNormalWindowLevel;
479
480 if (type == Qt::Tool)
481 windowLevel = NSFloatingWindowLevel;
482 else if ((type & Qt::Popup) == Qt::Popup)
483 windowLevel = NSPopUpMenuWindowLevel;
484
485 // StayOnTop window should appear above Tool windows.
487 windowLevel = NSModalPanelWindowLevel;
488 // Tooltips should appear above StayOnTop windows.
489 if (type == Qt::ToolTip)
490 windowLevel = NSScreenSaverWindowLevel;
491
492 auto *transientParent = window()->transientParent();
493 if (transientParent && transientParent->handle()) {
494 // We try to keep windows in at least the same window level as
495 // their transient parent. Unfortunately this only works when the
496 // window is created. If the window level changes after that, as
497 // a result of a call to setWindowFlags, or by changing the level
498 // of the native window, we will not pick this up, and the window
499 // will be left behind (or in a different window level than) its
500 // parent. We could KVO-observe the window level of our transient
501 // parent, but that requires us to know when the parent goes away
502 // so that we can unregister the observation before the parent is
503 // dealloced, something we can't do for generic NSWindows. Another
504 // way would be to override [NSWindow setLevel:] and notify child
505 // windows about the change, but that doesn't work for foreign
506 // windows, which can still be transient parents via fromWinId().
507 // One area where this problem is apparent is when AppKit tweaks
508 // the window level of modal windows during application activation
509 // and deactivation. Since we don't pick up on these window level
510 // changes in a generic way, we need to add logic explicitly to
511 // re-evaluate the window level after AppKit has done its tweaks.
512
513 auto *transientCocoaWindow = static_cast<QCocoaWindow *>(transientParent->handle());
514 auto *nsWindow = transientCocoaWindow->nativeWindow();
515
516 // We only upgrade the window level for "special" windows, to work
517 // around Qt Widgets Designer parenting the designer windows to the widget
518 // palette window (QTBUG-31779). This should be fixed in designer.
519 if (type != Qt::Window)
520 windowLevel = qMax(windowLevel, nsWindow.level);
521 }
522
523 return windowLevel;
524}
525
527{
528 const Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask));
529
530 // Determine initial style mask based on whether the window should
531 // have a frame and title or not. The NSWindowStyleMaskBorderless
532 // and NSWindowStyleMaskTitled styles are mutually exclusive, with
533 // values of 0 and 1 correspondingly.
534 NSUInteger styleMask = [&]{
535 // Honor explicit requests for borderless windows
537 return NSWindowStyleMaskBorderless;
538
539 // Popup windows should always be borderless
541 return NSWindowStyleMaskBorderless;
542
544 // CustomizeWindowHint turns off the default window title hints,
545 // so the choice is then up to the user via Qt::WindowTitleHint.
547 ? NSWindowStyleMaskTitled
548 : NSWindowStyleMaskBorderless;
549 } else {
550 // Otherwise, default to using titled windows
551 return NSWindowStyleMaskTitled;
552 }
553 }();
554
555 // We determine which buttons to show in updateTitleBarButtons,
556 // so we can enable all the relevant style masks here to ensure
557 // that behaviors that don't involve the title bar buttons are
558 // working (for example minimizing frameless windows, or resizing
559 // windows that don't have zoom or fullscreen titlebar buttons).
560 styleMask |= NSWindowStyleMaskClosable
561 | NSWindowStyleMaskMiniaturizable;
562
563 if (type != Qt::Popup) // We only care about popups exactly.
564 styleMask |= NSWindowStyleMaskResizable;
565
566 if (type == Qt::Tool)
567 styleMask |= NSWindowStyleMaskUtilityWindow;
568
569 // FIXME: Remove use of deprecated style mask
571 styleMask |= NSWindowStyleMaskTexturedBackground;
572
573 // Don't wipe existing states
574 if (m_view.window.styleMask & NSWindowStyleMaskFullScreen)
575 styleMask |= NSWindowStyleMaskFullScreen;
576 if (m_view.window.styleMask & NSWindowStyleMaskFullSizeContentView)
577 styleMask |= NSWindowStyleMaskFullSizeContentView;
578
579 return styleMask;
580}
581
587
588void QCocoaWindow::updateTitleBarButtons(Qt::WindowFlags windowFlags)
589{
590 if (!isContentView())
591 return;
592
593 static constexpr std::pair<NSWindowButton, Qt::WindowFlags> buttons[] = {
594 { NSWindowCloseButton, Qt::WindowCloseButtonHint },
595 { NSWindowMiniaturizeButton, Qt::WindowMinimizeButtonHint},
597 };
598
599 bool hideButtons = true;
600 for (const auto &[button, buttonHint] : buttons) {
601 // Set up Qt defaults based on window type
602 bool enabled = true;
603 if (button == NSWindowMiniaturizeButton)
604 enabled = window()->type() != Qt::Dialog;
605
606 // Let users override via CustomizeWindowHint
607 if (windowFlags & Qt::CustomizeWindowHint)
608 enabled = windowFlags & buttonHint;
609
610 // Then do some final sanitizations
611
612 if (button == NSWindowZoomButton && isFixedSize())
613 enabled = false;
614
615 // Mimic what macOS natively does for parent windows of modal
616 // sheets, which is to disable the close button, but leave the
617 // other buttons as they were.
618 if (button == NSWindowCloseButton && enabled
619 && QWindowPrivate::get(window())->blockedByModalWindow) {
620 enabled = false;
621 // If we end up having no enabled buttons, our workaround
622 // should not be a reason for hiding all of them.
623 hideButtons = false;
624 }
625
626 [m_view.window standardWindowButton:button].enabled = enabled;
627 hideButtons &= !enabled;
628 }
629
630 // Hide buttons in case we disabled all of them
631 for (const auto &[button, buttonHint] : buttons)
632 [m_view.window standardWindowButton:button].hidden = hideButtons;
633}
634
636{
637 // Updating the window flags may affect the window's theme frame, which
638 // in the process retains and then autoreleases the NSWindow. To make
639 // sure this doesn't leave lingering releases when there is no pool in
640 // place (e.g. during main(), before exec), we add one locally here.
642
643 if (!isContentView())
644 return;
645
646 // While setting style mask we can have handleGeometryChange calls on a content
647 // view with null geometry, reporting an invalid coordinates as a result.
648 m_inSetStyleMask = true;
649 m_view.window.styleMask = windowStyleMask(flags);
650 m_inSetStyleMask = false;
651
653 if ((type & Qt::Popup) != Qt::Popup && (type & Qt::Dialog) != Qt::Dialog) {
654 NSWindowCollectionBehavior behavior = m_view.window.collectionBehavior;
655 const bool enableFullScreen = m_view.window.qt_fullScreen
658 if (enableFullScreen) {
659 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
660 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
661 } else {
662 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
663 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
664 }
665 m_view.window.collectionBehavior = behavior;
666 }
667
668 // Set styleMask and collectionBehavior before applying window level, as
669 // the window level change will trigger verification of the two properties.
670 m_view.window.level = this->windowLevel(flags);
671
672 m_view.window.hasShadow = !(flags & Qt::NoDropShadowWindowHint);
673
676
678
679 // Make window ignore mouse events if WindowTransparentForInput is set.
680 // Note that ignoresMouseEvents has a special initial state where events
681 // are ignored (passed through) based on window transparency, and that
682 // setting the property to false does not return us to that state. Instead,
683 // this makes the window capture all mouse events. Take care to only
684 // set the property if needed. FIXME: recreate window if needed or find
685 // some other way to implement WindowTransparentForInput.
686 bool ignoreMouse = flags & Qt::WindowTransparentForInput;
687 if (m_view.window.ignoresMouseEvents != ignoreMouse)
688 m_view.window.ignoresMouseEvents = ignoreMouse;
689}
690
691// ----------------------- Window state -----------------------
692
701{
702 if (window()->isVisible())
703 applyWindowState(state); // Window state set for hidden windows take effect when show() is called
704}
705
706void QCocoaWindow::applyWindowState(Qt::WindowStates requestedState)
707{
708 if (!isContentView())
709 return;
710
713
714 if (newState == currentState)
715 return;
716
717 qCDebug(lcQpaWindow) << "Applying" << newState << "to" << window() << "in" << currentState;
718
719 const NSSize contentSize = m_view.frame.size;
720 if (contentSize.width <= 0 || contentSize.height <= 0) {
721 // If content view width or height is 0 then the window animations will crash so
722 // do nothing. We report the current state back to reflect the failed operation.
723 qWarning("invalid window content view size, check your window geometry");
725 return;
726 }
727
728 const NSWindow *nsWindow = m_view.window;
729
730 if (nsWindow.styleMask & NSWindowStyleMaskUtilityWindow
732 qWarning() << window()->type() << "windows cannot be made" << newState;
734 return;
735 }
736
737 const id sender = nsWindow;
738
739 // First we need to exit states that can't transition directly to other states
740 switch (currentState) {
742 [nsWindow deminiaturize:sender];
743 // Deminiaturizing is not synchronous, so we need to wait for the
744 // NSWindowDidMiniaturizeNotification before continuing to apply
745 // the new state.
746 return;
749 // Exiting fullscreen is not synchronous, so we need to wait for the
750 // NSWindowDidExitFullScreenNotification before continuing to apply
751 // the new state.
752 return;
753 }
754 default:;
755 }
756
757 // Then we apply the new state if needed
758 if (newState == windowState())
759 return;
760
761 switch (newState) {
764 break;
767 break;
769 [nsWindow miniaturize:sender];
770 break;
774 break;
775 default:
776 Q_UNREACHABLE();
777 }
778}
779
780Qt::WindowStates QCocoaWindow::windowState() const
781{
782 Qt::WindowStates states = Qt::WindowNoState;
783 NSWindow *window = m_view.window;
784
785 if (window.miniaturized)
787
788 // Full screen and maximized are mutually exclusive, as macOS
789 // will report a full screen window as zoomed.
790 if (window.qt_fullScreen) {
792 } else if ((window.zoomed && !isTransitioningToFullScreen())
795 }
796
797 // Note: We do not report Qt::WindowActive, even if isActive()
798 // is true, as QtGui does not expect this window state to be set.
799
800 return states;
801}
802
804{
805 const NSWindow *window = m_view.window;
806
807 // The NSWindow needs to be resizable, otherwise the window will
808 // not be possible to zoom back to non-zoomed state.
809 const bool wasResizable = window.styleMask & NSWindowStyleMaskResizable;
810 window.styleMask |= NSWindowStyleMaskResizable;
811
812 const id sender = window;
813 [window zoom:sender];
814
815 if (!wasResizable)
816 window.styleMask &= ~NSWindowStyleMaskResizable;
817}
818
823
825{
826 const NSWindow *window = m_view.window;
827
828 // The window needs to have the correct collection behavior for the
829 // toggleFullScreen call to have an effect. The collection behavior
830 // will be reset in windowDidEnterFullScreen/windowDidLeaveFullScreen.
831 window.collectionBehavior |= NSWindowCollectionBehaviorFullScreenPrimary;
832
833 const id sender = window;
835}
836
837void QCocoaWindow::windowWillEnterFullScreen()
838{
839 if (!isContentView())
840 return;
841
843
844 // The NSWindow needs to be resizable, otherwise we'll end up with
845 // the normal window geometry, centered in the middle of the screen
846 // on a black background. The styleMask will be reset below.
847 m_view.window.styleMask |= NSWindowStyleMaskResizable;
848}
849
851{
852 NSWindow *window = m_view.window;
853 return window.styleMask & NSWindowStyleMaskFullScreen && !window.qt_fullScreen;
854}
855
856void QCocoaWindow::windowDidEnterFullScreen()
857{
858 if (!isContentView())
859 return;
860
861 Q_ASSERT_X(m_view.window.qt_fullScreen, "QCocoaWindow",
862 "FullScreen category processes window notifications first");
863
864 // Reset to original styleMask
866
868}
869
870void QCocoaWindow::windowWillExitFullScreen()
871{
872 if (!isContentView())
873 return;
874
875 // The NSWindow needs to be resizable, otherwise we'll end up with
876 // a weird zoom animation. The styleMask will be reset below.
877 m_view.window.styleMask |= NSWindowStyleMaskResizable;
878}
879
880void QCocoaWindow::windowDidExitFullScreen()
881{
882 if (!isContentView())
883 return;
884
885 Q_ASSERT_X(!m_view.window.qt_fullScreen, "QCocoaWindow",
886 "FullScreen category processes window notifications first");
887
888 // Reset to original styleMask
890
891 Qt::WindowState requestedState = window()->windowState();
892
893 // Deliver update of QWindow state
895
896 if (requestedState != windowState() && requestedState != Qt::WindowFullScreen) {
897 // We were only going out of full screen as an intermediate step before
898 // progressing into the final step, so re-sync the desired state.
899 applyWindowState(requestedState);
900 }
901}
902
903void QCocoaWindow::windowDidMiniaturize()
904{
905 if (!isContentView())
906 return;
907
909}
910
911void QCocoaWindow::windowDidDeminiaturize()
912{
913 if (!isContentView())
914 return;
915
916 Qt::WindowState requestedState = window()->windowState();
917
919
920 if (requestedState != windowState() && requestedState != Qt::WindowMinimized) {
921 // We were only going out of minimized as an intermediate step before
922 // progressing into the final step, so re-sync the desired state.
923 applyWindowState(requestedState);
924 }
925}
926
928{
929 Qt::WindowStates currentState = windowState();
930 if (!(flags & HandleUnconditionally) && currentState == m_lastReportedWindowState)
931 return;
932
933 qCDebug(lcQpaWindow) << "QCocoaWindow::handleWindowStateChanged" <<
934 m_lastReportedWindowState << "-->" << currentState;
935
936 QWindowSystemInterface::handleWindowStateChanged<QWindowSystemInterface::SynchronousDelivery>(
937 window(), currentState, m_lastReportedWindowState);
938 m_lastReportedWindowState = currentState;
939}
940
941// ------------------------------------------------------------
942
944{
945 if (!isContentView())
946 return;
947
949 m_view.window.title = title.toNSString();
950
951 if (title.isEmpty() && !window()->filePath().isEmpty()) {
952 // Clearing the title should restore the default filename
953 setWindowFilePath(window()->filePath());
954 }
955}
956
958{
959 if (!isContentView())
960 return;
961
963
964 if (window()->title().isNull())
965 [m_view.window setTitleWithRepresentedFilename:filePath.toNSString()];
966 else
967 m_view.window.representedFilename = filePath.toNSString();
968
969 // Changing the file path may affect icon visibility
971}
972
974{
975 if (!isContentView())
976 return;
977
978 NSButton *iconButton = [m_view.window standardWindowButton:NSWindowDocumentIconButton];
979 if (!iconButton) {
980 // Window icons are only supported on macOS in combination with a document filePath
981 return;
982 }
983
985
986 if (icon.isNull()) {
987 iconButton.image = [NSWorkspace.sharedWorkspace iconForFile:m_view.window.representedFilename];
988 } else {
989 // Fall back to a size that looks good on the highest resolution screen available
990 auto fallbackSize = iconButton.frame.size.height * qGuiApp->devicePixelRatio();
991 iconButton.image = [NSImage imageFromQIcon:icon withSize:fallbackSize];
992 }
993}
994
996{
998 m_alertRequest = [NSApp requestUserAttention:NSCriticalRequest];
999 } else if (m_alertRequest != NoAlertRequest && !enabled) {
1000 [NSApp cancelUserAttentionRequest:m_alertRequest];
1002 }
1003}
1004
1006{
1008}
1009
1011{
1012 qCDebug(lcQpaWindow) << "QCocoaWindow::raise" << window();
1013
1014 // ### handle spaces (see Qt 4 raise_sys in qwidget_mac.mm)
1015 if (isContentView()) {
1016 if (m_view.window.visible) {
1017 {
1018 // Clean up auto-released temp objects from orderFront immediately.
1019 // Failure to do so has been observed to cause leaks also beyond any outer
1020 // autorelease pool (for example around a complete QWindow
1021 // construct-show-raise-hide-delete cycle), counter to expected autoreleasepool
1022 // behavior.
1024 [m_view.window orderFront:m_view.window];
1025 }
1026 static bool raiseProcess = qt_mac_resolveOption(true, "QT_MAC_SET_RAISE_PROCESS");
1027 if (raiseProcess)
1028 [NSApp activateIgnoringOtherApps:YES];
1029 }
1030 } else {
1031 [m_view.superview addSubview:m_view positioned:NSWindowAbove relativeTo:nil];
1032 }
1033}
1034
1036{
1037 qCDebug(lcQpaWindow) << "QCocoaWindow::lower" << window();
1038
1039 if (isContentView()) {
1040 if (m_view.window.visible)
1041 [m_view.window orderBack:m_view.window];
1042 } else {
1043 [m_view.superview addSubview:m_view positioned:NSWindowBelow relativeTo:nil];
1044 }
1045}
1046
1048{
1049 return !m_exposedRect.isEmpty();
1050}
1051
1053{
1054 // Child QWindows are not embedded
1055 if (window()->parent())
1056 return false;
1057
1058 // Top-level QWindows with non-Qt NSWindows are embedded
1059 if (m_view.window)
1060 return !([m_view.window isKindOfClass:[QNSWindow class]] ||
1061 [m_view.window isKindOfClass:[QNSPanel class]]);
1062
1063 // The window has no QWindow parent but also no NSWindow,
1064 // conservatively reuturn false.
1065 return false;
1066}
1067
1069{
1070 // OpenGL surfaces can be ordered either above(default) or below the NSWindow.
1071 // When ordering below the window must be tranclucent.
1072 static GLint openglSourfaceOrder = qt_mac_resolveOption(1, "QT_MAC_OPENGL_SURFACE_ORDER");
1073
1074 bool translucent = window()->format().alphaBufferSize() > 0
1075 || window()->opacity() < 1
1076 || !window()->mask().isEmpty()
1077 || (surface()->supportsOpenGL() && openglSourfaceOrder == -1);
1078 return !translucent;
1079}
1080
1082{
1084 if (!isContentView())
1085 return;
1086
1087 qCDebug(lcQpaWindow) << "QCocoaWindow::propagateSizeHints" << window()
1088 << "min:" << windowMinimumSize() << "max:" << windowMaximumSize()
1089 << "increment:" << windowSizeIncrement()
1090 << "base:" << windowBaseSize();
1091
1092 const NSWindow *window = m_view.window;
1093
1094 // Set the minimum content size.
1095 QSize minimumSize = windowMinimumSize();
1096 if (!minimumSize.isValid()) // minimumSize is (-1, -1) when not set. Make that (0, 0) for Cocoa.
1097 minimumSize = QSize(0, 0);
1098 window.contentMinSize = NSSizeFromCGSize(minimumSize.toCGSize());
1099
1100 // Set the maximum content size.
1101 window.contentMaxSize = NSSizeFromCGSize(windowMaximumSize().toCGSize());
1102
1103 // The window may end up with a fixed size; in this case the zoom button should be disabled.
1105
1106 // sizeIncrement is observed to take values of (-1, -1) and (0, 0) for windows that should be
1107 // resizable and that have no specific size increment set. Cocoa expects (1.0, 1.0) in this case.
1108 QSize sizeIncrement = windowSizeIncrement();
1109 if (sizeIncrement.isEmpty())
1110 sizeIncrement = QSize(1, 1);
1111 window.resizeIncrements = NSSizeFromCGSize(sizeIncrement.toCGSize());
1112
1113 QRect rect = geometry();
1114 QSize baseSize = windowBaseSize();
1115 if (!baseSize.isNull() && baseSize.isValid())
1116 [window setFrame:NSMakeRect(rect.x(), rect.y(), baseSize.width(), baseSize.height()) display:YES];
1117}
1118
1120{
1121 qCDebug(lcQpaWindow) << "QCocoaWindow::setOpacity" << level;
1122 if (!isContentView())
1123 return;
1124
1125 m_view.window.alphaValue = level;
1126}
1127
1129{
1130 qCDebug(lcQpaWindow) << "QCocoaWindow::setMask" << window() << region;
1131
1132 if (!region.isEmpty()) {
1133 QCFType<CGMutablePathRef> maskPath = CGPathCreateMutable();
1134 for (const QRect &r : region)
1135 CGPathAddRect(maskPath, nullptr, r.toCGRect());
1136 CAShapeLayer *maskLayer = [CAShapeLayer layer];
1137 maskLayer.path = maskPath;
1138 m_view.layer.mask = maskLayer;
1139 } else {
1140 m_view.layer.mask = nil;
1141 }
1142}
1143
1145{
1146 return false; // FIXME (QTBUG-106597)
1147}
1148
1150{
1151 return false; // FIXME (QTBUG-106597)
1152}
1153
1155{
1156 return WId(m_view);
1157}
1158
1160{
1161 qCDebug(lcQpaWindow) << "QCocoaWindow::setParent" << window() << (parentWindow ? parentWindow->window() : 0);
1162
1163 // Recreate in case we need to get rid of a NSWindow, or create one
1165
1167}
1168
1169NSView *QCocoaWindow::view() const
1170{
1171 return m_view;
1172}
1173
1175{
1176 return m_view.window;
1177}
1178
1180{
1181 // Release any previously created NSWindow.
1182 [m_nsWindow closeAndRelease];
1183 m_nsWindow = 0;
1184}
1185
1186// ----------------------- NSView notifications -----------------------
1187
1188void QCocoaWindow::viewDidChangeFrame()
1189{
1190 // Note: When the view is the content view, it would seem redundant
1191 // to deliver geometry changes both from windowDidResize and this
1192 // callback, but in some cases such as when macOS native tabbed
1193 // windows are enabled we may end up with the wrong geometry in
1194 // the initial windowDidResize callback when a new tab is created.
1196}
1197
1205void QCocoaWindow::viewDidChangeGlobalFrame()
1206{
1207 [m_view setNeedsDisplay:YES];
1208}
1209
1210// ----------------------- NSWindow notifications -----------------------
1211
1212// Note: The following notifications are delivered to every QCocoaWindow
1213// that is a child of the NSWindow that triggered the notification. Each
1214// callback should make sure to filter out notifications if they do not
1215// apply to that QCocoaWindow, e.g. if the window is not a content view.
1216
1217void QCocoaWindow::windowDidMove()
1218{
1219 if (!isContentView())
1220 return;
1221
1223
1224 // Moving a window might bring it out of maximized state
1226}
1227
1228void QCocoaWindow::windowDidResize()
1229{
1230 if (!isContentView())
1231 return;
1232
1234
1235 if (!m_view.inLiveResize)
1237}
1238
1239void QCocoaWindow::windowDidEndLiveResize()
1240{
1241 if (!isContentView())
1242 return;
1243
1245}
1246
1247void QCocoaWindow::windowDidBecomeKey()
1248{
1249 // The NSWindow we're part of become key. Check if we're the first
1250 // responder, and if so, deliver focus window change to our window.
1251 if (m_view.window.firstResponder != m_view)
1252 return;
1253
1254 qCDebug(lcQpaWindow) << m_view.window << "became key window."
1255 << "Updating focus window to" << this << "with view" << m_view;
1256
1257 if (windowIsPopupType()) {
1258 qCDebug(lcQpaWindow) << "Window is popup. Skipping focus window change.";
1259 return;
1260 }
1261
1262 // See also [QNSView becomeFirstResponder]
1263 QWindowSystemInterface::handleFocusWindowChanged<QWindowSystemInterface::SynchronousDelivery>(
1265}
1266
1267void QCocoaWindow::windowDidResignKey()
1268{
1269 // The NSWindow we're part of lost key. Check if we're the first
1270 // responder, and if so, deliver window deactivation to our window.
1271 if (m_view.window.firstResponder != m_view)
1272 return;
1273
1274 qCDebug(lcQpaWindow) << m_view.window << "resigned key window."
1275 << "Clearing focus window" << this << "with view" << m_view;
1276
1277 // Make sure popups are closed before we deliver activation changes, which are
1278 // otherwise ignored by QApplication.
1280
1281 // The current key window will be non-nil if another window became key. If that
1282 // window is a Qt window, we delay the window activation event until the didBecomeKey
1283 // notification is delivered to the active window, to ensure an atomic update.
1284 NSWindow *newKeyWindow = [NSApp keyWindow];
1285 if (newKeyWindow && newKeyWindow != m_view.window
1286 && [newKeyWindow conformsToProtocol:@protocol(QNSWindowProtocol)]) {
1287 qCDebug(lcQpaWindow) << "New key window" << newKeyWindow
1288 << "is Qt window. Deferring focus window change.";
1289 return;
1290 }
1291
1292 // Lost key window, go ahead and set the active window to zero
1293 if (!windowIsPopupType()) {
1294 QWindowSystemInterface::handleFocusWindowChanged<QWindowSystemInterface::SynchronousDelivery>(
1296 }
1297}
1298
1299void QCocoaWindow::windowDidOrderOnScreen()
1300{
1301 // The current mouse window needs to get a leave event when a popup window opens.
1302 // For modal dialogs, QGuiApplicationPrivate::showModalWindow takes care of this.
1303 if (QWindowPrivate::get(window())->isPopup()) {
1304 QWindowSystemInterface::handleLeaveEvent<QWindowSystemInterface::SynchronousDelivery>
1306 }
1307
1308 [m_view setNeedsDisplay:YES];
1309}
1310
1311void QCocoaWindow::windowDidOrderOffScreen()
1312{
1314 // We are closing a window, so the window that is now under the mouse
1315 // might need to get an Enter event if it isn't already the mouse window.
1316 if (window()->type() & Qt::Window) {
1317 const QPointF screenPoint = QCocoaScreen::mapFromNative([NSEvent mouseLocation]);
1318 if (QWindow *windowUnderMouse = QGuiApplication::topLevelAt(screenPoint.toPoint())) {
1319 if (windowUnderMouse != QGuiApplicationPrivate::instance()->currentMouseWindow) {
1320 const auto windowPoint = windowUnderMouse->mapFromGlobal(screenPoint);
1321 // asynchronous delivery on purpose
1322 QWindowSystemInterface::handleEnterEvent<QWindowSystemInterface::AsynchronousDelivery>
1323 (windowUnderMouse, windowPoint, screenPoint);
1324 }
1325 }
1326 }
1327}
1328
1329void QCocoaWindow::windowDidChangeOcclusionState()
1330{
1331 // Note, we don't take the view's hiddenOrHasHiddenAncestor state into
1332 // account here, but instead leave that up to handleExposeEvent, just
1333 // like all the other signals that could potentially change the exposed
1334 // state of the window.
1335 bool visible = m_view.window.occlusionState & NSWindowOcclusionStateVisible;
1336 qCDebug(lcQpaWindow) << "Occlusion state of" << m_view.window << "for"
1337 << window() << "changed to" << (visible ? "visible" : "occluded");
1338
1339 if (visible)
1340 [m_view setNeedsDisplay:YES];
1341 else
1343}
1344
1345void QCocoaWindow::windowDidChangeScreen()
1346{
1347 if (!window())
1348 return;
1349
1350 // Note: When a window is resized to 0x0 Cocoa will report the window's screen as nil
1351 NSScreen *nsScreen = m_view.window.screen;
1352
1353 qCDebug(lcQpaWindow) << window() << "did change" << nsScreen;
1354 QCocoaScreen::updateScreens();
1355
1356 auto *previousScreen = static_cast<QCocoaScreen*>(screen());
1357 auto *currentScreen = QCocoaScreen::get(nsScreen);
1358
1359 qCDebug(lcQpaWindow) << "Screen changed for" << window() << "from" << previousScreen << "to" << currentScreen;
1360
1361 // Note: The previous screen may be the same as the current screen, either because
1362 // a) the screen was just reconfigured, which still results in AppKit sending an
1363 // NSWindowDidChangeScreenNotification, b) because the previous screen was removed,
1364 // and we ended up calling QWindow::setScreen to move the window, which doesn't
1365 // actually move the window to the new screen, or c) because we've delivered the
1366 // screen change to the top level window, which will make all the child windows
1367 // of that window report the new screen when requested via QWindow::screen().
1368 // We still need to deliver the screen change in all these cases, as the
1369 // device-pixel ratio may have changed, and needs to be delivered to all
1370 // windows, both top level and child windows.
1371
1372 QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(
1373 window(), currentScreen ? currentScreen->screen() : nullptr);
1374
1375 if (currentScreen && hasPendingUpdateRequest()) {
1376 // Restart display-link on new screen. We need to do this unconditionally,
1377 // since we can't rely on the previousScreen reflecting whether or not the
1378 // window actually moved from one screen to another, or just stayed on the
1379 // same screen.
1380 currentScreen->requestUpdate();
1381 }
1382}
1383
1384// ----------------------- NSWindowDelegate callbacks -----------------------
1385
1387{
1388 qCDebug(lcQpaWindow) << "QCocoaWindow::windowShouldClose" << window();
1389
1390 // This callback should technically only determine if the window
1391 // should (be allowed to) close, but since our QPA API to determine
1392 // that also involves actually closing the window we do both at the
1393 // same time, instead of doing the latter in windowWillClose.
1394
1395 // If the window is closed, we will release and deallocate the NSWindow.
1396 // But frames higher up in the stack might still expect the window to
1397 // be alive, since the windowShouldClose: callback is technically only
1398 // supposed to answer YES or NO. To ensure the window is still alive
1399 // we put an autorelease in the closest pool (typically the runloop).
1400 [[m_view.window retain] autorelease];
1401
1402 return QWindowSystemInterface::handleCloseEvent<QWindowSystemInterface::SynchronousDelivery>(window());
1403}
1404
1405// ----------------------------- QPA forwarding -----------------------------
1406
1408{
1409 // Prevent geometry change during initialization, as that will result
1410 // in a resize event, and Qt expects those to come after the show event.
1411 // FIXME: Remove once we've clarified the Qt behavior for this.
1412 if (!m_initialized)
1413 return;
1414
1415 // It can happen that the current NSWindow is nil (if we are changing styleMask
1416 // from/to borderless, and the content view is being re-parented), which results
1417 // in invalid coordinates.
1418 if (m_inSetStyleMask && !m_view.window)
1419 return;
1420
1421 QRect newGeometry;
1422 if (isContentView() && !isEmbedded()) {
1423 // Content views are positioned at (0, 0) in the window, so we resolve via the window
1424 CGRect contentRect = [m_view.window contentRectForFrameRect:m_view.window.frame];
1425
1426 // The result above is in native screen coordinates, so remap to the Qt coordinate system
1427 newGeometry = QCocoaScreen::mapFromNative(contentRect).toRect();
1428 } else {
1429 // QNSView has isFlipped set, so no need to remap the geometry
1430 newGeometry = QRectF::fromCGRect(m_view.frame).toRect();
1431 }
1432
1433 qCDebug(lcQpaWindow) << "QCocoaWindow::handleGeometryChange" << window()
1434 << "current" << geometry() << "new" << newGeometry;
1435
1437
1438 // Guard against processing window system events during QWindow::setGeometry
1439 // calls, which Qt and Qt applications do not expect.
1440 if (!m_inSetGeometry)
1442}
1443
1445{
1446 // Ideally we'd implement isExposed() in terms of these properties,
1447 // plus the occlusionState of the NSWindow, and let the expose event
1448 // pull the exposed state out when needed. However, when the window
1449 // is first shown we receive a drawRect call where the occlusionState
1450 // of the window is still hidden, but we still want to prepare the
1451 // window for display by issuing an expose event to Qt. To work around
1452 // this we don't use the occlusionState directly, but instead base
1453 // the exposed state on the region we get in, which in the case of
1454 // a window being obscured is an empty region, and in the case of
1455 // a drawRect call is a non-null region, even if occlusionState
1456 // is still hidden. This ensures the window is prepared for display.
1457 if (m_view.window.visible && m_view.window.screen
1458 && !geometry().size().isEmpty() && !region.isEmpty()
1459 && !m_view.hiddenOrHasHiddenAncestor) {
1460 m_exposedRect = region.boundingRect();
1461 } else {
1462 m_exposedRect = QRect();
1463 }
1464
1465 qCDebug(lcQpaDrawing) << "QCocoaWindow::handleExposeEvent" << window() << region << "isExposed" << isExposed();
1466 QWindowSystemInterface::handleExposeEvent<QWindowSystemInterface::SynchronousDelivery>(window(), region);
1467}
1468
1469// --------------------------------------------------------------------------
1470
1472{
1473 if (type == Qt::Widget)
1474 type = window()->type();
1475 if (type == Qt::Tool)
1476 return false; // Qt::Tool has the Popup bit set but isn't, at least on Mac.
1477
1478 return ((type & Qt::Popup) == Qt::Popup);
1479}
1480
1491{
1492 return m_view.window.contentView == m_view;
1493}
1494
1502{
1504
1505 QPlatformWindow *parentWindow = QPlatformWindow::parent();
1506 auto *parentCocoaWindow = static_cast<QCocoaWindow *>(parentWindow);
1507
1508 QCocoaWindow *oldParentCocoaWindow = nullptr;
1509 if (QNSView *qnsView = qnsview_cast(m_view.superview))
1510 oldParentCocoaWindow = qnsView.platformWindow;
1511
1512 if (isForeignWindow()) {
1513 // A foreign window is created as such, and can never move between being
1514 // foreign and not, so we don't need to get rid of any existing NSWindows,
1515 // nor create new ones, as a foreign window is a single simple NSView.
1516 qCDebug(lcQpaWindow) << "Skipping NSWindow management for foreign window" << this;
1517
1518 // We do however need to manage the parent relationship
1519 if (parentCocoaWindow)
1520 [parentCocoaWindow->m_view addSubview:m_view];
1521 else if (oldParentCocoaWindow)
1522 [m_view removeFromSuperview];
1523
1524 return;
1525 }
1526
1527 const bool isEmbeddedView = isEmbedded();
1528 RecreationReasons recreateReason = RecreationNotNeeded;
1529
1530 if (parentWindow != oldParentCocoaWindow)
1531 recreateReason |= ParentChanged;
1532
1533 if (!m_view.window)
1534 recreateReason |= MissingWindow;
1535
1536 // If the modality has changed the style mask will need updating
1537 if (m_windowModality != window()->modality())
1538 recreateReason |= WindowModalityChanged;
1539
1540 Qt::WindowType type = window()->type();
1541
1542 const bool shouldBeContentView = !parentWindow
1543 && !((type & Qt::SubWindow) == Qt::SubWindow)
1544 && !isEmbeddedView;
1545 if (isContentView() != shouldBeContentView)
1546 recreateReason |= ContentViewChanged;
1547
1548 const bool isPanel = isContentView() && [m_view.window isKindOfClass:[QNSPanel class]];
1549 const bool shouldBePanel = shouldBeContentView &&
1550 ((type & Qt::Popup) == Qt::Popup || (type & Qt::Dialog) == Qt::Dialog);
1551
1552 if (isPanel != shouldBePanel)
1553 recreateReason |= PanelChanged;
1554
1555 qCDebug(lcQpaWindow) << "QCocoaWindow::recreateWindowIfNeeded" << window() << recreateReason;
1556
1557 if (recreateReason == RecreationNotNeeded)
1558 return;
1559
1560 // Remove current window (if any)
1561 if ((isContentView() && !shouldBeContentView) || (recreateReason & PanelChanged)) {
1562 if (m_nsWindow) {
1563 qCDebug(lcQpaWindow) << "Getting rid of existing window" << m_nsWindow;
1564 [m_nsWindow closeAndRelease];
1565 if (isContentView() && !isEmbeddedView) {
1566 // We explicitly disassociate m_view from the window's contentView,
1567 // as AppKit does not automatically do this in response to removing
1568 // the view from the NSThemeFrame subview list, so we might end up
1569 // with a NSWindow contentView pointing to a deallocated NSView.
1570 m_view.window.contentView = nil;
1571 }
1572 m_nsWindow = nil;
1573 }
1574 }
1575
1576 if (shouldBeContentView && !m_nsWindow) {
1577 // Move view to new NSWindow if needed
1578 if (auto *newWindow = createNSWindow(shouldBePanel)) {
1579 qCDebug(lcQpaWindow) << "Ensuring that" << m_view << "is content view for" << newWindow;
1580 [m_view setPostsFrameChangedNotifications:NO];
1581 [newWindow setContentView:m_view];
1582 [m_view setPostsFrameChangedNotifications:YES];
1583
1584 m_nsWindow = newWindow;
1585 Q_ASSERT(m_view.window == m_nsWindow);
1586 }
1587 }
1588
1589 if (parentCocoaWindow) {
1590 // Child windows have no NSWindow, re-parent to superview instead
1591 [parentCocoaWindow->m_view addSubview:m_view];
1592 }
1593}
1594
1596{
1597 qCDebug(lcQpaDrawing) << "QCocoaWindow::requestUpdate" << window()
1598 << "using" << (updatesWithDisplayLink() ? "display-link" : "timer");
1599
1600 if (updatesWithDisplayLink()) {
1601 if (!static_cast<QCocoaScreen *>(screen())->requestUpdate()) {
1602 qCDebug(lcQpaDrawing) << "Falling back to timer-based update request";
1604 }
1605 } else {
1606 // Fall back to the un-throttled timer-based callback
1608 }
1609}
1610
1612{
1613 // Update via CVDisplayLink if Vsync is enabled
1614 return format().swapInterval() != 0;
1615}
1616
1618{
1619 qCDebug(lcQpaDrawing) << "Delivering update request to" << window();
1621}
1622
1624{
1626 [m_view.window makeFirstResponder:m_view];
1627 [m_view.window makeKeyWindow];
1628}
1629
1630/*
1631 Closes all popups, and removes observers and monitors.
1632*/
1634{
1635 QGuiApplicationPrivate::instance()->closeAllPopups();
1636
1638}
1639
1641{
1643 [NSEvent removeMonitor:s_globalMouseMonitor];
1645 }
1647 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:s_applicationActivationObserver];
1649 }
1650}
1651
1653{
1654 // we open a popup window while we are not active. None of our existing event
1655 // handlers will get called if the user now clicks anywhere outside the application
1656 // or activates another window. Use a global event monitor to watch for mouse
1657 // presses, and close popups. We also want mouse tracking in the popup to work, so
1658 // also watch for MouseMoved.
1659 if (!s_globalMouseMonitor) {
1660 // we only get LeftMouseDown events when we also set LeftMouseUp.
1661 constexpr NSEventMask mouseButtonMask = NSEventTypeLeftMouseDown | NSEventTypeLeftMouseUp
1662 | NSEventMaskRightMouseDown | NSEventMaskOtherMouseDown
1663 | NSEventMaskMouseMoved;
1664 s_globalMouseMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:mouseButtonMask
1665 handler:^(NSEvent *e){
1666 if (!QGuiApplicationPrivate::instance()->popupActive()) {
1668 return;
1669 }
1670 const auto eventType = cocoaEvent2QtMouseEvent(e);
1671 if (eventType == QEvent::MouseMove) {
1672 if (s_windowUnderMouse) {
1673 QWindow *window = s_windowUnderMouse->window();
1674 const auto button = cocoaButton2QtButton(e);
1675 const auto buttons = currentlyPressedMouseButtons();
1676 const auto globalPoint = QCocoaScreen::mapFromNative(NSEvent.mouseLocation);
1677 const auto localPoint = window->mapFromGlobal(globalPoint.toPoint());
1678 QWindowSystemInterface::handleMouseEvent(window, localPoint, globalPoint,
1679 buttons, button, eventType);
1680 }
1681 } else {
1683 }
1684 }];
1685 }
1686 // The activation observer also gets called when we become active because the user clicks
1687 // into the popup. This should not close the popup, so QCocoaApplicationDelegate's
1688 // applicationDidBecomeActive implementation removes this observer.
1690 s_applicationActivationObserver = [[[NSWorkspace sharedWorkspace] notificationCenter]
1691 addObserverForName:NSWorkspaceDidActivateApplicationNotification
1692 object:nil queue:nil
1693 usingBlock:^(NSNotification *){
1695 }];
1696 }
1697}
1698
1699QCocoaNSWindow *QCocoaWindow::createNSWindow(bool shouldBePanel)
1700{
1702
1703 Qt::WindowType type = window()->type();
1704 Qt::WindowFlags flags = window()->flags();
1705
1706 QRect rect = geometry();
1707
1708 QScreen *targetScreen = nullptr;
1710 if (screen->geometry().contains(rect.topLeft())) {
1711 targetScreen = screen;
1712 break;
1713 }
1714 }
1715
1716 NSWindowStyleMask styleMask = windowStyleMask(flags);
1717
1718 if (!targetScreen) {
1719 qCWarning(lcQpaWindow) << "Window position" << rect << "outside any known screen, using primary screen";
1720 targetScreen = QGuiApplication::primaryScreen();
1721 // Unless the window is created as borderless AppKit won't find a position and
1722 // screen that's close to the requested invalid position, and will always place
1723 // the window on the primary screen.
1724 styleMask = NSWindowStyleMaskBorderless;
1725 }
1726
1727 rect.translate(-targetScreen->geometry().topLeft());
1728 auto *targetCocoaScreen = static_cast<QCocoaScreen *>(targetScreen->handle());
1729 NSRect contentRect = QCocoaScreen::mapToNative(rect, targetCocoaScreen);
1730
1731 if (targetScreen->primaryOrientation() == Qt::PortraitOrientation) {
1732 // The macOS window manager has a bug, where if a screen is rotated, it will not allow
1733 // a window to be created within the area of the screen that has a Y coordinate (I quadrant)
1734 // higher than the height of the screen in its non-rotated state (including a magic padding
1735 // of 24 points), unless the window is created with the NSWindowStyleMaskBorderless style mask.
1736 if (styleMask && (contentRect.origin.y + 24 > targetScreen->geometry().width())) {
1737 qCDebug(lcQpaWindow) << "Window positioned on portrait screen."
1738 << "Adjusting style mask during creation";
1739 styleMask = NSWindowStyleMaskBorderless;
1740 }
1741 }
1742
1743 // Create NSWindow
1744 Class windowClass = shouldBePanel ? [QNSPanel class] : [QNSWindow class];
1745 QCocoaNSWindow *nsWindow = [[windowClass alloc] initWithContentRect:contentRect
1746 // Mask will be updated in setWindowFlags if not the final mask
1747 styleMask:styleMask
1748 // Deferring window creation breaks OpenGL (the GL context is
1749 // set up before the window is shown and needs a proper window)
1750 backing:NSBackingStoreBuffered defer:NO
1751 screen:targetCocoaScreen->nativeScreen()
1752 platformWindow:this];
1753
1754 // The resulting screen can be different from the screen requested if
1755 // for example the application has been assigned to a specific display.
1756 auto resultingScreen = QCocoaScreen::get(nsWindow.screen);
1757
1758 // But may not always be resolved at this point, in which case we fall back
1759 // to the target screen. The real screen will be delivered as a screen change
1760 // when resolved as part of ordering the window on screen.
1761 if (!resultingScreen)
1762 resultingScreen = targetCocoaScreen;
1763
1764 if (resultingScreen->screen() != window()->screen()) {
1766 QWindowSystemInterface::SynchronousDelivery>(window(), resultingScreen->screen());
1767 }
1768
1769 static QSharedPointer<QNSWindowDelegate> sharedDelegate([[QNSWindowDelegate alloc] init],
1770 [](QNSWindowDelegate *delegate) { [delegate release]; });
1771 nsWindow.delegate = sharedDelegate.get();
1772
1773 // Prevent Cocoa from releasing the window on close. Qt
1774 // handles the close event asynchronously and we want to
1775 // make sure that NSWindow stays valid until the
1776 // QCocoaWindow is deleted by Qt.
1777 [nsWindow setReleasedWhenClosed:NO];
1778
1779 if (alwaysShowToolWindow()) {
1780 static dispatch_once_t onceToken;
1781 dispatch_once(&onceToken, ^{
1782 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
1783 [center addObserver:[QNSWindow class] selector:@selector(applicationActivationChanged:)
1784 name:NSApplicationWillResignActiveNotification object:nil];
1785 [center addObserver:[QNSWindow class] selector:@selector(applicationActivationChanged:)
1786 name:NSApplicationWillBecomeActiveNotification object:nil];
1787 });
1788 }
1789
1790 nsWindow.restorable = NO;
1791 nsWindow.level = windowLevel(flags);
1792 nsWindow.tabbingMode = NSWindowTabbingModeDisallowed;
1793
1794 if (shouldBePanel) {
1795 // Qt::Tool windows hide on app deactivation, unless Qt::WA_MacAlwaysShowToolWindow is set
1796 nsWindow.hidesOnDeactivate = ((type & Qt::Tool) == Qt::Tool) && !alwaysShowToolWindow();
1797
1798 // Make popup windows show on the same desktop as the parent window
1799 nsWindow.collectionBehavior = NSWindowCollectionBehaviorFullScreenAuxiliary
1800 | NSWindowCollectionBehaviorMoveToActiveSpace;
1801
1802 if ((type & Qt::Popup) == Qt::Popup) {
1803 nsWindow.hasShadow = YES;
1804 nsWindow.animationBehavior = NSWindowAnimationBehaviorUtilityWindow;
1807 }
1808 }
1809
1810 // Persist modality so we can detect changes later on
1812
1814
1815 // We propagate the view's color space granulary to both the IOSurfaces
1816 // used for QSurface::RasterSurface, as well as the CAMetalLayer used for
1817 // QSurface::MetalSurface, but for QSurface::OpenGLSurface we don't have
1818 // that option as we use NSOpenGLContext instead of CAOpenGLLayer. As a
1819 // workaround we set the NSWindow's color space, which affects GL drawing
1820 // with NSOpenGLContext as well. This does not conflict with the granular
1821 // modifications we do to each surface for raster or Metal.
1822 if (auto *qtView = qnsview_cast(m_view))
1823 nsWindow.colorSpace = qtView.colorSpace;
1824
1825 return nsWindow;
1826}
1827
1829{
1830 return qt_mac_resolveOption(false, window(), "_q_macAlwaysShowToolWindow", "");
1831}
1832
1834{
1835 if (!isContentView())
1836 return false;
1837
1838 m_view.window.documentEdited = modified;
1839 return true;
1840}
1841
1843{
1844 m_menubar = mb;
1845}
1846
1848{
1849 return m_menubar;
1850}
1851
1853{
1854 // Setting a cursor in a foreign view is not supported
1855 if (isForeignWindow())
1856 return;
1857
1858 qCInfo(lcQpaMouse) << "Setting" << this << "cursor to" << cursor;
1859
1861 if (cursor == view.cursor)
1862 return;
1863
1864 view.cursor = cursor;
1865
1866 // We're not using the the legacy cursor rects API to manage our
1867 // cursor, but calling this function also invalidates AppKit's
1868 // view of whether or not we need a cursorUpdate callback for
1869 // our tracking area.
1870 [m_view.window invalidateCursorRectsForView:m_view];
1871
1872 // We've informed AppKit that we need a cursorUpdate, but cursor
1873 // updates for tracking areas are deferred in some cases, such as
1874 // when the mouse is down, whereas we want a synchronous update.
1875 // To ensure an updated cursor we synthesize a cursor update event
1876 // now if the window is otherwise allowed to change the cursor.
1877 auto locationInWindow = m_view.window.mouseLocationOutsideOfEventStream;
1878 auto locationInSuperview = [m_view.superview convertPoint:locationInWindow fromView:nil];
1879 bool mouseIsOverView = [m_view hitTest:locationInSuperview] == m_view;
1880 auto utilityMask = NSWindowStyleMaskUtilityWindow | NSWindowStyleMaskTitled;
1881 bool isUtilityWindow = (m_view.window.styleMask & utilityMask) == utilityMask;
1882 if (mouseIsOverView && (m_view.window.keyWindow || isUtilityWindow)) {
1883 qCDebug(lcQpaMouse) << "Synthesizing cursor update";
1884 [m_view cursorUpdate:[NSEvent enterExitEventWithType:NSEventTypeCursorUpdate
1885 location:locationInWindow modifierFlags:0 timestamp:0
1886 windowNumber:m_view.window.windowNumber context:nil
1887 eventNumber:0 trackingNumber:0 userData:0]];
1888 }
1889}
1890
1892{
1893 m_registerTouchCount += enable ? 1 : -1;
1894 if (enable && m_registerTouchCount == 1)
1895 m_view.allowedTouchTypes |= NSTouchTypeMaskIndirect;
1896 else if (m_registerTouchCount == 0)
1897 m_view.allowedTouchTypes &= ~NSTouchTypeMaskIndirect;
1898}
1899
1900void QCocoaWindow::registerContentBorderArea(quintptr identifier, int upper, int lower)
1901{
1902 m_contentBorderAreas.insert(identifier, BorderRange(identifier, upper, lower));
1904}
1905
1911
1917
1919{
1920 if (!window && isContentView())
1921 window = m_view.window;
1922
1923 if (!window)
1924 return;
1925
1927 window.styleMask = window.styleMask & ~NSWindowStyleMaskTexturedBackground;
1928 [window.contentView.superview setNeedsDisplay:YES];
1929 window.titlebarAppearsTransparent = NO;
1930 return;
1931 }
1932
1933 // Find consecutive registered border areas, starting from the top.
1934 std::vector<BorderRange> ranges(m_contentBorderAreas.cbegin(), m_contentBorderAreas.cend());
1935 std::sort(ranges.begin(), ranges.end());
1936 int effectiveTopContentBorderThickness = 0;
1937 for (BorderRange range : ranges) {
1938 // Skip disiabled ranges (typically hidden tool bars)
1939 if (!m_enabledContentBorderAreas.value(range.identifier, false))
1940 continue;
1941
1942 // Is this sub-range adjacent to or overlapping the
1943 // existing total border area range? If so merge
1944 // it into the total range,
1945 if (range.upper <= (effectiveTopContentBorderThickness + 1))
1946 effectiveTopContentBorderThickness = qMax(effectiveTopContentBorderThickness, range.lower);
1947 else
1948 break;
1949 }
1950
1951 int effectiveBottomContentBorderThickness = 0;
1952
1953 [window setStyleMask:[window styleMask] | NSWindowStyleMaskTexturedBackground];
1954 window.titlebarAppearsTransparent = YES;
1955
1956 // Setting titlebarAppearsTransparent to YES means that the border thickness has to account
1957 // for the title bar height as well, otherwise sheets will not be presented at the correct
1958 // position, which should be (title bar height + top content border size).
1959 const NSRect frameRect = window.frame;
1960 const NSRect contentRect = [window contentRectForFrameRect:frameRect];
1961 const CGFloat titlebarHeight = frameRect.size.height - contentRect.size.height;
1962 effectiveTopContentBorderThickness += titlebarHeight;
1963
1964 [window setContentBorderThickness:effectiveTopContentBorderThickness forEdge:NSMaxYEdge];
1965 [window setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge];
1966
1967 [window setContentBorderThickness:effectiveBottomContentBorderThickness forEdge:NSMinYEdge];
1968 [window setAutorecalculatesContentBorderThickness:NO forEdge:NSMinYEdge];
1969
1970 [[[window contentView] superview] setNeedsDisplay:YES];
1971}
1972
1974{
1976 return false;
1977
1978 // Determine if the given y position (relative to the content area) is inside the
1979 // unified toolbar area. Note that the value returned by contentBorderThicknessForEdge
1980 // includes the title bar height; subtract it.
1981 const int contentBorderThickness = [m_view.window contentBorderThicknessForEdge:NSMaxYEdge];
1982 const NSRect frameRect = m_view.window.frame;
1983 const NSRect contentRect = [m_view.window contentRectForFrameRect:frameRect];
1984 const CGFloat titlebarHeight = frameRect.size.height - contentRect.size.height;
1985 return 0 <= position && position < (contentBorderThickness - titlebarHeight);
1986}
1987
1989{
1990 // The documented way to observe the relationship between device-independent
1991 // and device pixels is to use one for the convertToBacking functions. Other
1992 // methods such as [NSWindow backingScaleFactor] might not give the correct
1993 // result, for example if setWantsBestResolutionOpenGLSurface is not set or
1994 // or ignored by the OpenGL driver.
1995 NSSize backingSize = [m_view convertSizeToBacking:NSMakeSize(1.0, 1.0)];
1996 return backingSize.height;
1997}
1998
2000{
2001 QWindow *targetWindow = window();
2002 for (QObject *child : targetWindow->children())
2003 if (QWindow *childWindow = qobject_cast<QWindow *>(child))
2004 if (QPlatformWindow *handle = childWindow->handle())
2005 if (handle->isExposed() && childWindow->geometry().contains(windowPoint))
2006 targetWindow = static_cast<QCocoaWindow*>(handle)->childWindowAt(windowPoint - childWindow->position());
2007
2008 return targetWindow;
2009}
2010
2012{
2013 // This function speaks up if there's any reason
2014 // to refuse key window or first responder state.
2015
2017 return true;
2018
2019 // For application modal windows, as well as direct parent windows
2020 // of window modal windows, AppKit takes care of blocking interaction.
2021 // The Qt expectation however, is that all transient parents of a
2022 // window modal window is blocked, as reflected by QGuiApplication.
2023 // We reflect this by returning false from this function for transient
2024 // parents blocked by a modal window, but limit it to the cases not
2025 // covered by AppKit to avoid potential unwanted side effects.
2026 QWindow *modalWindow = nullptr;
2027 if (QGuiApplicationPrivate::instance()->isWindowBlocked(window(), &modalWindow)) {
2028 if (modalWindow->modality() == Qt::WindowModal && modalWindow->transientParent() != window()) {
2029 qCDebug(lcQpaWindow) << "Refusing key window for" << this << "due to being"
2030 << "blocked by" << modalWindow;
2031 return true;
2032 }
2033 }
2034
2035 if (m_inSetVisible) {
2036 QVariant showWithoutActivating = window()->property("_q_showWithoutActivating");
2037 if (showWithoutActivating.isValid() && showWithoutActivating.toBool())
2038 return true;
2039 }
2040
2041 return false;
2042}
2043
2045{
2046 switch (event->type()) {
2050 break;
2051 default:
2052 break;
2053 }
2054
2056}
2057
2059{
2060 if (!m_view)
2061 return QPoint();
2062 const NSPoint origin = [m_view isFlipped] ? NSMakePoint(0, [m_view frame].size.height)
2063 : NSMakePoint(0, 0);
2064 const NSRect visibleRect = [m_view visibleRect];
2065
2066 return QPoint(visibleRect.origin.x, -visibleRect.origin.y + (origin.y - visibleRect.size.height));
2067}
2068
2070{
2071 if (!isContentView())
2072 return QMargins();
2073
2074 NSRect frameW = m_view.window.frame;
2075 NSRect frameC = [m_view.window contentRectForFrameRect:frameW];
2076
2077 return QMargins(frameW.origin.x - frameC.origin.x,
2078 (frameW.origin.y + frameW.size.height) - (frameC.origin.y + frameC.size.height),
2079 (frameW.origin.x + frameW.size.width) - (frameC.origin.x + frameC.size.width),
2080 frameC.origin.y - frameW.origin.y);
2081}
2082
2087
2088#ifndef QT_NO_DEBUG_STREAM
2090{
2091 QDebugStateSaver saver(debug);
2092 debug.nospace();
2093 debug << "QCocoaWindow(" << (const void *)window;
2094 if (window)
2095 debug << ", window=" << window->window();
2096 debug << ')';
2097 return debug;
2098}
2099#endif // !QT_NO_DEBUG_STREAM
2100
2102
2103#include "moc_qcocoawindow.cpp"
static QCocoaIntegration * instance()
static CGPoint mapToNative(const QPointF &pos, QCocoaScreen *screen=QCocoaScreen::primaryScreen())
static QCocoaScreen * get(NSScreen *nsScreen)
bool requestUpdate()
static QPointF mapFromNative(CGPoint pos, QCocoaScreen *screen=QCocoaScreen::primaryScreen())
void handleWindowStateChanged(HandleFlags flags=NoHandleFlags)
static void setupPopupMonitor()
void setContentBorderAreaEnabled(quintptr identifier, bool enable)
bool alwaysShowToolWindow() const
void setMenubar(QCocoaMenuBar *mb)
void toggleMaximized()
QWindow * childWindowAt(QPoint windowPoint)
void setAlertState(bool enabled) override
Reimplement this method to set whether the window demands attention (for example, by flashing the tas...
int m_registerTouchCount
Qt::WindowModality m_windowModality
void setWindowTitle(const QString &title) override
Reimplement to set the window title to title.
void setWindowCursor(NSCursor *cursor)
bool testContentBorderAreaPosition(int position) const
NSView * view() const
QRect normalGeometry() const override
the geometry of the window as it will appear when shown as a normal (not maximized or full screen) to...
bool updatesWithDisplayLink() const
bool m_inSetStyleMask
WId winId() const override
Reimplement in subclasses to return a handle to the native window.
bool isForeignWindow() const override
bool setMouseGrabEnabled(bool grab) override
QCocoaNSWindow * m_nsWindow
void propagateSizeHints() override
Reimplement to propagate the size hints of the QWindow.
void setOpacity(qreal level) override
Reimplement to be able to let Qt set the opacity level of a window.
QCocoaNSWindow * createNSWindow(bool shouldBePanel)
void requestActivateWindow() override
Reimplement to let Qt be able to request activation/focus for a window.
static id s_globalMouseMonitor
void setMask(const QRegion &region) override
Reimplement to be able to let Qt set the mask of a window.
QHash< quintptr, bool > m_enabledContentBorderAreas
QCocoaWindow(QWindow *tlw, WId nativeHandle=0)
void setFrameStrutEventsEnabled(bool enabled) override
Reimplement this method to set whether frame strut events should be sent to enabled.
void toggleFullScreen()
QCocoaMenuBar * menubar() const
void setWindowFilePath(const QString &filePath) override
Reimplement to set the window file path to filePath.
void setCocoaGeometry(const QRect &rect)
QRect geometry() const override
Returns the current geometry of a window.
NSInteger windowLevel(Qt::WindowFlags flags)
void setContentBorderEnabled(bool enable) override
bool isOpaque() const
void setParent(const QPlatformWindow *window) override
This function is called to enable native child window in QPA.
static void removePopupMonitor()
bool m_frameStrutEventsEnabled
bool setWindowModified(bool modified) override
Reimplement to be able to let Qt indicate that the window has been modified.
NSWindow * nativeWindow() const
void applyContentBorderThickness(NSWindow *window=nullptr)
void setWindowState(Qt::WindowStates state) override
Changes the state of the NSWindow, going in/out of minimize/zoomed/fullscreen.
bool windowShouldClose()
void registerTouch(bool enable)
void updateTitleBarButtons(Qt::WindowFlags flags)
QMargins frameMargins() const override
void setGeometry(const QRect &rect) override
This function is called by Qt whenever a window is moved or resized using the QWindow API.
void updateNormalGeometry()
bool isFixedSize() const
bool isAlertState() const override
Reimplement this method return whether the window is in an alert state.
bool isExposed() const override
Returns if this window is exposed in the windowing system.
void deliverUpdateRequest() override
Delivers an QEvent::UpdateRequest event to the window.
bool isEmbedded() const override
Returns true if the window is a child of a non-Qt window.
void setWindowFlags(Qt::WindowFlags flags) override
Requests setting the window flags of this surface to flags.
void lower() override
Reimplement to be able to let Qt lower windows to the bottom of the desktop.
bool shouldRefuseKeyWindowAndFirstResponder()
void requestUpdate() override
Requests an QEvent::UpdateRequest event.
static QPointer< QCocoaWindow > s_windowUnderMouse
static void closeAllPopups()
void setWindowIcon(const QIcon &icon) override
Reimplement to set the window icon to icon.
bool isContentView() const
Checks if the window is the content view of its immediate NSWindow.
NSInteger m_alertRequest
QRect m_normalGeometry
void recreateWindowIfNeeded()
Recreates (or removes) the NSWindow for this QWindow, if needed.
void handleGeometryChange()
bool m_drawContentBorderGradient
bool m_resizableTransientParent
NSUInteger windowStyleMask(Qt::WindowFlags flags)
QCocoaMenuBar * m_menubar
void setVisible(bool visible) override
Reimplemented in subclasses to show the surface if visible is true, and hide it if visible is false.
bool startSystemMove() override
Reimplement this method to start a system move operation if the system supports it and return true to...
static const int NoAlertRequest
QRect m_exposedRect
bool windowEvent(QEvent *event) override
Reimplement this method to be able to do any platform specific event handling.
void raise() override
Reimplement to be able to let Qt raise windows to the top of the desktop.
void windowWillZoom()
bool setKeyboardGrabEnabled(bool grab) override
bool windowIsPopupType(Qt::WindowType type=Qt::Widget) const
void registerContentBorderArea(quintptr identifier, int upper, int lower)
void setEmbeddedInForeignView()
void applyWindowState(Qt::WindowStates newState)
QPoint bottomLeftClippedByNSWindowOffset() const override
bool isTransitioningToFullScreen() const
QHash< quintptr, BorderRange > m_contentBorderAreas
Qt::WindowStates windowState() const
qreal devicePixelRatio() const override
Reimplement this function in subclass to return the device pixel ratio for the window.
void initialize() override
Called as part of QWindow::create(), after constructing the window.
Qt::WindowStates m_lastReportedWindowState
QSurfaceFormat format() const override
Returns the actual surface format of the window.
static id s_applicationActivationObserver
NSView * m_view
void handleExposeEvent(const QRegion &region)
\inmodule QtCore
\inmodule QtCore
@ ExcludeSocketNotifiers
Definition qeventloop.h:28
@ ExcludeUserInputEvents
Definition qeventloop.h:27
\inmodule QtCore
Definition qcoreevent.h:45
@ WindowBlocked
Definition qcoreevent.h:141
@ WindowUnblocked
Definition qcoreevent.h:142
@ MouseMove
Definition qcoreevent.h:63
static QGuiApplicationPrivate * instance()
static QWindow * currentMouseWindow
static Qt::ApplicationState applicationState()
static QWindow * topLevelAt(const QPoint &pos)
Returns the top level window at the given position pos, if any.
static QWindowList allWindows()
Returns a list of all the windows in the application.
QScreen * primaryScreen
the primary (or default) screen of the application.
static QList< QScreen * > screens()
Returns a list of all the screens associated with the windowing system the application is connected t...
T value(const Key &key) const noexcept
Definition qhash.h:1054
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
The QIcon class provides scalable icons in different modes and states.
Definition qicon.h:20
bool isNull() const
Returns true if the icon is empty; otherwise returns false.
Definition qicon.cpp:1019
\inmodule QtCore
Definition qmargins.h:24
constexpr int left() const noexcept
Returns the left margin.
Definition qmargins.h:106
constexpr int top() const noexcept
Returns the top margin.
Definition qmargins.h:109
\inmodule QtCore
Definition qmetaobject.h:19
\inmodule QtCore
Definition qmetatype.h:341
constexpr const QMetaObject * metaObject() const
Definition qmetatype.h:2663
static QObjectPrivate * get(QObject *o)
Definition qobject_p.h:150
\inmodule QtCore
Definition qobject.h:103
const QObjectList & children() const
Returns a list of child objects.
Definition qobject.h:201
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:346
QObject * sender() const
Returns a pointer to the object that sent the signal, if called in a slot activated by a signal; othe...
Definition qobject.cpp:2658
QVariant property(const char *name) const
Returns the value of the object's name property.
Definition qobject.cpp:4323
virtual QRect geometry() const =0
Reimplement in subclass to return the pixel geometry of the screen.
QSurface * surface() const
The QPlatformWindow class provides an abstraction for top-level windows.
QScopedPointer< QPlatformWindowPrivate > d_ptr
QWindow * window() const
Returns the window which belongs to the QPlatformWindow.
QPlatformScreen * screen() const override
Returns the platform screen handle corresponding to this platform window, or null if the window is no...
QSize windowMinimumSize() const
Returns the QWindow minimum size.
virtual bool windowEvent(QEvent *event)
Reimplement this method to be able to do any platform specific event handling.
QPlatformWindow * parent() const
Returns the parent platform window (or \nullptr if orphan).
virtual void setGeometry(const QRect &rect)
This function is called by Qt whenever a window is moved or resized using the QWindow API.
bool hasPendingUpdateRequest() const
Returns true if the window has a pending update request.
virtual QRect geometry() const
Returns the current geometry of a window.
virtual void deliverUpdateRequest()
Delivers an QEvent::UpdateRequest event to the window.
virtual void requestUpdate()
Requests an QEvent::UpdateRequest event.
QRect windowGeometry() const
Returns the QWindow geometry.
QSize windowBaseSize() const
Returns the QWindow base size.
QSize windowMaximumSize() const
Returns the QWindow maximum size.
static QRect initialGeometry(const QWindow *w, const QRect &initialGeometry, int defaultWidth, int defaultHeight, const QScreen **resultingScreenReturn=nullptr)
Helper function to get initial geometry on windowing systems which do not do smart positioning and al...
QSize windowSizeIncrement() const
Returns the QWindow size increment.
\inmodule QtCore\reentrant
Definition qpoint.h:217
\inmodule QtCore\reentrant
Definition qpoint.h:25
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr bool isEmpty() const noexcept
Returns true if the rectangle is empty, otherwise returns false.
Definition qrect.h:167
The QRegion class specifies a clip region for a painter.
Definition qregion.h:27
QRect boundingRect() const noexcept
Returns the bounding rectangle of this region.
bool isEmpty() const
Returns true if the region is empty; otherwise returns false.
The QScreen class is used to query screen properties. \inmodule QtGui.
Definition qscreen.h:32
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
constexpr bool isNull() const noexcept
Returns true if both the width and height is 0; otherwise returns false.
Definition qsize.h:121
constexpr bool isEmpty() const noexcept
Returns true if either of the width and height is less than or equal to 0; otherwise returns false.
Definition qsize.h:124
constexpr bool isValid() const noexcept
Returns true if both the width and height is equal to or greater than 0; otherwise returns false.
Definition qsize.h:127
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
QString mid(qsizetype position, qsizetype n=-1) const &
Definition qstring.cpp:5300
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
The QSurfaceFormat class represents the format of a QSurface. \inmodule QtGui.
int alphaBufferSize() const
Get the size in bits of the alpha channel of the color buffer.
int swapInterval() const
Returns the swap interval.
bool supportsOpenGL() const
Returns true if the surface is OpenGL compatible and can be used in conjunction with QOpenGLContext; ...
Definition qsurface.cpp:70
\inmodule QtCore
Definition qvariant.h:65
bool isValid() const
Returns true if the storage type of this variant is not QMetaType::UnknownType; otherwise returns fal...
Definition qvariant.h:714
bool toBool() const
Returns the variant as a bool if the variant has userType() Bool.
static QWindowPrivate * get(QWindow *window)
Definition qwindow_p.h:106
static Qt::WindowState effectiveState(Qt::WindowStates)
Definition qwindow.cpp:1419
static bool flushWindowSystemEvents(QEventLoop::ProcessEventsFlags flags=QEventLoop::AllEvents)
Make Qt Gui process all events on the event queue immediately.
static void handleWindowScreenChanged(QWindow *window, QScreen *newScreen)
static bool handleMouseEvent(QWindow *window, const QPointF &local, const QPointF &global, Qt::MouseButtons state, Qt::MouseButton button, QEvent::Type type, Qt::KeyboardModifiers mods=Qt::NoModifier, Qt::MouseEventSource source=Qt::MouseEventNotSynthesized)
static void handleGeometryChange(QWindow *window, const QRect &newRect)
\inmodule QtGui
Definition qwindow.h:63
Qt::WindowFlags flags
the window flags of the window
Definition qwindow.h:79
QSurfaceFormat format() const override
Returns the actual format of this window.
Definition qwindow.cpp:946
QString title
the window's title in the windowing system
Definition qwindow.h:77
Qt::WindowModality modality
the modality of the window
Definition qwindow.h:78
qreal opacity
The opacity of the window in the windowing system.
Definition qwindow.h:96
QPushButton * button
[2]
QCursor cursor
rect
[4]
else opt state
[0]
void newState(QList< State > &states, const char *token, const char *lexem, bool pre)
struct wl_display * display
Definition linuxdmabuf.h:41
QRegion toNativeLocalRegion(const QRegion &pointRegion, const QWindow *window)
Combined button and popup list for selecting options.
WindowState
Definition qnamespace.h:251
@ WindowFullScreen
Definition qnamespace.h:255
@ WindowNoState
Definition qnamespace.h:252
@ WindowMinimized
Definition qnamespace.h:253
@ WindowMaximized
Definition qnamespace.h:254
@ WindowModal
@ ApplicationModal
@ PortraitOrientation
Definition qnamespace.h:273
@ ApplicationActive
Definition qnamespace.h:266
@ DirectConnection
WindowType
Definition qnamespace.h:205
@ CustomizeWindowHint
Definition qnamespace.h:239
@ Widget
Definition qnamespace.h:206
@ FramelessWindowHint
Definition qnamespace.h:225
@ WindowDoesNotAcceptFocus
Definition qnamespace.h:236
@ ToolTip
Definition qnamespace.h:213
@ Popup
Definition qnamespace.h:211
@ WindowType_Mask
Definition qnamespace.h:220
@ Window
Definition qnamespace.h:207
@ WindowFullscreenButtonHint
Definition qnamespace.h:245
@ WindowStaysOnTopHint
Definition qnamespace.h:233
@ WindowMaximizeButtonHint
Definition qnamespace.h:229
@ WindowMinimizeButtonHint
Definition qnamespace.h:228
@ Dialog
Definition qnamespace.h:208
@ NoDropShadowWindowHint
Definition qnamespace.h:244
@ Sheet
Definition qnamespace.h:209
@ WindowTransparentForInput
Definition qnamespace.h:234
@ SubWindow
Definition qnamespace.h:216
@ Tool
Definition qnamespace.h:212
@ WindowTitleHint
Definition qnamespace.h:226
@ WindowCloseButtonHint
Definition qnamespace.h:241
@ ActiveWindowFocusReason
static void * context
float CGFloat
QNSView * qnsview_cast(NSView *view)
Returns the view cast to a QNSview if possible.
QEvent::Type cocoaEvent2QtMouseEvent(NSEvent *event)
Returns the QEvent::Type that corresponds to an NSEvent.type.
Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum)
Returns the Qt::Button that corresponds to an NSEvent.buttonNumber.
Qt::MouseButtons currentlyPressedMouseButtons()
Returns the Qt::MouseButtons that corresponds to an NSEvent.pressedMouseButtons.
unsigned long NSUInteger
#define Q_NOTIFICATION_PREFIX
long NSInteger
const NSNotificationName QCocoaWindowWillReleaseQNSViewNotification
static void qRegisterNotificationCallbacks()
@ defaultWindowHeight
@ defaultWindowWidth
QDebug operator<<(QDebug debug, const QCocoaWindow *window)
const NSNotificationName QCocoaWindowWillReleaseQNSViewNotification
Q_CONSTRUCTOR_FUNCTION(qt_apple_check_os_version)
#define qApp
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char * method
EGLOutputLayerEXT layer
#define qGuiApp
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCInfo(category,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLint location
typedef GLint(GL_APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONINDEXEXTPROC)(GLuint program
GLuint64 GLenum void * handle
GLenum GLuint GLint level
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLboolean r
[2]
GLuint object
[3]
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLsizei range
GLenum type
GLbitfield flags
GLboolean enable
GLuint name
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
struct _cl_event * event
GLuint * states
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
#define qPrintable(string)
Definition qstring.h:1531
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
size_t quintptr
Definition qtypes.h:167
double qreal
Definition qtypes.h:187
Q_GUI_EXPORT QWindowPrivate * qt_window_private(QWindow *window)
Definition qwindow.cpp:2950
#define enabled
QWidget * win
Definition settings.cpp:6
QFileSelector selector
[1]
obj metaObject() -> className()
QObject::connect nullptr
QString title
[35]
sem release()
QQueue< int > queue
[0]
edit isVisible()
QLayoutItem * child
[0]
aWidget window() -> setWindowTitle("New Window Title")
[2]
QFrame frame
[0]
\inmodule QtCore