summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/cocoa
diff options
context:
space:
mode:
authorLiang Qi <liang.qi@qt.io>2016-11-17 14:15:53 +0100
committerLiang Qi <liang.qi@qt.io>2016-11-17 14:43:26 +0100
commite5ac4afbf954a3e1616ce8543d46ddc668d0374f (patch)
treebe6d97001edebd5cb74c64aaf0010f3cc76a7293 /src/plugins/platforms/cocoa
parente3ed95dd44b95b6e9361b562807e711d7ce5a58b (diff)
parent03c1a6ac717e3c5693653a5e294214056bda970e (diff)
Merge remote-tracking branch 'origin/5.8' into dev
Conflicts: mkspecs/features/mac/default_post.prf mkspecs/features/uikit/default_post.prf Change-Id: I2a6f783451f2ac9eb4c1a050f605435d2dacf218
Diffstat (limited to 'src/plugins/platforms/cocoa')
-rw-r--r--src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm124
-rw-r--r--src/plugins/platforms/cocoa/qcocoacursor.mm9
-rw-r--r--src/plugins/platforms/cocoa/qcocoaeventdispatcher.h2
-rw-r--r--src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm13
-rw-r--r--src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm5
-rw-r--r--src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm131
-rw-r--r--src/plugins/platforms/cocoa/qcocoahelpers.h23
-rw-r--r--src/plugins/platforms/cocoa/qcocoahelpers.mm114
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenu.mm17
-rw-r--r--src/plugins/platforms/cocoa/qcocoanativeinterface.h2
-rw-r--r--src/plugins/platforms/cocoa/qcocoanativeinterface.mm6
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.h2
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.mm60
-rw-r--r--src/plugins/platforms/cocoa/qnsview.h1
-rw-r--r--src/plugins/platforms/cocoa/qnsview.mm23
15 files changed, 268 insertions, 264 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm
index 3a5a0c8e78..e53c085e41 100644
--- a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm
+++ b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm
@@ -45,36 +45,19 @@
#include "qcocoacolordialoghelper.h"
#include "qcocoahelpers.h"
+#include "qcocoaeventdispatcher.h"
#import <AppKit/AppKit.h>
QT_USE_NAMESPACE
-static NSButton *macCreateButton(const char *text, NSView *superview)
-{
- static const NSRect buttonFrameRect = { { 0.0, 0.0 }, { 0.0, 0.0 } };
-
- NSButton *button = [[NSButton alloc] initWithFrame:buttonFrameRect];
- [button setButtonType:NSMomentaryLightButton];
- [button setBezelStyle:NSRoundedBezelStyle];
- [button setTitle:(NSString*)(CFStringRef)QCFString(
- QPlatformTheme::removeMnemonics(QCoreApplication::translate("QPlatformTheme", text)))];
- [[button cell] setFont:[NSFont systemFontOfSize:
- [NSFont systemFontSizeForControlSize:NSRegularControlSize]]];
- [superview addSubview:button];
- return button;
-}
-
-@class QT_MANGLE_NAMESPACE(QNSColorPanelDelegate);
-
-@interface QT_MANGLE_NAMESPACE(QNSColorPanelDelegate) : NSObject<NSWindowDelegate>
+@interface QT_MANGLE_NAMESPACE(QNSColorPanelDelegate) : NSObject<NSWindowDelegate, QT_MANGLE_NAMESPACE(QNSPanelDelegate)>
{
@public
NSColorPanel *mColorPanel;
QCocoaColorDialogHelper *mHelper;
NSView *mStolenContentView;
- NSButton *mOkButton;
- NSButton *mCancelButton;
+ QNSPanelContentsWrapper *mPanelButtons;
QColor mQtColor;
NSInteger mResultCode;
BOOL mDialogIsExecuting;
@@ -82,7 +65,6 @@ static NSButton *macCreateButton(const char *text, NSView *superview)
BOOL mClosingDueToKnownButton;
};
- (void)restoreOriginalContentView;
-- (void)relayout;
- (void)updateQtColor;
- (void)finishOffWithCode:(NSInteger)code;
@end
@@ -97,8 +79,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate);
mColorPanel = [NSColorPanel sharedColorPanel];
mHelper = 0;
mStolenContentView = 0;
- mOkButton = 0;
- mCancelButton = 0;
+ mPanelButtons = nil;
mResultCode = NSCancelButton;
mDialogIsExecuting = false;
mResultSet = false;
@@ -142,23 +123,10 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate);
[mColorPanel setContentView:0];
// create a new content view and add the stolen one as a subview
- NSRect frameRect = { { 0.0, 0.0 }, { 0.0, 0.0 } };
- NSView *ourContentView = [[NSView alloc] initWithFrame:frameRect];
- [ourContentView addSubview:mStolenContentView];
-
- // create OK and Cancel buttons and add these as subviews
- mOkButton = macCreateButton("&OK", ourContentView);
- mCancelButton = macCreateButton("Cancel", ourContentView);
-
- [mColorPanel setContentView:ourContentView];
- [mColorPanel setDefaultButtonCell:[mOkButton cell]];
- [self relayout];
-
- [mOkButton setAction:@selector(onOkClicked)];
- [mOkButton setTarget:self];
-
- [mCancelButton setAction:@selector(onCancelClicked)];
- [mCancelButton setTarget:self];
+ mPanelButtons = [[QNSPanelContentsWrapper alloc] initWithPanelDelegate:self];
+ [mPanelButtons addSubview:mStolenContentView];
+ mColorPanel.contentView = mPanelButtons;
+ mColorPanel.defaultButtonCell = mPanelButtons.okButton.cell;
}
}
@@ -167,12 +135,6 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate);
[mColorPanel close];
}
-- (void)windowDidResize:(NSNotification *)notification
-{
- Q_UNUSED(notification);
- [self relayout];
-}
-
- (void)colorChanged:(NSNotification *)notification
{
Q_UNUSED(notification);
@@ -182,7 +144,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate);
- (void)windowWillClose:(NSNotification *)notification
{
Q_UNUSED(notification);
- if (mCancelButton && mHelper && !mClosingDueToKnownButton) {
+ if (mPanelButtons && mHelper && !mClosingDueToKnownButton) {
mClosingDueToKnownButton = true; // prevent repeating emit
emit mHelper->reject();
}
@@ -191,68 +153,16 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate);
- (void)restoreOriginalContentView
{
if (mStolenContentView) {
- NSView *ourContentView = [mColorPanel contentView];
-
// return stolen stuff to its rightful owner
[mStolenContentView removeFromSuperview];
[mColorPanel setContentView:mStolenContentView];
- [mOkButton release];
- [mCancelButton release];
- [ourContentView release];
- mOkButton = 0;
- mCancelButton = 0;
- mStolenContentView = 0;
+ [mStolenContentView release];
+ mStolenContentView = nil;
+ [mPanelButtons release];
+ mPanelButtons = nil;
}
}
-- (void)relayout
-{
- if (!mOkButton)
- return;
-
- NSRect rect = [[mStolenContentView superview] frame];
-
- // should a priori be kept in sync with qfontdialog_mac.mm
- const CGFloat ButtonMinWidth = 78.0; // 84.0 for Carbon
- const CGFloat ButtonMinHeight = 32.0;
- const CGFloat ButtonSpacing = 0.0;
- const CGFloat ButtonTopMargin = 0.0;
- const CGFloat ButtonBottomMargin = 7.0;
- const CGFloat ButtonSideMargin = 9.0;
-
- [mOkButton sizeToFit];
- NSSize okSizeHint = [mOkButton frame].size;
-
- [mCancelButton sizeToFit];
- NSSize cancelSizeHint = [mCancelButton frame].size;
-
- const CGFloat ButtonWidth = qMin(qMax(ButtonMinWidth,
- qMax(okSizeHint.width, cancelSizeHint.width)),
- CGFloat((rect.size.width - 2.0 * ButtonSideMargin - ButtonSpacing) * 0.5));
- const CGFloat ButtonHeight = qMax(ButtonMinHeight,
- qMax(okSizeHint.height, cancelSizeHint.height));
-
- NSRect okRect = { { rect.size.width - ButtonSideMargin - ButtonWidth,
- ButtonBottomMargin },
- { ButtonWidth, ButtonHeight } };
- [mOkButton setFrame:okRect];
- [mOkButton setNeedsDisplay:YES];
-
- NSRect cancelRect = { { okRect.origin.x - ButtonSpacing - ButtonWidth,
- ButtonBottomMargin },
- { ButtonWidth, ButtonHeight } };
- [mCancelButton setFrame:cancelRect];
- [mCancelButton setNeedsDisplay:YES];
-
- const CGFloat Y = ButtonBottomMargin + ButtonHeight + ButtonTopMargin;
- NSRect stolenCVRect = { { 0.0, Y },
- { rect.size.width, rect.size.height - Y } };
- [mStolenContentView setFrame:stolenCVRect];
- [mStolenContentView setNeedsDisplay:YES];
-
- [[mStolenContentView superview] setNeedsDisplay:YES];
-}
-
- (void)onOkClicked
{
mClosingDueToKnownButton = true;
@@ -263,7 +173,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate);
- (void)onCancelClicked
{
- if (mOkButton) {
+ if (mPanelButtons) {
mClosingDueToKnownButton = true;
[mColorPanel close];
mQtColor = QColor();
@@ -322,6 +232,10 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate);
// cleanup of modal sessions. Do this before showing the native dialog, otherwise it will
// close down during the cleanup.
qApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
+
+ // Make sure we don't interrupt the runModalForWindow call.
+ QCocoaEventDispatcher::clearCurrentThreadCocoaEventDispatcherInterruptFlag();
+
[NSApp runModalForWindow:mColorPanel];
mDialogIsExecuting = false;
return (mResultCode == NSOKButton);
@@ -335,7 +249,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate);
- (BOOL)windowShouldClose:(id)window
{
Q_UNUSED(window);
- if (!mOkButton)
+ if (!mPanelButtons)
[self updateQtColor];
if (mDialogIsExecuting) {
[self finishOffWithCode:NSCancelButton];
diff --git a/src/plugins/platforms/cocoa/qcocoacursor.mm b/src/plugins/platforms/cocoa/qcocoacursor.mm
index 8e523dfbbf..3df2a7c962 100644
--- a/src/plugins/platforms/cocoa/qcocoacursor.mm
+++ b/src/plugins/platforms/cocoa/qcocoacursor.mm
@@ -62,7 +62,7 @@ QCocoaCursor::~QCocoaCursor()
void QCocoaCursor::changeCursor(QCursor *cursor, QWindow *window)
{
- NSCursor * cocoaCursor = convertCursor(cursor);
+ NSCursor *cocoaCursor = convertCursor(cursor);
if (QPlatformWindow * platformWindow = window->handle())
static_cast<QCocoaWindow *>(platformWindow)->setWindowCursor(cocoaCursor);
@@ -84,9 +84,12 @@ void QCocoaCursor::setPos(const QPoint &position)
CFRelease(e);
}
-NSCursor *QCocoaCursor::convertCursor(QCursor * cursor)
+NSCursor *QCocoaCursor::convertCursor(QCursor *cursor)
{
- const Qt::CursorShape newShape = cursor ? cursor->shape() : Qt::ArrowCursor;
+ if (cursor == Q_NULLPTR)
+ return 0;
+
+ const Qt::CursorShape newShape = cursor->shape();
NSCursor *cocoaCursor;
// Check for a suitable built-in NSCursor first:
diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h
index 84880d51c5..70887c41c9 100644
--- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h
+++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h
@@ -131,6 +131,8 @@ public:
void interrupt();
void flush();
+ static void clearCurrentThreadCocoaEventDispatcherInterruptFlag();
+
friend void qt_mac_maybeCancelWaitForMoreEventsForwarder(QAbstractEventDispatcher *eventDispatcher);
};
diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
index b2b9fe2c1e..72c7856c2d 100644
--- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
+++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
@@ -965,6 +965,19 @@ void QCocoaEventDispatcher::interrupt()
void QCocoaEventDispatcher::flush()
{ }
+// QTBUG-56746: The behavior of processEvents() has been changed to not clear
+// the interrupt flag. Use this function to clear it.
+ void QCocoaEventDispatcher::clearCurrentThreadCocoaEventDispatcherInterruptFlag()
+{
+ QCocoaEventDispatcher *cocoaEventDispatcher =
+ qobject_cast<QCocoaEventDispatcher *>(QThread::currentThread()->eventDispatcher());
+ if (!cocoaEventDispatcher)
+ return;
+ QCocoaEventDispatcherPrivate *cocoaEventDispatcherPrivate =
+ static_cast<QCocoaEventDispatcherPrivate *>(QObjectPrivate::get(cocoaEventDispatcher));
+ cocoaEventDispatcherPrivate->interrupt = false;
+}
+
QCocoaEventDispatcher::~QCocoaEventDispatcher()
{
Q_D(QCocoaEventDispatcher);
diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm
index 7706f39198..41a809cdd2 100644
--- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm
+++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm
@@ -53,6 +53,7 @@
#include "qt_mac_p.h"
#include "qcocoahelpers.h"
#include "qcocoamenubar.h"
+#include "qcocoaeventdispatcher.h"
#include <qregexp.h>
#include <qbuffer.h>
#include <qdebug.h>
@@ -251,6 +252,10 @@ static QString strippedText(QString s)
// cleanup of modal sessions. Do this before showing the native dialog, otherwise it will
// close down during the cleanup.
qApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
+
+ // Make sure we don't interrupt the runModal call below.
+ QCocoaEventDispatcher::clearCurrentThreadCocoaEventDispatcherInterruptFlag();
+
QCocoaMenuBar::redirectKnownMenuItemsToFirstResponder();
mReturnCode = [mSavePanel runModal];
QCocoaMenuBar::resetKnownMenuItemsToQt();
diff --git a/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm
index 0be931b54f..33dd4260a5 100644
--- a/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm
+++ b/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm
@@ -49,6 +49,7 @@
#include "qcocoafontdialoghelper.h"
#include "qcocoahelpers.h"
+#include "qcocoaeventdispatcher.h"
#import <AppKit/AppKit.h>
@@ -58,33 +59,6 @@ typedef float CGFloat; // Should only not be defined on 32-bit platforms
QT_USE_NAMESPACE
-// should a priori be kept in sync with qcolordialog_mac.mm
-const CGFloat ButtonMinWidth = 78.0;
-const CGFloat ButtonMinHeight = 32.0;
-const CGFloat ButtonSpacing = 0.0;
-const CGFloat ButtonTopMargin = 0.0;
-const CGFloat ButtonBottomMargin = 7.0;
-const CGFloat ButtonSideMargin = 9.0;
-
-// looks better with some margins
-const CGFloat DialogTopMargin = 7.0;
-const CGFloat DialogSideMargin = 9.0;
-
-static NSButton *macCreateButton(const char *text, NSView *superview)
-{
- static const NSRect buttonFrameRect = { { 0.0, 0.0 }, { 0.0, 0.0 } };
-
- NSButton *button = [[NSButton alloc] initWithFrame:buttonFrameRect];
- [button setButtonType:NSMomentaryLightButton];
- [button setBezelStyle:NSRoundedBezelStyle];
- [button setTitle:(NSString*)(CFStringRef)QCFString(
- QPlatformTheme::removeMnemonics(QCoreApplication::translate("QDialogButtonBox", text)))];
- [[button cell] setFont:[NSFont systemFontOfSize:
- [NSFont systemFontSizeForControlSize:NSRegularControlSize]]];
- [superview addSubview:button];
- return button;
-}
-
static QFont qfontForCocoaFont(NSFont *cocoaFont, const QFont &resolveFont)
{
QFont newFont;
@@ -103,22 +77,19 @@ static QFont qfontForCocoaFont(NSFont *cocoaFont, const QFont &resolveFont)
@class QT_MANGLE_NAMESPACE(QNSFontPanelDelegate);
-@interface QT_MANGLE_NAMESPACE(QNSFontPanelDelegate) : NSObject<NSWindowDelegate>
+@interface QT_MANGLE_NAMESPACE(QNSFontPanelDelegate) : NSObject<NSWindowDelegate, QT_MANGLE_NAMESPACE(QNSPanelDelegate)>
{
@public
NSFontPanel *mFontPanel;
QCocoaFontDialogHelper *mHelper;
NSView *mStolenContentView;
- NSButton *mOkButton;
- NSButton *mCancelButton;
+ QNSPanelContentsWrapper *mPanelButtons;
QFont mQtFont;
NSInteger mResultCode;
BOOL mDialogIsExecuting;
BOOL mResultSet;
};
- (void)restoreOriginalContentView;
-- (void)relayout;
-- (void)relayoutToContentSize:(NSSize)frameSize;
- (void)updateQtFont;
- (void)changeFont:(id)sender;
- (void)finishOffWithCode:(NSInteger)code;
@@ -134,8 +105,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSFontPanelDelegate);
mFontPanel = [NSFontPanel sharedFontPanel];
mHelper = 0;
mStolenContentView = 0;
- mOkButton = 0;
- mCancelButton = 0;
+ mPanelButtons = 0;
mResultCode = NSCancelButton;
mDialogIsExecuting = false;
mResultSet = false;
@@ -173,23 +143,11 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSFontPanelDelegate);
[mFontPanel setContentView:0];
// create a new content view and add the stolen one as a subview
- NSRect frameRect = { { 0.0, 0.0 }, { 0.0, 0.0 } };
- NSView *ourContentView = [[NSView alloc] initWithFrame:frameRect];
- [ourContentView addSubview:mStolenContentView];
-
- // create OK and Cancel buttons and add these as subviews
- mOkButton = macCreateButton("&OK", ourContentView);
- mCancelButton = macCreateButton("Cancel", ourContentView);
-
- [mFontPanel setContentView:ourContentView];
- [mFontPanel setDefaultButtonCell:[mOkButton cell]];
- [self relayoutToContentSize:[[mStolenContentView superview] frame].size];
-
- [mOkButton setAction:@selector(onOkClicked)];
- [mOkButton setTarget:self];
-
- [mCancelButton setAction:@selector(onCancelClicked)];
- [mCancelButton setTarget:self];
+ mPanelButtons = [[QNSPanelContentsWrapper alloc] initWithPanelDelegate:self];
+ [mPanelButtons addSubview:mStolenContentView];
+ mPanelButtons.panelContentsMargins = NSEdgeInsetsMake(0, 0, 7, 0);
+ mFontPanel.contentView = mPanelButtons;
+ mFontPanel.defaultButtonCell = mPanelButtons.okButton.cell;
}
}
@@ -198,77 +156,18 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSFontPanelDelegate);
[mFontPanel close];
}
-- (void)windowDidResize:(NSNotification *)notification
-{
- Q_UNUSED(notification);
- [self relayout];
-}
-
- (void)restoreOriginalContentView
{
if (mStolenContentView) {
- NSView *ourContentView = [mFontPanel contentView];
-
// return stolen stuff to its rightful owner
[mStolenContentView removeFromSuperview];
[mFontPanel setContentView:mStolenContentView];
- [mOkButton release];
- [mCancelButton release];
- [ourContentView release];
- mOkButton = 0;
- mCancelButton = 0;
mStolenContentView = 0;
+ [mPanelButtons release];
+ mPanelButtons = nil;
}
}
-- (void)relayout
-{
- if (!mOkButton)
- return;
-
- [self relayoutToContentSize:[[mStolenContentView superview] frame].size];
-}
-
-- (void)relayoutToContentSize:(NSSize)frameSize
-{
- Q_ASSERT(mOkButton);
-
- [mOkButton sizeToFit];
- NSSize okSizeHint = [mOkButton frame].size;
-
- [mCancelButton sizeToFit];
- NSSize cancelSizeHint = [mCancelButton frame].size;
-
- const CGFloat ButtonWidth = qMin(qMax(ButtonMinWidth,
- qMax(okSizeHint.width, cancelSizeHint.width)),
- CGFloat((frameSize.width - 2.0 * ButtonSideMargin - ButtonSpacing) * 0.5));
- const CGFloat ButtonHeight = qMax(ButtonMinHeight,
- qMax(okSizeHint.height, cancelSizeHint.height));
-
- const CGFloat X = DialogSideMargin;
- const CGFloat Y = ButtonBottomMargin + ButtonHeight + ButtonTopMargin;
-
- NSRect okRect = { { frameSize.width - ButtonSideMargin - ButtonWidth,
- ButtonBottomMargin },
- { ButtonWidth, ButtonHeight } };
- [mOkButton setFrame:okRect];
- [mOkButton setNeedsDisplay:YES];
-
- NSRect cancelRect = { { okRect.origin.x - ButtonSpacing - ButtonWidth,
- ButtonBottomMargin },
- { ButtonWidth, ButtonHeight } };
- [mCancelButton setFrame:cancelRect];
- [mCancelButton setNeedsDisplay:YES];
-
- NSRect stolenCVRect = { { X, Y },
- { frameSize.width - X - X, frameSize.height - Y - DialogTopMargin } };
- [mStolenContentView setFrame:stolenCVRect];
- [mStolenContentView setNeedsDisplay:YES];
-
- [[mStolenContentView superview] setNeedsDisplay:YES];
-}
-
-
- (void)onOkClicked
{
[mFontPanel close];
@@ -277,7 +176,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSFontPanelDelegate);
- (void)onCancelClicked
{
- if (mOkButton) {
+ if (mPanelButtons) {
[mFontPanel close];
mQtFont = QFont();
[self finishOffWithCode:NSCancelButton];
@@ -319,6 +218,10 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSFontPanelDelegate);
// cleanup of modal sessions. Do this before showing the native dialog, otherwise it will
// close down during the cleanup.
qApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
+
+ // Make sure we don't interrupt the runModalForWindow call.
+ QCocoaEventDispatcher::clearCurrentThreadCocoaEventDispatcherInterruptFlag();
+
[NSApp runModalForWindow:mFontPanel];
mDialogIsExecuting = false;
return (mResultCode == NSOKButton);
@@ -332,7 +235,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSFontPanelDelegate);
- (BOOL)windowShouldClose:(id)window
{
Q_UNUSED(window);
- if (!mOkButton)
+ if (!mPanelButtons)
[self updateQtFont];
if (mDialogIsExecuting) {
[self finishOffWithCode:NSCancelButton];
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h
index 1b038a6b5e..a2e0876073 100644
--- a/src/plugins/platforms/cocoa/qcocoahelpers.h
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.h
@@ -158,7 +158,30 @@ T qt_mac_resolveOption(const T &fallback, QWindow *window, const QByteArray &pro
// return default value.
return fallback;
}
+
QT_END_NAMESPACE
+@protocol QT_MANGLE_NAMESPACE(QNSPanelDelegate)
+@required
+- (void)onOkClicked;
+- (void)onCancelClicked;
+@end
+
+@interface QT_MANGLE_NAMESPACE(QNSPanelContentsWrapper) : NSView
+
+@property (nonatomic, readonly) NSButton *okButton;
+@property (nonatomic, readonly) NSButton *cancelButton;
+@property (nonatomic, readonly) NSView *panelContents; // ARC: unretained, make it weak
+@property (nonatomic, assign) NSEdgeInsets panelContentsMargins;
+
+- (instancetype)initWithPanelDelegate:(id<QT_MANGLE_NAMESPACE(QNSPanelDelegate)>)panelDelegate;
+- (void)dealloc;
+
+- (NSButton *)createButtonWithTitle:(const char *)title;
+- (void)layout;
+@end
+
+QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSPanelContentsWrapper);
+
#endif //QCOCOAHELPERS_H
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm
index b092d013c5..fa0365dbf7 100644
--- a/src/plugins/platforms/cocoa/qcocoahelpers.mm
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm
@@ -298,3 +298,117 @@ QString qt_mac_removeAmpersandEscapes(QString s)
}
QT_END_NAMESPACE
+
+/*! \internal
+
+ This NSView derived class is used to add OK/Cancel
+ buttons to NSColorPanel and NSFontPanel. It replaces
+ the panel's content view, while reparenting the former
+ content view into itself. It also takes care of setting
+ the target-action for the OK/Cancel buttons and making
+ sure the layout is consistent.
+ */
+@implementation QNSPanelContentsWrapper
+
+- (instancetype)initWithPanelDelegate:(id<QT_MANGLE_NAMESPACE(QNSPanelDelegate)>)panelDelegate
+{
+ if ((self = [super initWithFrame:NSZeroRect])) {
+ // create OK and Cancel buttons and add these as subviews
+ _okButton = [self createButtonWithTitle:"&OK"];
+ _okButton.action = @selector(onOkClicked);
+ _okButton.target = panelDelegate;
+
+ _cancelButton = [self createButtonWithTitle:"Cancel"];
+ _cancelButton.action = @selector(onCancelClicked);
+ _cancelButton.target = panelDelegate;
+
+ _panelContents = nil;
+
+ _panelContentsMargins = NSEdgeInsetsMake(0, 0, 0, 0);
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [_okButton release];
+ _okButton = nil;
+ [_cancelButton release];
+ _cancelButton = nil;
+
+ _panelContents = nil;
+
+ [super dealloc];
+}
+
+- (NSButton *)createButtonWithTitle:(const char *)title
+{
+ NSButton *button = [[NSButton alloc] initWithFrame:NSZeroRect];
+ button.buttonType = NSMomentaryLightButton;
+ button.bezelStyle = NSRoundedBezelStyle;
+ const QString &cleanTitle = QPlatformTheme::removeMnemonics(QCoreApplication::translate("QDialogButtonBox", title));
+ // FIXME: Not obvious, from Cocoa's documentation, that QString::toNSString() makes a deep copy
+ button.title = (NSString *)cleanTitle.toCFString();
+ ((NSButtonCell *)button.cell).font =
+ [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]];
+ [self addSubview:button];
+ return button;
+}
+
+- (void)layout
+{
+ static const CGFloat ButtonMinWidth = 78.0; // 84.0 for Carbon
+ static const CGFloat ButtonMinHeight = 32.0;
+ static const CGFloat ButtonSpacing = 0.0;
+ static const CGFloat ButtonTopMargin = 0.0;
+ static const CGFloat ButtonBottomMargin = 7.0;
+ static const CGFloat ButtonSideMargin = 9.0;
+
+ NSSize frameSize = self.frame.size;
+
+ [self.okButton sizeToFit];
+ NSSize okSizeHint = self.okButton.frame.size;
+
+ [self.cancelButton sizeToFit];
+ NSSize cancelSizeHint = self.cancelButton.frame.size;
+
+ const CGFloat buttonWidth = qMin(qMax(ButtonMinWidth,
+ qMax(okSizeHint.width, cancelSizeHint.width)),
+ CGFloat((frameSize.width - 2.0 * ButtonSideMargin - ButtonSpacing) * 0.5));
+ const CGFloat buttonHeight = qMax(ButtonMinHeight,
+ qMax(okSizeHint.height, cancelSizeHint.height));
+
+ NSRect okRect = { { frameSize.width - ButtonSideMargin - buttonWidth,
+ ButtonBottomMargin },
+ { buttonWidth, buttonHeight } };
+ self.okButton.frame = okRect;
+ self.okButton.needsDisplay = YES;
+
+ NSRect cancelRect = { { okRect.origin.x - ButtonSpacing - buttonWidth,
+ ButtonBottomMargin },
+ { buttonWidth, buttonHeight } };
+ self.cancelButton.frame = cancelRect;
+ self.cancelButton.needsDisplay = YES;
+
+ // The third view should be the original panel contents. Cache it.
+ if (!self.panelContents)
+ for (NSView *view in self.subviews)
+ if (view != self.okButton && view != self.cancelButton) {
+ _panelContents = view;
+ break;
+ }
+
+ const CGFloat buttonBoxHeight = ButtonBottomMargin + buttonHeight + ButtonTopMargin;
+ const NSRect panelContentsFrame = NSMakeRect(
+ self.panelContentsMargins.left,
+ buttonBoxHeight + self.panelContentsMargins.bottom,
+ frameSize.width - (self.panelContentsMargins.left + self.panelContentsMargins.right),
+ frameSize.height - buttonBoxHeight - (self.panelContentsMargins.top + self.panelContentsMargins.bottom));
+ self.panelContents.frame = panelContentsFrame;
+ self.panelContents.needsDisplay = YES;
+
+ self.needsDisplay = YES;
+}
+
+@end
diff --git a/src/plugins/platforms/cocoa/qcocoamenu.mm b/src/plugins/platforms/cocoa/qcocoamenu.mm
index 566363e01f..88ffd48538 100644
--- a/src/plugins/platforms/cocoa/qcocoamenu.mm
+++ b/src/plugins/platforms/cocoa/qcocoamenu.mm
@@ -147,6 +147,10 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QCocoaMenuDelegate);
- (void) itemFired:(NSMenuItem*) item
{
QCocoaMenuItem *cocoaItem = reinterpret_cast<QCocoaMenuItem *>([item tag]);
+ // Menu-holding items also get a target to play nicely
+ // with NSMenuValidation but should not trigger.
+ if (cocoaItem->menu())
+ return;
QScopedScopeLevelCounter scopeLevelCounter(QGuiApplicationPrivate::instance()->threadData);
QGuiApplicationPrivate::modifier_buttons = [QNSView convertKeyModifiers:[NSEvent modifierFlags]];
static QMetaMethod activatedSignal = QMetaMethod::fromSignal(&QCocoaMenuItem::activated);
@@ -156,7 +160,8 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QCocoaMenuDelegate);
- (BOOL)validateMenuItem:(NSMenuItem*)menuItem
{
QCocoaMenuItem *cocoaItem = reinterpret_cast<QCocoaMenuItem *>(menuItem.tag);
- if (!cocoaItem)
+ // Menu-holding items are always enabled, as it's conventional in Cocoa
+ if (!cocoaItem || cocoaItem->menu())
return YES;
return cocoaItem->isEnabled();
@@ -327,9 +332,9 @@ void QCocoaMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *
void QCocoaMenu::insertNative(QCocoaMenuItem *item, QCocoaMenuItem *beforeItem)
{
item->nsItem().target = m_nativeMenu.delegate;
- if (!item->menu())
- [item->nsItem() setAction:@selector(itemFired:)];
- else if (isOpen() && item->nsItem()) // Someone's adding new items after aboutToShow() was emitted
+ item->nsItem().action = @selector(itemFired:);
+ // Someone's adding new items after aboutToShow() was emitted
+ if (isOpen() && item->menu() && item->nsItem())
item->menu()->setAttachedItem(item->nsItem());
item->setParentEnabled(isEnabled());
@@ -425,6 +430,10 @@ void QCocoaMenu::syncMenuItem(QPlatformMenuItem *menuItem)
QCocoaMenuItem* beforeItem = itemOrNull(m_menuItems.indexOf(cocoaItem) + 1);
insertNative(cocoaItem, beforeItem);
+ } else {
+ // Force NSMenuValidation to kick in. This is needed e.g.
+ // when an item's enabled state changes after menuWillOpen:
+ [m_nativeMenu update];
}
}
diff --git a/src/plugins/platforms/cocoa/qcocoanativeinterface.h b/src/plugins/platforms/cocoa/qcocoanativeinterface.h
index 477165607c..26fbe3e4bc 100644
--- a/src/plugins/platforms/cocoa/qcocoanativeinterface.h
+++ b/src/plugins/platforms/cocoa/qcocoanativeinterface.h
@@ -100,6 +100,8 @@ private:
*/
Q_INVOKABLE QPixmap defaultBackgroundPixmapForQWizard();
+ Q_INVOKABLE void clearCurrentThreadCocoaEventDispatcherInterruptFlag();
+
// QMacPastebardMime support. The mac pasteboard void pointers are
// QMacPastebardMime instances from the cocoa plugin or qtmacextras
// These two classes are kept in sync and can be casted between.
diff --git a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm
index 10435d0b1d..972230349b 100644
--- a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm
+++ b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm
@@ -44,6 +44,7 @@
#include "qcocoahelpers.h"
#include "qcocoaapplication.h"
#include "qcocoaintegration.h"
+#include "qcocoaeventdispatcher.h"
#include <qbytearray.h>
#include <qwindow.h>
@@ -194,6 +195,11 @@ QPixmap QCocoaNativeInterface::defaultBackgroundPixmapForQWizard()
return QPixmap();
}
+void QCocoaNativeInterface::clearCurrentThreadCocoaEventDispatcherInterruptFlag()
+{
+ QCocoaEventDispatcher::clearCurrentThreadCocoaEventDispatcherInterruptFlag();
+}
+
void QCocoaNativeInterface::onAppFocusWindowChanged(QWindow *window)
{
Q_UNUSED(window);
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h
index 1cd4c1905a..ac5f1a6851 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.h
+++ b/src/plugins/platforms/cocoa/qcocoawindow.h
@@ -238,6 +238,8 @@ public:
void setMenubar(QCocoaMenuBar *mb);
QCocoaMenuBar *menubar() const;
+ NSCursor *effectiveWindowCursor() const;
+ void applyEffectiveWindowCursor();
void setWindowCursor(NSCursor *cursor);
void registerTouch(bool enable);
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index 30ac79f3a0..0b33b9255f 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -1859,31 +1859,53 @@ QCocoaMenuBar *QCocoaWindow::menubar() const
return m_menubar;
}
-void QCocoaWindow::setWindowCursor(NSCursor *cursor)
+// Finds the effective cursor for this window by walking up the
+// ancestor chain (including this window) until a set cursor is
+// found. Returns nil if there is not set cursor.
+NSCursor *QCocoaWindow::effectiveWindowCursor() const
{
- // This function is called (via QCocoaCursor) by Qt to set
- // the cursor for this window. It can be called for a window
- // that is not currenly under the mouse pointer (for example
- // for a popup window.) Qt expects the set cursor to "stick":
- // it should be accociated with the window until a different
- // cursor is set.
- if (m_windowCursor != cursor) {
- [m_windowCursor release];
- m_windowCursor = [cursor retain];
- }
- // Use the built in cursor rect API if the QCocoaWindow has a NSWindow.
- // Othervise, set the cursor if this window is under the mouse. In
- // this case QNSView::cursorUpdate will set the cursor as the pointer
- // moves.
- if (m_nsWindow && window()->type() != Qt::ForeignWindow) {
- [m_nsWindow invalidateCursorRectsForView:m_view];
+ if (m_windowCursor)
+ return m_windowCursor;
+ if (!QPlatformWindow::parent())
+ return nil;
+ return static_cast<QCocoaWindow *>(QPlatformWindow::parent())->effectiveWindowCursor();
+}
+
+// Applies the cursor as returned by effectiveWindowCursor(), handles
+// the special no-cursor-set case by setting the arrow cursor.
+void QCocoaWindow::applyEffectiveWindowCursor()
+{
+ NSCursor *effectiveCursor = effectiveWindowCursor();
+ if (effectiveCursor) {
+ [effectiveCursor set];
} else {
- if (m_windowUnderMouse)
- [cursor set];
+ // We wold like to _unset_ the cursor here; but there is no such
+ // API. Fall back to setting the default arrow cursor.
+ [[NSCursor arrowCursor] set];
}
}
+void QCocoaWindow::setWindowCursor(NSCursor *cursor)
+{
+ if (m_windowCursor == cursor)
+ return;
+
+ // Setting a cursor in a foregin view is not supported.
+ if (window()->type() == Qt::ForeignWindow)
+ return;
+
+ [m_windowCursor release];
+ m_windowCursor = cursor;
+ [m_windowCursor retain];
+
+ // The installed view tracking area (see QNSView updateTrackingAreas) will
+ // handle cursor updates on mouse enter/leave. Handle the case where the
+ // mouse is on the this window by changing the cursor immediately.
+ if (m_windowUnderMouse)
+ applyEffectiveWindowCursor();
+}
+
void QCocoaWindow::registerTouch(bool enable)
{
m_registerTouchCount += enable ? 1 : -1;
diff --git a/src/plugins/platforms/cocoa/qnsview.h b/src/plugins/platforms/cocoa/qnsview.h
index d911b804cc..84be7eb797 100644
--- a/src/plugins/platforms/cocoa/qnsview.h
+++ b/src/plugins/platforms/cocoa/qnsview.h
@@ -127,7 +127,6 @@ Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper));
- (void)mouseMovedImpl:(NSEvent *)theEvent;
- (void)mouseEnteredImpl:(NSEvent *)theEvent;
- (void)mouseExitedImpl:(NSEvent *)theEvent;
-- (void)cursorUpdateImpl:(NSEvent *)theEvent;
- (void)rightMouseDown:(NSEvent *)theEvent;
- (void)rightMouseDragged:(NSEvent *)theEvent;
- (void)rightMouseUp:(NSEvent *)theEvent;
diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm
index 73fe5d8526..689fd06d66 100644
--- a/src/plugins/platforms/cocoa/qnsview.mm
+++ b/src/plugins/platforms/cocoa/qnsview.mm
@@ -121,7 +121,7 @@ static bool _q_dontOverrideCtrlLMB = false;
- (void)cursorUpdate:(NSEvent *)theEvent
{
- [view cursorUpdateImpl:theEvent];
+ [self cursorUpdate:theEvent];
}
@end
@@ -954,21 +954,10 @@ static bool _q_dontOverrideCtrlLMB = false;
[self addTrackingArea:m_trackingArea];
}
--(void)cursorUpdateImpl:(NSEvent *)theEvent
-{
- Q_UNUSED(theEvent)
- // Set the cursor manually if there is no NSWindow.
- if (!m_platformWindow->m_nsWindow && m_platformWindow->m_windowCursor)
- [m_platformWindow->m_windowCursor set];
- else
- [super cursorUpdate:theEvent];
-}
-
--(void)resetCursorRects
+- (void)cursorUpdate:(NSEvent *)theEvent
{
- // Use the cursor rect API if there is a NSWindow
- if (m_platformWindow->m_nsWindow && m_platformWindow->m_windowCursor)
- [self addCursorRect:[self visibleRect] cursor:m_platformWindow->m_windowCursor];
+ Q_UNUSED(theEvent);
+ m_platformWindow->applyEffectiveWindowCursor();
}
- (void)mouseMovedImpl:(NSEvent *)theEvent
@@ -1792,9 +1781,7 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent)
- (NSRange) selectedRange
{
- NSRange selectedRange = {NSNotFound, 0};
- selectedRange.location = NSNotFound;
- selectedRange.length = 0;
+ NSRange selectedRange = {0, 0};
QObject *fo = QGuiApplication::focusObject();
if (!fo)