Cocoa: NSMenu views never get viewDidUnhide called

This is the case for QWidgets added as native menu items with
QWidgetAction. According to Cocoa's documentation [1], we should
rely on -[QNSView viewDidMoveToWindow] instead.

On 10.9 however, we receive NSWindowDidChangeOcclusionStateNotification
from the NSMenu window, which is preferable to using -[QNSView
viewDidMoveToWindow] as it guarantees the view is actually visible.
We do runtime symbol lookup to get this to work on 10.9 regardless
of the build SDK version.

[1] https://developer.apple.com/library/mac/documentation/cocoa/Conceptual/MenuList/Articles/ViewsInMenuItems.html

Task-number: QTBUG-19840
Change-Id: If4676df5d79c359965f09ef2e5eddf4c925e3533
Reviewed-by: Morten Johan Sørvig <morten.sorvig@digia.com>
This commit is contained in:
Gabriel de Dietrich 2014-04-01 18:18:17 +02:00 committed by The Qt Project
parent 454dc332b3
commit c7bd85e97d

View File

@ -42,6 +42,7 @@
#include <QtCore/qglobal.h>
#include <Carbon/Carbon.h>
#include <dlfcn.h>
#include "qnsview.h"
#include "qcocoawindow.h"
@ -65,6 +66,9 @@
static QTouchDevice *touchDevice = 0;
// ### HACK Remove once 10.8 is unsupported
static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil;
@interface NSEvent (Qt_Compile_Leopard_DeviceDelta)
- (CGFloat)deviceDeltaX;
- (CGFloat)deviceDeltaY;
@ -73,6 +77,13 @@ static QTouchDevice *touchDevice = 0;
@implementation QNSView
+ (void)initialize
{
NSString **notificationNameVar = (NSString **)dlsym(RTLD_NEXT, "NSWindowDidChangeOcclusionStateNotification");
if (notificationNameVar)
_q_NSWindowDidChangeOcclusionStateNotification = *notificationNameVar;
}
- (id) init
{
self = [super initWithFrame : NSMakeRect(0,0, 300,300)];
@ -192,6 +203,19 @@ static QTouchDevice *touchDevice = 0;
}
}
- (void)viewDidMoveToWindow
{
if (self.window) {
// This is the case of QWidgetAction's generated QWidget inserted in an NSMenu.
// 10.9 and newer get the NSWindowDidChangeOcclusionStateNotification
if (!_q_NSWindowDidChangeOcclusionStateNotification
&& [self.window.className isEqualToString:@"NSCarbonMenuWindow"])
m_platformWindow->exposeWindow();
} else {
m_platformWindow->obscureWindow();
}
}
- (void)viewWillMoveToWindow:(NSWindow *)newWindow
{
// ### Merge "normal" window code path with this one for 5.1.
@ -325,6 +349,23 @@ static QTouchDevice *touchDevice = 0;
m_platformWindow->obscureWindow();
} else if ([notificationName isEqualToString: @"NSWindowDidOrderOnScreenAndFinishAnimatingNotification"]) {
m_platformWindow->exposeWindow();
} else if (_q_NSWindowDidChangeOcclusionStateNotification
&& [notificationName isEqualToString:_q_NSWindowDidChangeOcclusionStateNotification]) {
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9
// ### HACK Remove the enum declaration, the warning disabling and the cast further down once 10.8 is unsupported
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-method-access"
enum { NSWindowOcclusionStateVisible = 1UL << 1 };
#endif
// Older versions managed in -[QNSView viewDidMoveToWindow].
// Support QWidgetAction in NSMenu. Mavericks only sends this notification.
// Ideally we should support this in Qt as well, in order to disable animations
// when the window is occluded.
if ((NSUInteger)[self.window occlusionState] & NSWindowOcclusionStateVisible)
m_platformWindow->exposeWindow();
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9
#pragma clang diagnostic pop
#endif
} else if (notificationName == NSWindowDidChangeScreenNotification) {
if (m_window) {
NSUInteger screenIndex = [[NSScreen screens] indexOfObject:self.window.screen];