diff options
Diffstat (limited to 'src/plugins')
74 files changed, 3178 insertions, 2595 deletions
diff --git a/src/plugins/bearer/corewlan/qcorewlanengine.mm b/src/plugins/bearer/corewlan/qcorewlanengine.mm index 341d3bccf2..c3dd49ff3e 100644 --- a/src/plugins/bearer/corewlan/qcorewlanengine.mm +++ b/src/plugins/bearer/corewlan/qcorewlanengine.mm @@ -62,33 +62,29 @@ extern "C" { // Otherwise it won't find CWKeychain* symbols at link time #include <ifaddrs.h> @interface QT_MANGLE_NAMESPACE(QNSListener) : NSObject <CWEventDelegate> -{ - NSNotificationCenter *notificationCenter; - CWWiFiClient *client; - QCoreWlanEngine *engine; - NSLock *locker; -} -- (void)powerStateDidChangeForWiFiInterfaceWithName:(NSString *)interfaceName; -- (void)remove; -- (void)setEngine:(QCoreWlanEngine *)coreEngine; -- (QCoreWlanEngine *)engine; -- (void)dealloc; @property (assign) QCoreWlanEngine* engine; @end -@implementation QT_MANGLE_NAMESPACE(QNSListener) +@implementation QT_MANGLE_NAMESPACE(QNSListener) { + NSNotificationCenter *notificationCenter; + CWWiFiClient *client; + QCoreWlanEngine *engine; + NSLock *locker; +} -- (id) init +- (instancetype)init { - [locker lock]; - QMacAutoReleasePool pool; - notificationCenter = [NSNotificationCenter defaultCenter]; - client = [CWWiFiClient sharedWiFiClient]; - client.delegate = self; - [client startMonitoringEventWithType:CWEventTypePowerDidChange error:nil]; - [locker unlock]; + if ((self = [super init])) { + [locker lock]; + QMacAutoReleasePool pool; + notificationCenter = [NSNotificationCenter defaultCenter]; + client = [CWWiFiClient sharedWiFiClient]; + client.delegate = self; + [client startMonitoringEventWithType:CWEventTypePowerDidChange error:nil]; + [locker unlock]; + } return self; } @@ -300,7 +296,7 @@ void QScanThread::getUserConfigurations() if(airportPlist != nil) { NSDictionary *prefNetDict = [airportPlist objectForKey:@"PreferredNetworks"]; - NSArray *thisSsidarray = [prefNetDict valueForKey:@"SSID_STR"]; + NSArray<NSString *> *thisSsidarray = [prefNetDict valueForKey:@"SSID_STR"]; for (NSString *ssidkey in thisSsidarray) { QString thisSsid = QString::fromNSString(ssidkey); if(!userProfiles.contains(thisSsid)) { diff --git a/src/plugins/imageformats/jpeg/qjpeghandler.cpp b/src/plugins/imageformats/jpeg/qjpeghandler.cpp index 1cdc28c077..e52a19703c 100644 --- a/src/plugins/imageformats/jpeg/qjpeghandler.cpp +++ b/src/plugins/imageformats/jpeg/qjpeghandler.cpp @@ -493,10 +493,10 @@ static inline void set_text(const QImage &image, j_compress_ptr cinfo, const QSt { const QMap<QString, QString> text = qt_getImageText(image, description); for (auto it = text.begin(), end = text.end(); it != end; ++it) { - QByteArray comment = it.key().toLatin1(); + QByteArray comment = it.key().toUtf8(); if (!comment.isEmpty()) comment += ": "; - comment += it.value().toLatin1(); + comment += it.value().toUtf8(); if (comment.length() > 65530) comment.truncate(65530); jpeg_write_marker(cinfo, JPEG_COM, (const JOCTET *)comment.constData(), comment.size()); @@ -904,7 +904,7 @@ bool QJpegHandlerPrivate::readJpegHeader(QIODevice *device) for (jpeg_saved_marker_ptr marker = info.marker_list; marker != NULL; marker = marker->next) { if (marker->marker == JPEG_COM) { QString key, value; - QString s = QString::fromLatin1((const char *)marker->data, marker->data_length); + QString s = QString::fromUtf8((const char *)marker->data, marker->data_length); int index = s.indexOf(QLatin1String(": ")); if (index == -1 || s.indexOf(QLatin1Char(' ')) < index) { key = QLatin1String("Description"); diff --git a/src/plugins/platforms/android/androidjniinput.cpp b/src/plugins/platforms/android/androidjniinput.cpp index dabab553c2..c4142a9b6e 100644 --- a/src/plugins/platforms/android/androidjniinput.cpp +++ b/src/plugins/platforms/android/androidjniinput.cpp @@ -805,8 +805,10 @@ namespace QtAndroidInput QAndroidInputContext *inputContext = QAndroidInputContext::androidInputContext(); if (inputContext && qGuiApp) { inputContext->emitInputPanelVisibleChanged(); - if (!visibility) + if (!visibility) { inputContext->emitKeyboardRectChanged(); + QMetaObject::invokeMethod(inputContext, "hideSelectionHandles", Qt::QueuedConnection); + } } #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL qDebug() << "@@@ KEYBOARDVISIBILITYCHANGED" << inputContext; diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp index f548a1fa96..430eaf638b 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.cpp +++ b/src/plugins/platforms/android/qandroidinputcontext.cpp @@ -63,6 +63,29 @@ QT_BEGIN_NAMESPACE +template <typename T> +class ScopedValueChangeBack +{ +public: + ScopedValueChangeBack(T &variable, T newValue) + : m_oldValue(variable), + m_variable(variable) + { + m_variable = newValue; + } + inline void setOldValue() + { + m_variable = m_oldValue; + } + ~ScopedValueChangeBack() + { + setOldValue(); + } +private: + T m_oldValue; + T &m_variable; +}; + static QAndroidInputContext *m_androidInputContext = 0; static char const *const QtNativeInputConnectionClassName = "org/qtproject/qt5/android/QtNativeInputConnection"; static char const *const QtExtractedTextClassName = "org/qtproject/qt5/android/QtExtractedText"; @@ -364,7 +387,7 @@ static QRect inputItemRectangle() QAndroidInputContext::QAndroidInputContext() : QPlatformInputContext(), m_composingTextStart(-1), m_blockUpdateSelection(false), - m_cursorHandleShown(CursorHandleNotShown), m_batchEditNestingLevel(0), m_focusObject(0) + m_handleMode(Hidden), m_batchEditNestingLevel(0), m_focusObject(0) { jclass clazz = QJNIEnvironmentPrivate::findClass(QtNativeInputConnectionClassName); if (Q_UNLIKELY(!clazz)) { @@ -444,10 +467,17 @@ QAndroidInputContext::QAndroidInputContext() auto im = qGuiApp->inputMethod(); if (!im->inputItemClipRectangle().contains(im->anchorRectangle()) || !im->inputItemClipRectangle().contains(im->cursorRectangle())) { - m_cursorHandleShown = CursorHandleNotShown; + m_handleMode = Hidden; updateSelectionHandles(); } }); + m_hideCursorHandleTimer.setInterval(4000); + m_hideCursorHandleTimer.setSingleShot(true); + m_hideCursorHandleTimer.setTimerType(Qt::VeryCoarseTimer); + connect(&m_hideCursorHandleTimer, &QTimer::timeout, this, [this]{ + m_handleMode = Hidden; + updateSelectionHandles(); + }); } QAndroidInputContext::~QAndroidInputContext() @@ -485,7 +515,7 @@ void QAndroidInputContext::reset() { clear(); m_batchEditNestingLevel = 0; - m_cursorHandleShown = QAndroidInputContext::CursorHandleNotShown; + m_handleMode = Hidden; if (qGuiApp->focusObject()) { QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQueryThreadSafe(Qt::ImEnabled); if (!query.isNull() && query->value(Qt::ImEnabled).toBool()) { @@ -541,9 +571,9 @@ void QAndroidInputContext::updateSelectionHandles() return; auto im = qGuiApp->inputMethod(); - if (!m_focusObject || (m_cursorHandleShown == CursorHandleNotShown)) { + if (!m_focusObject || ((m_handleMode & 0xff) == Hidden)) { // Hide the handles - QtAndroidInput::updateHandles(0); + QtAndroidInput::updateHandles(Hidden); return; } QWindow *window = qGuiApp->focusWindow(); @@ -558,18 +588,23 @@ void QAndroidInputContext::updateSelectionHandles() if (cpos == anchor || im->anchorRectangle().isNull()) { if (!query.value(Qt::ImEnabled).toBool()) { - QtAndroidInput::updateHandles(0); + QtAndroidInput::updateHandles(Hidden); return; } auto curRect = im->cursorRectangle(); QPoint cursorPoint(curRect.center().x(), curRect.bottom()); QPoint editMenuPoint(curRect.center().x(), curRect.top()); - QtAndroidInput::updateHandles(m_cursorHandleShown, cursorPoint * pixelDensity, + QtAndroidInput::updateHandles(m_handleMode, cursorPoint * pixelDensity, editMenuPoint * pixelDensity); + m_handleMode |= ShowCursor; + // The VK is hidden, reset the timer + if (m_hideCursorHandleTimer.isActive()) + m_hideCursorHandleTimer.start(); return; } + m_handleMode |= ShowSelection; auto leftRect = im->cursorRectangle(); auto rightRect = im->anchorRectangle(); if (cpos > anchor) @@ -577,13 +612,9 @@ void QAndroidInputContext::updateSelectionHandles() QPoint leftPoint(leftRect.bottomLeft().toPoint() * pixelDensity); QPoint righPoint(rightRect.bottomRight().toPoint() * pixelDensity); - QtAndroidInput::updateHandles(CursorHandleShowSelection, leftPoint, righPoint, + QtAndroidInput::updateHandles(ShowSelection, leftPoint, righPoint, query.value(Qt::ImCurrentSelection).toString().isRightToLeft()); - - if (m_cursorHandleShown == CursorHandleShowPopup) { - // make sure the popup does not reappear when the selection menu closes - m_cursorHandleShown = QAndroidInputContext::CursorHandleNotShown; - } + m_hideCursorHandleTimer.stop(); } /* @@ -668,31 +699,73 @@ void QAndroidInputContext::handleLocationChanged(int handleId, int x, int y) void QAndroidInputContext::touchDown(int x, int y) { - if (m_focusObject && inputItemRectangle().contains(x, y) && !m_cursorHandleShown) { + if (m_focusObject && inputItemRectangle().contains(x, y)) { // If the user touch the input rectangle, we can show the cursor handle - m_cursorHandleShown = QAndroidInputContext::CursorHandleShowNormal; - updateSelectionHandles(); + m_handleMode = ShowCursor; + // The VK will appear in a moment, stop the timer + m_hideCursorHandleTimer.stop(); + QMetaObject::invokeMethod(this, "updateSelectionHandles", Qt::QueuedConnection); } } void QAndroidInputContext::longPress(int x, int y) { if (m_focusObject && inputItemRectangle().contains(x, y)) { - // Show the paste menu if there is something to paste. - m_cursorHandleShown = QAndroidInputContext::CursorHandleShowPopup; - updateSelectionHandles(); + handleLocationChanged(1, x, y); + ScopedValueChangeBack<bool> svcb(m_blockUpdateSelection, true); + + QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImAnchorPosition | Qt::ImTextBeforeCursor | Qt::ImTextAfterCursor); + QCoreApplication::sendEvent(m_focusObject, &query); + int cursor = query.value(Qt::ImCursorPosition).toInt(); + int anchor = cursor; + QString before = query.value(Qt::ImTextBeforeCursor).toString(); + QString after = query.value(Qt::ImTextAfterCursor).toString(); + for (const auto &ch : after) { + if (!ch.isLetterOrNumber()) + break; + ++anchor; + } + + for (auto itch = before.rbegin(); itch != after.rend(); ++itch) { + if (!itch->isLetterOrNumber()) + break; + --cursor; + } + if (cursor == anchor || cursor < 0 || cursor - anchor > 500) { + m_handleMode = ShowCursor | ShowEditPopup; + QMetaObject::invokeMethod(this, "updateSelectionHandles", Qt::QueuedConnection); + return; + } + QList<QInputMethodEvent::Attribute> imAttributes; + imAttributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursor, 0, QVariant())); + imAttributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, anchor, cursor - anchor, QVariant())); + QInputMethodEvent event(QString(), imAttributes); + QGuiApplication::sendEvent(m_focusObject, &event); + + m_handleMode = ShowSelection | ShowEditPopup; + QMetaObject::invokeMethod(this, "updateSelectionHandles", Qt::QueuedConnection); } } void QAndroidInputContext::keyDown() { - if (m_cursorHandleShown) { + if (m_handleMode) { // When the user enter text on the keyboard, we hide the cursor handle - m_cursorHandleShown = QAndroidInputContext::CursorHandleNotShown; + m_handleMode = Hidden; updateSelectionHandles(); } } +void QAndroidInputContext::hideSelectionHandles() +{ + if (m_handleMode & ShowSelection) { + m_handleMode = Hidden; + updateSelectionHandles(); + } else { + m_hideCursorHandleTimer.start(); + } +} + void QAndroidInputContext::update(Qt::InputMethodQueries queries) { QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQueryThreadSafe(queries); @@ -809,9 +882,7 @@ jboolean QAndroidInputContext::endBatchEdit() */ jboolean QAndroidInputContext::commitText(const QString &text, jint newCursorPosition) { - bool updateSelectionWasBlocked = m_blockUpdateSelection; - m_blockUpdateSelection = true; - + ScopedValueChangeBack<bool> svcb(m_blockUpdateSelection, true); QInputMethodEvent event; event.setCommitString(text); sendInputMethodEventThreadSafe(&event); @@ -831,8 +902,7 @@ jboolean QAndroidInputContext::commitText(const QString &text, jint newCursorPos newLocalPos, 0)); } } - m_blockUpdateSelection = updateSelectionWasBlocked; - + svcb.setOldValue(); updateCursorPosition(); return JNI_TRUE; } @@ -1083,8 +1153,7 @@ jboolean QAndroidInputContext::setComposingRegion(jint start, jint end) int localStart = start - blockPosition; // Qt uses position inside block int currentCursor = wasComposing ? m_composingCursor : blockPosition + localPos; - bool updateSelectionWasBlocked = m_blockUpdateSelection; - m_blockUpdateSelection = true; + ScopedValueChangeBack<bool> svcb(m_blockUpdateSelection, true); QString text = query->value(Qt::ImSurroundingText).toString(); @@ -1110,8 +1179,6 @@ jboolean QAndroidInputContext::setComposingRegion(jint start, jint end) event.setCommitString(QString(), relativeStart, length); sendInputMethodEventThreadSafe(&event); - m_blockUpdateSelection = updateSelectionWasBlocked; - #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL QSharedPointer<QInputMethodQueryEvent> query2 = focusObjectInputMethodQueryThreadSafe(); if (!query2.isNull()) { @@ -1163,19 +1230,21 @@ jboolean QAndroidInputContext::setSelection(jint start, jint end) jboolean QAndroidInputContext::selectAll() { + m_handleMode &= ~ShowEditPopup; sendShortcut(QKeySequence::SelectAll); return JNI_TRUE; } jboolean QAndroidInputContext::cut() { - m_cursorHandleShown = CursorHandleNotShown; + m_handleMode &= ~ShowEditPopup; sendShortcut(QKeySequence::Cut); return JNI_TRUE; } jboolean QAndroidInputContext::copy() { + m_handleMode &= ~ShowEditPopup; sendShortcut(QKeySequence::Copy); return JNI_TRUE; } @@ -1189,7 +1258,7 @@ jboolean QAndroidInputContext::copyURL() jboolean QAndroidInputContext::paste() { finishComposingText(); - m_cursorHandleShown = CursorHandleNotShown; + m_handleMode &= ~ShowEditPopup; sendShortcut(QKeySequence::Paste); return JNI_TRUE; } diff --git a/src/plugins/platforms/android/qandroidinputcontext.h b/src/plugins/platforms/android/qandroidinputcontext.h index e7692bf720..1b351f16bd 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.h +++ b/src/plugins/platforms/android/qandroidinputcontext.h @@ -44,6 +44,7 @@ #include <qpa/qplatforminputcontext.h> #include <jni.h> #include <qevent.h> +#include <QTimer> QT_BEGIN_NAMESPACE @@ -58,6 +59,14 @@ class QAndroidInputContext: public QPlatformInputContext }; public: + enum HandleMode { + Hidden = 0, + ShowCursor = 1, + ShowSelection = 2, + ShowEditPopup = 0x100 + }; + Q_DECLARE_FLAGS(HandleModes, HandleMode) + struct ExtractedText { ExtractedText() { clear(); } @@ -124,6 +133,7 @@ public slots: void touchDown(int x, int y); void longPress(int x, int y); void keyDown(); + void hideSelectionHandles(); private slots: void showInputPanelLater(Qt::ApplicationState); @@ -145,17 +155,12 @@ private: int m_composingCursor; QMetaObject::Connection m_updateCursorPosConnection; bool m_blockUpdateSelection; - enum CursorHandleShowMode { - CursorHandleNotShown, - CursorHandleShowNormal = 1, - CursorHandleShowSelection = 2, - CursorHandleShowPopup = 3 - }; - CursorHandleShowMode m_cursorHandleShown; + HandleModes m_handleMode; QAtomicInt m_batchEditNestingLevel; QObject *m_focusObject; + QTimer m_hideCursorHandleTimer; }; - +Q_DECLARE_OPERATORS_FOR_FLAGS(QAndroidInputContext::HandleModes) QT_END_NAMESPACE #endif // ANDROIDINPUTCONTEXT_H diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.h b/src/plugins/platforms/cocoa/qcocoaaccessibility.h index e456364555..457c158ddc 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibility.h +++ b/src/plugins/platforms/cocoa/qcocoaaccessibility.h @@ -46,6 +46,8 @@ #ifndef QT_NO_ACCESSIBILITY +@class QT_MANGLE_NAMESPACE(QMacAccessibilityElement); + QT_BEGIN_NAMESPACE class QCocoaAccessibility : public QPlatformAccessibility @@ -82,9 +84,8 @@ namespace QCocoaAccessible { NSString *macRole(QAccessibleInterface *interface); NSString *macSubrole(QAccessibleInterface *interface); bool shouldBeIgnored(QAccessibleInterface *interface); -NSArray *unignoredChildren(QAccessibleInterface *interface); +NSArray<QT_MANGLE_NAMESPACE(QMacAccessibilityElement) *> *unignoredChildren(QAccessibleInterface *interface); NSString *getTranslatedAction(const QString &qtAction); -NSMutableArray *createTranslatedActionsList(const QStringList &qtActions); QString translateAction(NSString *nsAction, QAccessibleInterface *interface); bool hasValueAttribute(QAccessibleInterface *interface); id getValueAttribute(QAccessibleInterface *interface); diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm index 8b6dcb35a6..0a773ced29 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm @@ -257,12 +257,12 @@ bool shouldBeIgnored(QAccessibleInterface *interface) return false; } -NSArray *unignoredChildren(QAccessibleInterface *interface) +NSArray<QMacAccessibilityElement *> *unignoredChildren(QAccessibleInterface *interface) { int numKids = interface->childCount(); // qDebug() << "Children for: " << axid << iface << " are: " << numKids; - NSMutableArray *kids = [NSMutableArray arrayWithCapacity:numKids]; + NSMutableArray<QMacAccessibilityElement *> *kids = [NSMutableArray<QMacAccessibilityElement *> arrayWithCapacity:numKids]; for (int i = 0; i < numKids; ++i) { QAccessibleInterface *child = interface->child(i); if (!child || !child->isValid() || child->state().invalid || child->state().invisible) @@ -387,7 +387,7 @@ id getValueAttribute(QAccessibleInterface *interface) } if (interface->state().checkable) { - return [NSNumber numberWithInt: (interface->state().checked ? 1 : 0)]; + return interface->state().checked ? @(1) : @(0); } return nil; diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h index 07f3f3a99e..914aaa2b1b 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h +++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h @@ -52,13 +52,10 @@ @class QT_MANGLE_NAMESPACE(QMacAccessibilityElement); -@interface QT_MANGLE_NAMESPACE(QMacAccessibilityElement) : NSObject { - NSString *role; - QAccessible::Id axid; -} +@interface QT_MANGLE_NAMESPACE(QMacAccessibilityElement) : NSObject -- (id)initWithId:(QAccessible::Id)anId; -+ (QT_MANGLE_NAMESPACE(QMacAccessibilityElement) *)elementWithId:(QAccessible::Id)anId; +- (instancetype)initWithId:(QAccessible::Id)anId; ++ (instancetype)elementWithId:(QAccessible::Id)anId; @end diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm index df2336d08b..ad251a6a44 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm @@ -99,9 +99,12 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of *end = curEnd; } -@implementation QMacAccessibilityElement +@implementation QMacAccessibilityElement { + NSString *role; + QAccessible::Id axid; +} -- (id)initWithId:(QAccessible::Id)anId +- (instancetype)initWithId:(QAccessible::Id)anId { Q_ASSERT((int)anId < 0); self = [super init]; @@ -115,7 +118,7 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of return self; } -+ (id)elementWithId:(QAccessible::Id)anId ++ (instancetype)elementWithId:(QAccessible::Id)anId { Q_ASSERT(anId); if (!anId) @@ -168,22 +171,15 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of { QStringRef textBefore = QStringRef(&text, 0, index); int newlines = textBefore.count(QLatin1Char('\n')); - return [NSNumber numberWithInt: newlines]; + return @(newlines); } - (BOOL) accessibilityNotifiesWhenDestroyed { return YES; } -- (NSArray *)accessibilityAttributeNames { - static NSArray *defaultAttributes = nil; - - QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); - if (!iface || !iface->isValid()) - return defaultAttributes; - - if (defaultAttributes == nil) { - defaultAttributes = [[NSArray alloc] initWithObjects: +- (NSArray<NSString *> *)accessibilityAttributeNames { + static NSArray<NSString *> *defaultAttributes = [@[ NSAccessibilityRoleAttribute, NSAccessibilityRoleDescriptionAttribute, NSAccessibilitySubroleAttribute, @@ -196,35 +192,36 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of NSAccessibilitySizeAttribute, NSAccessibilityTitleAttribute, NSAccessibilityDescriptionAttribute, - NSAccessibilityEnabledAttribute, - nil]; - } + NSAccessibilityEnabledAttribute + ] retain]; + + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface || !iface->isValid()) + return defaultAttributes; - NSMutableArray *attributes = [[NSMutableArray alloc] initWithCapacity : [defaultAttributes count]]; - [attributes addObjectsFromArray : defaultAttributes]; + NSMutableArray<NSString *> *attributes = [[NSMutableArray<NSString *> alloc] initWithCapacity:defaultAttributes.count]; + [attributes addObjectsFromArray:defaultAttributes]; if (QCocoaAccessible::hasValueAttribute(iface)) { - [attributes addObject : NSAccessibilityValueAttribute]; + [attributes addObject:NSAccessibilityValueAttribute]; } if (iface->textInterface()) { - [attributes addObjectsFromArray: [[NSArray alloc] initWithObjects: + [attributes addObjectsFromArray:@[ NSAccessibilityNumberOfCharactersAttribute, NSAccessibilitySelectedTextAttribute, NSAccessibilitySelectedTextRangeAttribute, NSAccessibilityVisibleCharacterRangeAttribute, - NSAccessibilityInsertionPointLineNumberAttribute, - nil + NSAccessibilityInsertionPointLineNumberAttribute ]]; // TODO: multi-selection: NSAccessibilitySelectedTextRangesAttribute, } if (iface->valueInterface()) { - [attributes addObjectsFromArray: [[NSArray alloc] initWithObjects: + [attributes addObjectsFromArray:@[ NSAccessibilityMinValueAttribute, - NSAccessibilityMaxValueAttribute, - nil + NSAccessibilityMaxValueAttribute ]]; } @@ -261,13 +258,13 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of - (id) minValueAttribute:(QAccessibleInterface*)iface { if (QAccessibleValueInterface *val = iface->valueInterface()) - return [NSNumber numberWithDouble: val->minimumValue().toDouble()]; + return @(val->minimumValue().toDouble()); return nil; } - (id) maxValueAttribute:(QAccessibleInterface*)iface { if (QAccessibleValueInterface *val = iface->valueInterface()) - return [NSNumber numberWithDouble: val->maximumValue().toDouble()]; + return @(val->maximumValue().toDouble()); return nil; } @@ -289,7 +286,7 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of } else if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) { // Just check if the app thinks we're focused. id focusedElement = [NSApp accessibilityAttributeValue:NSAccessibilityFocusedUIElementAttribute]; - return [NSNumber numberWithBool:[focusedElement isEqual:self]]; + return @([focusedElement isEqual:self]); } else if ([attribute isEqualToString:NSAccessibilityParentAttribute]) { return NSAccessibilityUnignoredAncestor([self parentElement]); } else if ([attribute isEqualToString:NSAccessibilityWindowAttribute]) { @@ -312,7 +309,7 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of } else if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) { return iface->text(QAccessible::Description).toNSString(); } else if ([attribute isEqualToString:NSAccessibilityEnabledAttribute]) { - return [NSNumber numberWithBool:!iface->state().disabled]; + return @(!iface->state().disabled); } else if ([attribute isEqualToString:NSAccessibilityValueAttribute]) { // VoiceOver asks for the value attribute for all elements. Return nil // if we don't want the element to have a value attribute. @@ -323,7 +320,7 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of } else if ([attribute isEqualToString:NSAccessibilityNumberOfCharactersAttribute]) { if (QAccessibleTextInterface *text = iface->textInterface()) - return [NSNumber numberWithInt: text->characterCount()]; + return @(text->characterCount()); return nil; } else if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) { if (QAccessibleTextInterface *text = iface->textInterface()) { @@ -357,7 +354,7 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of int position = text->cursorPosition(); convertLineOffset(text, &line, &position); } - return [NSNumber numberWithInt: line]; + return @(line); } return nil; } else if ([attribute isEqualToString:NSAccessibilityMinValueAttribute]) { @@ -378,18 +375,17 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of } if (iface->textInterface()) { - return [[NSArray alloc] initWithObjects: - NSAccessibilityStringForRangeParameterizedAttribute, - NSAccessibilityLineForIndexParameterizedAttribute, - NSAccessibilityRangeForLineParameterizedAttribute, - NSAccessibilityRangeForPositionParameterizedAttribute, -// NSAccessibilityRangeForIndexParameterizedAttribute, - NSAccessibilityBoundsForRangeParameterizedAttribute, -// NSAccessibilityRTFForRangeParameterizedAttribute, - NSAccessibilityStyleRangeForIndexParameterizedAttribute, - NSAccessibilityAttributedStringForRangeParameterizedAttribute, - nil - ]; + return @[ + NSAccessibilityStringForRangeParameterizedAttribute, + NSAccessibilityLineForIndexParameterizedAttribute, + NSAccessibilityRangeForLineParameterizedAttribute, + NSAccessibilityRangeForPositionParameterizedAttribute, +// NSAccessibilityRangeForIndexParameterizedAttribute, + NSAccessibilityBoundsForRangeParameterizedAttribute, +// NSAccessibilityRTFForRangeParameterizedAttribute, + NSAccessibilityStyleRangeForIndexParameterizedAttribute, + NSAccessibilityAttributedStringForRangeParameterizedAttribute + ]; } return nil; @@ -416,7 +412,7 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of return nil; int line = -1; convertLineOffset(iface->textInterface(), &line, &index); - return [NSNumber numberWithInt:line]; + return @(line); } if ([attribute isEqualToString: NSAccessibilityRangeForLineParameterizedAttribute]) { int line = [parameter intValue]; diff --git a/src/plugins/platforms/cocoa/qcocoaapplication.h b/src/plugins/platforms/cocoa/qcocoaapplication.h index 66a92686bc..15530d8281 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplication.h +++ b/src/plugins/platforms/cocoa/qcocoaapplication.h @@ -89,8 +89,7 @@ #import <AppKit/AppKit.h> -@interface QT_MANGLE_NAMESPACE(QNSApplication) : NSApplication { -} +@interface QT_MANGLE_NAMESPACE(QNSApplication) : NSApplication @end QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSApplication); diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h index 59c71017e3..3073cb6b44 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h +++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h @@ -92,18 +92,12 @@ @class QT_MANGLE_NAMESPACE(QCocoaMenuLoader); -@interface QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) : NSObject <NSApplicationDelegate> { - bool startedQuit; - NSMenu *dockMenu; - NSObject <NSApplicationDelegate> *reflectionDelegate; - bool inLaunch; -} -+ (QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate)*)sharedDelegate; +@interface QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) : NSObject <NSApplicationDelegate> ++ (instancetype)sharedDelegate; - (void)setDockMenu:(NSMenu *)newMenu; -- (void)setReflectionDelegate:(NSObject <NSApplicationDelegate> *)oldDelegate; -- (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent; -- (void) removeAppleEventHandlers; -- (bool) inLaunch; +- (void)setReflectionDelegate:(NSObject<NSApplicationDelegate> *)oldDelegate; +- (void)removeAppleEventHandlers; +- (bool)inLaunch; @end QT_NAMESPACE_ALIAS_OBJC_CLASS(QCocoaApplicationDelegate); diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm index a94e0dc517..53407d8b62 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm +++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm @@ -73,7 +73,6 @@ #import "qcocoaapplicationdelegate.h" -#import "qnswindowdelegate.h" #import "qcocoamenuloader.h" #include "qcocoaintegration.h" #include <qevent.h> @@ -86,7 +85,12 @@ QT_USE_NAMESPACE -@implementation QCocoaApplicationDelegate +@implementation QCocoaApplicationDelegate { + bool startedQuit; + NSMenu *dockMenu; + NSObject <NSApplicationDelegate> *reflectionDelegate; + bool inLaunch; +} + (instancetype)sharedDelegate { @@ -102,7 +106,7 @@ QT_USE_NAMESPACE return shared; } -- (id)init +- (instancetype)init { self = [super init]; if (self) { @@ -151,7 +155,7 @@ QT_USE_NAMESPACE return [[dockMenu retain] autorelease]; } -- (BOOL) canQuit +- (BOOL)canQuit { [[NSApp mainMenu] cancelTracking]; @@ -219,7 +223,7 @@ QT_USE_NAMESPACE return NSTerminateCancel; } -- (void) applicationWillFinishLaunching:(NSNotification *)notification +- (void)applicationWillFinishLaunching:(NSNotification *)notification { Q_UNUSED(notification); @@ -249,14 +253,14 @@ QT_USE_NAMESPACE } // called by QCocoaIntegration's destructor before resetting the application delegate to nil -- (void) removeAppleEventHandlers +- (void)removeAppleEventHandlers { NSAppleEventManager *eventManager = [NSAppleEventManager sharedAppleEventManager]; [eventManager removeEventHandlerForEventClass:kCoreEventClass andEventID:kAEQuitApplication]; [eventManager removeEventHandlerForEventClass:kInternetEventClass andEventID:kAEGetURL]; } -- (bool) inLaunch +- (bool)inLaunch { return inLaunch; } diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm index 57a03905ab..3621e5102f 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm @@ -44,7 +44,7 @@ QT_BEGIN_NAMESPACE -Q_LOGGING_CATEGORY(lcCocoaBackingStore, "qt.qpa.cocoa.backingstore"); +Q_LOGGING_CATEGORY(lcQpaBackingStore, "qt.qpa.backingstore"); QCocoaBackingStore::QCocoaBackingStore(QWindow *window) : QRasterBackingStore(window) @@ -101,13 +101,13 @@ void QCocoaBackingStore::flush(QWindow *window, const QRegion ®ion, const QPo QNSView *topLevelView = qnsview_cast(static_cast<QCocoaWindow *>(topLevelWindow->handle())->view()); QNSView *view = qnsview_cast(static_cast<QCocoaWindow *>(window->handle())->view()); - if (lcCocoaBackingStore().isDebugEnabled()) { + if (lcQpaBackingStore().isDebugEnabled()) { QString targetViewDescription; if (view != topLevelView) { QDebug targetDebug(&targetViewDescription); targetDebug << "onto" << topLevelView << "at" << offset; } - qCDebug(lcCocoaBackingStore) << "Flushing" << region << "of" << view << qPrintable(targetViewDescription); + qCDebug(lcQpaBackingStore) << "Flushing" << region << "of" << view << qPrintable(targetViewDescription); } // Prevent potentially costly color conversion by assigning the display color space diff --git a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm index aa6124507d..0e127902ae 100644 --- a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm @@ -50,7 +50,14 @@ QT_USE_NAMESPACE @interface QT_MANGLE_NAMESPACE(QNSColorPanelDelegate) : NSObject<NSWindowDelegate, QNSPanelDelegate> -{ +- (void)restoreOriginalContentView; +- (void)updateQtColor; +- (void)finishOffWithCode:(NSInteger)code; +@end + +QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate); + +@implementation QNSColorPanelDelegate { @public NSColorPanel *mColorPanel; QCocoaColorDialogHelper *mHelper; @@ -61,17 +68,9 @@ QT_USE_NAMESPACE BOOL mDialogIsExecuting; BOOL mResultSet; BOOL mClosingDueToKnownButton; -}; -- (void)restoreOriginalContentView; -- (void)updateQtColor; -- (void)finishOffWithCode:(NSInteger)code; -@end - -QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate); - -@implementation QNSColorPanelDelegate +} -- (id)init +- (instancetype)init { self = [super init]; mColorPanel = [NSColorPanel sharedColorPanel]; diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm index bf9ba4eccf..06978fb3fc 100644 --- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm +++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm @@ -637,6 +637,8 @@ NSModalSession QCocoaEventDispatcherPrivate::currentModalSession() if (!info.session) { QMacAutoReleasePool pool; QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(info.window->handle()); + if (!cocoaWindow) + continue; NSWindow *nswindow = cocoaWindow->nativeWindow(); if (!nswindow) continue; @@ -647,6 +649,15 @@ NSModalSession QCocoaEventDispatcherPrivate::currentModalSession() [(NSWindow*) info.nswindow retain]; QRect rect = cocoaWindow->geometry(); info.session = [NSApp beginModalSessionForWindow:nswindow]; + + // The call to beginModalSessionForWindow above processes events and may + // have deleted or destroyed the window. Check if it's still valid. + if (!info.window) + continue; + cocoaWindow = static_cast<QCocoaWindow *>(info.window->handle()); + if (!cocoaWindow) + continue; + if (rect != cocoaWindow->geometry()) cocoaWindow->setGeometry(rect); } diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm index 94f2125bad..55c4e51242 100644 --- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm @@ -81,23 +81,6 @@ typedef QSharedPointer<QFileDialogOptions> SharedPointerFileDialogOptions; @interface QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) : NSObject<NSOpenSavePanelDelegate> -{ - @public - NSOpenPanel *mOpenPanel; - NSSavePanel *mSavePanel; - NSView *mAccessoryView; - NSPopUpButton *mPopUpButton; - NSTextField *mTextField; - QCocoaFileDialogHelper *mHelper; - NSString *mCurrentDir; - - int mReturnCode; - - SharedPointerFileDialogOptions mOptions; - QString *mCurrentSelection; - QStringList *mNameFilterDropDownList; - QStringList *mSelectedNameFilter; -} - (NSString *)strip:(const QString &)label; - (BOOL)panel:(id)sender shouldEnableURL:(NSURL *)url; @@ -117,12 +100,27 @@ typedef QSharedPointer<QFileDialogOptions> SharedPointerFileDialogOptions; QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSOpenSavePanelDelegate); -@implementation QNSOpenSavePanelDelegate +@implementation QNSOpenSavePanelDelegate { + @public + NSOpenPanel *mOpenPanel; + NSSavePanel *mSavePanel; + NSView *mAccessoryView; + NSPopUpButton *mPopUpButton; + NSTextField *mTextField; + QCocoaFileDialogHelper *mHelper; + NSString *mCurrentDir; + + int mReturnCode; + + SharedPointerFileDialogOptions mOptions; + QString *mCurrentSelection; + QStringList *mNameFilterDropDownList; + QStringList *mSelectedNameFilter; +} -- (id)initWithAcceptMode: - (const QString &)selectFile - options:(SharedPointerFileDialogOptions)options - helper:(QCocoaFileDialogHelper *)helper +- (instancetype)initWithAcceptMode:(const QString &)selectFile + options:(SharedPointerFileDialogOptions)options + helper:(QCocoaFileDialogHelper *)helper { self = [super init]; mOptions = options; @@ -406,9 +404,9 @@ static QString strippedText(QString s) { if (mOpenPanel) { QList<QUrl> result; - NSArray* array = [mOpenPanel URLs]; - for (NSUInteger i=0; i<[array count]; ++i) { - QString path = QString::fromNSString([[array objectAtIndex:i] path]).normalized(QString::NormalizationForm_C); + NSArray<NSURL *> *array = [mOpenPanel URLs]; + for (NSURL *url in array) { + QString path = QString::fromNSString(url.path).normalized(QString::NormalizationForm_C); result << QUrl::fromLocalFile(path); } return result; diff --git a/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm index 9a96895d07..3010aa0870 100644 --- a/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm @@ -76,7 +76,15 @@ static QFont qfontForCocoaFont(NSFont *cocoaFont, const QFont &resolveFont) @class QT_MANGLE_NAMESPACE(QNSFontPanelDelegate); @interface QT_MANGLE_NAMESPACE(QNSFontPanelDelegate) : NSObject<NSWindowDelegate, QNSPanelDelegate> -{ +- (void)restoreOriginalContentView; +- (void)updateQtFont; +- (void)changeFont:(id)sender; +- (void)finishOffWithCode:(NSInteger)code; +@end + +QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSFontPanelDelegate); + +@implementation QNSFontPanelDelegate { @public NSFontPanel *mFontPanel; QCocoaFontDialogHelper *mHelper; @@ -86,33 +94,25 @@ static QFont qfontForCocoaFont(NSFont *cocoaFont, const QFont &resolveFont) NSInteger mResultCode; BOOL mDialogIsExecuting; BOOL mResultSet; -}; -- (void)restoreOriginalContentView; -- (void)updateQtFont; -- (void)changeFont:(id)sender; -- (void)finishOffWithCode:(NSInteger)code; -@end - -QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSFontPanelDelegate); - -@implementation QNSFontPanelDelegate +} -- (id)init +- (instancetype)init { - self = [super init]; - mFontPanel = [NSFontPanel sharedFontPanel]; - mHelper = 0; - mStolenContentView = 0; - mPanelButtons = 0; - mResultCode = NSModalResponseCancel; - mDialogIsExecuting = false; - mResultSet = false; + if ((self = [super init])) { + mFontPanel = [NSFontPanel sharedFontPanel]; + mHelper = 0; + mStolenContentView = 0; + mPanelButtons = 0; + mResultCode = NSModalResponseCancel; + mDialogIsExecuting = false; + mResultSet = false; - [mFontPanel setRestorable:NO]; - [mFontPanel setDelegate:self]; - [[NSFontManager sharedFontManager] setDelegate:self]; + [mFontPanel setRestorable:NO]; + [mFontPanel setDelegate:self]; + [[NSFontManager sharedFontManager] setDelegate:self]; - [mFontPanel retain]; + [mFontPanel retain]; + } return self; } diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h index 84632c1487..1bead3304a 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.h +++ b/src/plugins/platforms/cocoa/qcocoahelpers.h @@ -62,19 +62,16 @@ Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSView)); QT_BEGIN_NAMESPACE -Q_DECLARE_LOGGING_CATEGORY(lcQpaCocoaWindow) -Q_DECLARE_LOGGING_CATEGORY(lcQpaCocoaDrawing) -Q_DECLARE_LOGGING_CATEGORY(lcQpaCocoaMouse) +Q_DECLARE_LOGGING_CATEGORY(lcQpaWindow) +Q_DECLARE_LOGGING_CATEGORY(lcQpaDrawing) +Q_DECLARE_LOGGING_CATEGORY(lcQpaMouse) class QPixmap; class QString; // Conversion functions -QStringList qt_mac_NSArrayToQStringList(void *nsarray); -void *qt_mac_QStringListToNSMutableArrayVoid(const QStringList &list); - -inline NSMutableArray *qt_mac_QStringListToNSMutableArray(const QStringList &qstrlist) -{ return reinterpret_cast<NSMutableArray *>(qt_mac_QStringListToNSMutableArrayVoid(qstrlist)); } +QStringList qt_mac_NSArrayToQStringList(NSArray<NSString *> *nsarray); +NSMutableArray<NSString *> *qt_mac_QStringListToNSMutableArray(const QStringList &list); NSDragOperation qt_mac_mapDropAction(Qt::DropAction action); NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions); @@ -170,12 +167,7 @@ QT_END_NAMESPACE - (void)onCancelClicked; @end -@interface QT_MANGLE_NAMESPACE(QNSPanelContentsWrapper) : NSView { - NSButton *_okButton; - NSButton *_cancelButton; - NSView *_panelContents; - NSEdgeInsets _panelContentsMargins; -} +@interface QT_MANGLE_NAMESPACE(QNSPanelContentsWrapper) : NSView @property (nonatomic, readonly) NSButton *okButton; @property (nonatomic, readonly) NSButton *cancelButton; @@ -187,6 +179,7 @@ QT_END_NAMESPACE - (NSButton *)createButtonWithTitle:(const char *)title; - (void)layout; + @end QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSPanelContentsWrapper); diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm index b729c7f4c0..d7b3936662 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.mm +++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm @@ -57,29 +57,27 @@ QT_BEGIN_NAMESPACE -Q_LOGGING_CATEGORY(lcQpaCocoaWindow, "qt.qpa.cocoa.window"); -Q_LOGGING_CATEGORY(lcQpaCocoaDrawing, "qt.qpa.cocoa.drawing"); -Q_LOGGING_CATEGORY(lcQpaCocoaMouse, "qt.qpa.cocoa.mouse"); +Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window"); +Q_LOGGING_CATEGORY(lcQpaDrawing, "qt.qpa.drawing"); +Q_LOGGING_CATEGORY(lcQpaMouse, "qt.qpa.input.mouse"); // // Conversion Functions // -QStringList qt_mac_NSArrayToQStringList(void *nsarray) +QStringList qt_mac_NSArrayToQStringList(NSArray<NSString *> *array) { QStringList result; - NSArray *array = static_cast<NSArray *>(nsarray); - for (NSUInteger i=0; i<[array count]; ++i) - result << QString::fromNSString([array objectAtIndex:i]); + for (NSString *string in array) + result << QString::fromNSString(string); return result; } -void *qt_mac_QStringListToNSMutableArrayVoid(const QStringList &list) +NSMutableArray<NSString *> *qt_mac_QStringListToNSMutableArray(const QStringList &list) { - NSMutableArray *result = [NSMutableArray arrayWithCapacity:list.size()]; - for (int i=0; i<list.size(); ++i){ - [result addObject:list[i].toNSString()]; - } + NSMutableArray<NSString *> *result = [NSMutableArray<NSString *> arrayWithCapacity:list.size()]; + for (const QString &string : list) + [result addObject:string.toNSString()]; return result; } @@ -93,6 +91,7 @@ struct dndenum_mapper static dndenum_mapper dnd_enums[] = { { NSDragOperationLink, Qt::LinkAction, true }, { NSDragOperationMove, Qt::MoveAction, true }, + { NSDragOperationDelete, Qt::MoveAction, true }, { NSDragOperationCopy, Qt::CopyAction, true }, { NSDragOperationGeneric, Qt::CopyAction, false }, { NSDragOperationEvery, Qt::ActionMask, false }, @@ -288,7 +287,12 @@ QT_END_NAMESPACE the target-action for the OK/Cancel buttons and making sure the layout is consistent. */ -@implementation QNSPanelContentsWrapper +@implementation QNSPanelContentsWrapper { + NSButton *_okButton; + NSButton *_cancelButton; + NSView *_panelContents; + NSEdgeInsets _panelContentsMargins; +} @synthesize okButton = _okButton; @synthesize cancelButton = _cancelButton; diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index 55b3805df3..a50b9251b7 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -196,7 +196,7 @@ QCocoaIntegration::~QCocoaIntegration() QCocoaApplicationDelegate *delegate = [QCocoaApplicationDelegate sharedDelegate]; [delegate removeAppleEventHandlers]; // reset the application delegate - [[NSApplication sharedApplication] setDelegate: 0]; + [[NSApplication sharedApplication] setDelegate:nil]; } #ifndef QT_NO_CLIPBOARD @@ -225,15 +225,15 @@ QCocoaIntegration::Options QCocoaIntegration::options() const return mOptions; } -Q_LOGGING_CATEGORY(lcCocoaScreen, "qt.qpa.cocoa.screens"); +Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen"); /*! \brief Synchronizes the screen list, adds new screens, removes deleted ones */ void QCocoaIntegration::updateScreens() { - NSArray *scrs = [NSScreen screens]; - NSMutableArray *screens = [NSMutableArray arrayWithArray:scrs]; + NSArray<NSScreen *> *scrs = [NSScreen screens]; + NSMutableArray<NSScreen *> *screens = [NSMutableArray<NSScreen *> arrayWithArray:scrs]; if ([screens count] == 0) if ([NSScreen mainScreen]) [screens addObject:[NSScreen mainScreen]]; @@ -244,7 +244,7 @@ void QCocoaIntegration::updateScreens() uint screenCount = [screens count]; for (uint i = 0; i < screenCount; i++) { NSScreen* scr = [screens objectAtIndex:i]; - CGDirectDisplayID dpy = [[[scr deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue]; + CGDirectDisplayID dpy = scr.qt_displayId; // If this screen is a mirror and is not the primary one of the mirror set, ignore it. // Exception: The NSScreen API has been observed to a return a screen list with one // mirrored, non-primary screen when Qt is running as a startup item. Always use the @@ -266,11 +266,11 @@ void QCocoaIntegration::updateScreens() if (screen) { remainingScreens.remove(screen); screen->updateGeometry(); - qCDebug(lcCocoaScreen) << "Updated properties of" << screen; + qCDebug(lcQpaScreen) << "Updated properties of" << screen; } else { screen = new QCocoaScreen(i); mScreens.append(screen); - qCDebug(lcCocoaScreen) << "Adding" << screen; + qCDebug(lcQpaScreen) << "Adding" << screen; screenAdded(screen); } siblings << screen; @@ -287,7 +287,7 @@ void QCocoaIntegration::updateScreens() mScreens.removeOne(screen); // Prevent stale references to NSScreen during destroy screen->m_screenIndex = -1; - qCDebug(lcCocoaScreen) << "Removing" << screen; + qCDebug(lcQpaScreen) << "Removing" << screen; destroyScreen(screen); } } diff --git a/src/plugins/platforms/cocoa/qcocoamenu.mm b/src/plugins/platforms/cocoa/qcocoamenu.mm index a788ac7d45..49b3e76606 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.mm +++ b/src/plugins/platforms/cocoa/qcocoamenu.mm @@ -278,9 +278,7 @@ void QCocoaMenu::syncSeparatorsCollapsible(bool enable) bool previousIsSeparator = true; // setting to true kills all the separators placed at the top. NSMenuItem *previousItem = nil; - NSArray *itemArray = [m_nativeMenu itemArray]; - for (unsigned int i = 0; i < [itemArray count]; ++i) { - NSMenuItem *item = reinterpret_cast<NSMenuItem *>([itemArray objectAtIndex:i]); + for (NSMenuItem *item in [m_nativeMenu itemArray]) { if ([item isSeparatorItem]) { QCocoaMenuItem *cocoaItem = reinterpret_cast<QCocoaMenuItem *>([item tag]); if (cocoaItem) diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.mm b/src/plugins/platforms/cocoa/qcocoamenuitem.mm index f8f9648822..59e0150168 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuitem.mm +++ b/src/plugins/platforms/cocoa/qcocoamenuitem.mm @@ -331,12 +331,9 @@ NSMenuItem *QCocoaMenuItem::sync() NSFont *customMenuFont = [NSFont fontWithName:m_font.family().toNSString() size:m_font.pointSize()]; if (customMenuFont) { - NSArray *keys = [NSArray arrayWithObjects:NSFontAttributeName, nil]; - NSArray *objects = [NSArray arrayWithObjects:customMenuFont, nil]; - NSDictionary *attributes = [NSDictionary dictionaryWithObjects:objects forKeys:keys]; NSAttributedString *str = [[[NSAttributedString alloc] initWithString:finalString.toNSString() - attributes:attributes] autorelease]; - [m_native setAttributedTitle: str]; + attributes:@{NSFontAttributeName: customMenuFont}] autorelease]; + [m_native setAttributedTitle:str]; useAttributedTitle = true; } } diff --git a/src/plugins/platforms/cocoa/qcocoamenuloader.h b/src/plugins/platforms/cocoa/qcocoamenuloader.h index 95f347646c..2fe669b489 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuloader.h +++ b/src/plugins/platforms/cocoa/qcocoamenuloader.h @@ -55,41 +55,18 @@ #include <QtCore/private/qcore_mac_p.h> @interface QT_MANGLE_NAMESPACE(QCocoaMenuLoader) : NSResponder -{ - IBOutlet NSMenu *theMenu; - IBOutlet NSMenu *appMenu; - IBOutlet NSMenuItem *quitItem; - IBOutlet NSMenuItem *preferencesItem; - IBOutlet NSMenuItem *aboutItem; - IBOutlet NSMenuItem *aboutQtItem; - IBOutlet NSMenuItem *hideItem; - NSMenuItem *lastAppSpecificItem; - NSMenuItem *servicesItem; - NSMenuItem *hideAllOthersItem; - NSMenuItem *showAllItem; -} + (instancetype)sharedMenuLoader; -- (instancetype)init; - (void)ensureAppMenuInMenu:(NSMenu *)menu; - (void)removeActionsFromAppMenu; -- (NSMenu *)applicationMenu; -- (NSMenu *)menu; - (NSMenuItem *)quitMenuItem; - (NSMenuItem *)preferencesMenuItem; - (NSMenuItem *)aboutMenuItem; - (NSMenuItem *)aboutQtMenuItem; - (NSMenuItem *)hideMenuItem; - (NSMenuItem *)appSpecificMenuItem:(NSInteger)tag; -- (IBAction)terminate:(id)sender; -- (IBAction)orderFrontStandardAboutPanel:(id)sender; -- (IBAction)hideOtherApplications:(id)sender; -- (IBAction)unhideAllApplications:(id)sender; -- (IBAction)hide:(id)sender; -- (IBAction)qtDispatcherToQPAMenuItem:(id)sender; -- (void)orderFrontCharacterPalette:(id)sender; -- (BOOL)validateMenuItem:(NSMenuItem*)menuItem; +- (void)qtDispatcherToQPAMenuItem:(id)sender; - (void)qtTranslateApplicationMenu; -- (NSArray *)mergeable; +- (NSArray<NSMenuItem *> *)mergeable; @end QT_NAMESPACE_ALIAS_OBJC_CLASS(QCocoaMenuLoader); diff --git a/src/plugins/platforms/cocoa/qcocoamenuloader.mm b/src/plugins/platforms/cocoa/qcocoamenuloader.mm index cd597da71c..d4d7bc1d7a 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuloader.mm +++ b/src/plugins/platforms/cocoa/qcocoamenuloader.mm @@ -50,7 +50,19 @@ #include <QtCore/qcoreapplication.h> #include <QtGui/private/qguiapplication_p.h> -@implementation QCocoaMenuLoader +@implementation QCocoaMenuLoader { + NSMenu *theMenu; + NSMenu *appMenu; + NSMenuItem *quitItem; + NSMenuItem *preferencesItem; + NSMenuItem *aboutItem; + NSMenuItem *aboutQtItem; + NSMenuItem *hideItem; + NSMenuItem *lastAppSpecificItem; + NSMenuItem *servicesItem; + NSMenuItem *hideAllOthersItem; + NSMenuItem *showAllItem; +} + (instancetype)sharedMenuLoader { @@ -323,7 +335,7 @@ #endif } -- (IBAction)qtDispatcherToQPAMenuItem:(id)sender +- (void)qtDispatcherToQPAMenuItem:(id)sender { NSMenuItem *item = static_cast<NSMenuItem *>(sender); if (item == quitItem) { @@ -363,9 +375,10 @@ } } -- (NSArray*) mergeable +- (NSArray<NSMenuItem *> *)mergeable { - // don't include the quitItem here, since we want it always visible and enabled regardless + // Don't include the quitItem here, since we want it always visible and enabled regardless + // Note that lastAppSpecificItem may be nil, so we can't use @[] here. return [NSArray arrayWithObjects:preferencesItem, aboutItem, aboutQtItem, lastAppSpecificItem, nil]; } diff --git a/src/plugins/platforms/cocoa/qcocoascreen.h b/src/plugins/platforms/cocoa/qcocoascreen.h index 3d59c3de79..850ccaad7a 100644 --- a/src/plugins/platforms/cocoa/qcocoascreen.h +++ b/src/plugins/platforms/cocoa/qcocoascreen.h @@ -104,5 +104,9 @@ QDebug operator<<(QDebug debug, const QCocoaScreen *screen); QT_END_NAMESPACE +@interface NSScreen (QtExtras) +@property(readonly) CGDirectDisplayID qt_displayId; +@end + #endif diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm index a17a02b629..f5408d7c13 100644 --- a/src/plugins/platforms/cocoa/qcocoascreen.mm +++ b/src/plugins/platforms/cocoa/qcocoascreen.mm @@ -66,7 +66,7 @@ QCocoaScreen::~QCocoaScreen() NSScreen *QCocoaScreen::nativeScreen() const { - NSArray *screens = [NSScreen screens]; + NSArray<NSScreen *> *screens = [NSScreen screens]; // Stale reference, screen configuration has changed if (m_screenIndex < 0 || (NSUInteger)m_screenIndex >= [screens count]) @@ -121,8 +121,7 @@ void QCocoaScreen::updateGeometry() m_format = QImage::Format_RGB32; m_depth = NSBitsPerPixelFromDepth([nsScreen depth]); - NSDictionary *devDesc = [nsScreen deviceDescription]; - CGDirectDisplayID dpy = [[devDesc objectForKey:@"NSScreenNumber"] unsignedIntValue]; + CGDirectDisplayID dpy = nsScreen.qt_displayId; CGSize size = CGDisplayScreenSize(dpy); m_physicalSize = QSizeF(size.width, size.height); m_logicalDpi.first = 72; @@ -310,3 +309,12 @@ QDebug operator<<(QDebug debug, const QCocoaScreen *screen) #endif // !QT_NO_DEBUG_STREAM QT_END_NAMESPACE + +@implementation NSScreen (QtExtras) + +- (CGDirectDisplayID)qt_displayId +{ + return [self.deviceDescription[@"NSScreenNumber"] unsignedIntValue]; +} + +@end diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm index 5e6913a89a..d3aafa3785 100644 --- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm +++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm @@ -95,31 +95,16 @@ QT_USE_NAMESPACE @class QT_MANGLE_NAMESPACE(QNSImageView); @interface QT_MANGLE_NAMESPACE(QNSStatusItem) : NSObject <NSUserNotificationCenterDelegate> -{ -@public - QCocoaSystemTrayIcon *systray; - NSStatusItem *item; - QCocoaMenu *menu; - QIcon icon; - QT_MANGLE_NAMESPACE(QNSImageView) *imageCell; -} --(id)initWithSysTray:(QCocoaSystemTrayIcon *)systray; --(void)dealloc; --(NSStatusItem*)item; --(QRectF)geometry; +@property (nonatomic, assign) QCocoaMenu *menu; +@property (nonatomic, assign) QIcon icon; +@property (nonatomic, readonly) NSStatusItem *item; +@property (nonatomic, readonly) QRectF geometry; +- (instancetype)initWithSysTray:(QCocoaSystemTrayIcon *)systray; - (void)triggerSelector:(id)sender button:(Qt::MouseButton)mouseButton; - (void)doubleClickSelector:(id)sender; -- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification; -- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification; @end -@interface QT_MANGLE_NAMESPACE(QNSImageView) : NSImageView { - BOOL down; - QT_MANGLE_NAMESPACE(QNSStatusItem) *parent; -} --(id)initWithParent:(QT_MANGLE_NAMESPACE(QNSStatusItem)*)myParent; --(void)menuTrackingDone:(NSNotification*)notification; --(void)mousePressed:(NSEvent *)mouseEvent button:(Qt::MouseButton)mouseButton; +@interface QT_MANGLE_NAMESPACE(QNSImageView) : NSImageView @end QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSStatusItem); @@ -178,7 +163,7 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon) if (!m_sys) return; - m_sys->item->icon = icon; + m_sys->item.icon = icon; // The reccomended maximum title bar icon height is 18 points // (device independent pixels). The menu height on past and @@ -249,8 +234,8 @@ void QCocoaSystemTrayIcon::updateMenu(QPlatformMenu *menu) if (!m_sys) return; - m_sys->item->menu = static_cast<QCocoaMenu *>(menu); - if (menu && [m_sys->item->menu->nsMenu() numberOfItems] > 0) { + m_sys->item.menu = static_cast<QCocoaMenu *>(menu); + if (menu && [m_sys->item.menu->nsMenu() numberOfItems] > 0) { [[m_sys->item item] setHighlightMode:YES]; } else { [[m_sys->item item] setHighlightMode:NO]; @@ -289,23 +274,29 @@ void QCocoaSystemTrayIcon::showMessage(const QString &title, const QString &mess } QT_END_NAMESPACE -@implementation QNSImageView --(id)initWithParent:(QNSStatusItem*)myParent { +@implementation NSStatusItem (Qt) +@end + +@implementation QNSImageView { + BOOL down; + QT_MANGLE_NAMESPACE(QNSStatusItem) *parent; +} + +- (instancetype)initWithParent:(QNSStatusItem *)myParent { self = [super init]; parent = myParent; down = NO; return self; } --(void)menuTrackingDone:(NSNotification*)notification +- (void)menuTrackingDone:(NSNotification *)__unused notification { - Q_UNUSED(notification); down = NO; [self setNeedsDisplay:YES]; } --(void)mousePressed:(NSEvent *)mouseEvent button:(Qt::MouseButton)mouseButton +- (void)mousePressed:(NSEvent *)mouseEvent button:(Qt::MouseButton)mouseButton { down = YES; int clickCount = [mouseEvent clickCount]; @@ -319,12 +310,12 @@ QT_END_NAMESPACE } } --(void)mouseDown:(NSEvent *)mouseEvent +- (void)mouseDown:(NSEvent *)mouseEvent { [self mousePressed:mouseEvent button:Qt::LeftButton]; } --(void)mouseUp:(NSEvent *)mouseEvent +- (void)mouseUp:(NSEvent *)mouseEvent { Q_UNUSED(mouseEvent); [self menuTrackingDone:nil]; @@ -335,7 +326,7 @@ QT_END_NAMESPACE [self mousePressed:mouseEvent button:Qt::RightButton]; } --(void)rightMouseUp:(NSEvent *)mouseEvent +- (void)rightMouseUp:(NSEvent *)mouseEvent { Q_UNUSED(mouseEvent); [self menuTrackingDone:nil]; @@ -346,21 +337,28 @@ QT_END_NAMESPACE [self mousePressed:mouseEvent button:cocoaButton2QtButton([mouseEvent buttonNumber])]; } --(void)otherMouseUp:(NSEvent *)mouseEvent +- (void)otherMouseUp:(NSEvent *)mouseEvent { Q_UNUSED(mouseEvent); [self menuTrackingDone:nil]; } --(void)drawRect:(NSRect)rect { +- (void)drawRect:(NSRect)rect { [[parent item] drawStatusBarBackgroundInRect:rect withHighlight:down]; [super drawRect:rect]; } @end -@implementation QNSStatusItem +@implementation QNSStatusItem { + QCocoaSystemTrayIcon *systray; + NSStatusItem *item; + QT_MANGLE_NAMESPACE(QNSImageView) *imageCell; +} + +@synthesize menu = menu; +@synthesize icon = icon; --(id)initWithSysTray:(QCocoaSystemTrayIcon *)sys +- (instancetype)initWithSysTray:(QCocoaSystemTrayIcon *)sys { self = [super init]; if (self) { @@ -373,19 +371,19 @@ QT_END_NAMESPACE return self; } --(void)dealloc { +- (void)dealloc { [[NSStatusBar systemStatusBar] removeStatusItem:item]; [[NSNotificationCenter defaultCenter] removeObserver:imageCell]; [imageCell release]; [item release]; [super dealloc]; - } --(NSStatusItem*)item { +- (NSStatusItem *)item { return item; } --(QRectF)geometry { + +- (QRectF)geometry { if (NSWindow *window = [[item view] window]) { if (QCocoaScreen *screen = QCocoaIntegration::instance()->screenForNSScreen([window screen])) return screen->mapFromNative([window frame]); diff --git a/src/plugins/platforms/cocoa/qcocoatheme.mm b/src/plugins/platforms/cocoa/qcocoatheme.mm index 93f0400916..f73db8873b 100644 --- a/src/plugins/platforms/cocoa/qcocoatheme.mm +++ b/src/plugins/platforms/cocoa/qcocoatheme.mm @@ -77,26 +77,24 @@ #include <Carbon/Carbon.h> -@interface QT_MANGLE_NAMESPACE(QCocoaThemeNotificationReceiver) : NSObject { -QCocoaTheme *mPrivate; -} -- (id)initWithPrivate:(QCocoaTheme *)priv; -- (void)systemColorsDidChange:(NSNotification *)notification; +@interface QT_MANGLE_NAMESPACE(QCocoaThemeNotificationReceiver) : NSObject @end QT_NAMESPACE_ALIAS_OBJC_CLASS(QCocoaThemeNotificationReceiver); -@implementation QCocoaThemeNotificationReceiver -- (id)initWithPrivate:(QCocoaTheme *)priv +@implementation QCocoaThemeNotificationReceiver { + QCocoaTheme *mPrivate; +} + +- (instancetype)initWithPrivate:(QCocoaTheme *)priv { - self = [super init]; - mPrivate = priv; + if ((self = [self init])) + mPrivate = priv; return self; } -- (void)systemColorsDidChange:(NSNotification *)notification +- (void)systemColorsDidChange:(NSNotification *)__unused notification { - Q_UNUSED(notification); mPrivate->reset(); QWindowSystemInterface::handleThemeChange(nullptr); } diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h index fb91c53a7a..ec9fb444bd 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.h +++ b/src/plugins/platforms/cocoa/qcocoawindow.h @@ -241,7 +241,6 @@ public: // for QNSView bool m_viewIsEmbedded; // true if the m_view is actually embedded in a "foreign" NSView hiearchy bool m_viewIsToBeEmbedded; // true if the m_view is intended to be embedded in a "foreign" NSView hiearchy - Qt::WindowFlags m_windowFlags; Qt::WindowStates m_lastReportedWindowState; Qt::WindowModality m_windowModality; QPointer<QWindow> m_enterLeaveTargetWindow; diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index f9c676a5b0..e201f1eb04 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -117,24 +117,19 @@ static void qRegisterNotificationCallbacks() return; } - if (lcCocoaNotifications().isDebugEnabled()) { - if (cocoaWindows.isEmpty()) { - qCDebug(lcCocoaNotifications) << "Could not find forwarding target for" << - qPrintable(notificationName) << "from" << notification.object; - } else { - QVector<QCocoaWindow *> debugWindows; - for (QCocoaWindow *cocoaWindow : cocoaWindows) - debugWindows += cocoaWindow; - qCDebug(lcCocoaNotifications) << "Forwarding" << qPrintable(notificationName) << - "to" << debugWindows; - } + if (lcCocoaNotifications().isDebugEnabled() && !cocoaWindows.isEmpty()) { + QVector<QCocoaWindow *> debugWindows; + for (QCocoaWindow *cocoaWindow : cocoaWindows) + debugWindows += cocoaWindow; + qCDebug(lcCocoaNotifications) << "Forwarding" << qPrintable(notificationName) << + "to" << debugWindows; } // FIXME: Could be a foreign window, look up by iterating top level QWindows for (QCocoaWindow *cocoaWindow : cocoaWindows) { if (!method.invoke(cocoaWindow, Qt::DirectConnection)) { - qCWarning(lcQpaCocoaWindow) << "Failed to invoke NSNotification callback for" + qCWarning(lcQpaWindow) << "Failed to invoke NSNotification callback for" << notification.name << "on" << cocoaWindow; } } @@ -173,7 +168,7 @@ QCocoaWindow::QCocoaWindow(QWindow *win, WId nativeHandle) , m_topContentBorderThickness(0) , m_bottomContentBorderThickness(0) { - qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::QCocoaWindow" << window(); + qCDebug(lcQpaWindow) << "QCocoaWindow::QCocoaWindow" << window(); if (nativeHandle) { m_view = reinterpret_cast<NSView *>(nativeHandle); @@ -183,7 +178,7 @@ QCocoaWindow::QCocoaWindow(QWindow *win, WId nativeHandle) void QCocoaWindow::initialize() { - qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::initialize" << window(); + qCDebug(lcQpaWindow) << "QCocoaWindow::initialize" << window(); QMacAutoReleasePool pool; @@ -200,9 +195,6 @@ void QCocoaWindow::initialize() [m_view setWantsBestResolutionOpenGLSurface:enable]; // See also QCocoaGLContext::makeCurrent for software renderer workarounds. } - BOOL enable = qt_mac_resolveOption(NO, window(), "_q_mac_wantsLayer", - "QT_MAC_WANTS_LAYER"); - [m_view setWantsLayer:enable]; } setGeometry(initialGeometry(window(), windowGeometry(), defaultWindowWidth, defaultWindowHeight)); @@ -217,7 +209,7 @@ void QCocoaWindow::initialize() QCocoaWindow::~QCocoaWindow() { - qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::~QCocoaWindow" << window(); + qCDebug(lcQpaWindow) << "QCocoaWindow::~QCocoaWindow" << window(); QMacAutoReleasePool pool; [m_nsWindow makeFirstResponder:nil]; @@ -255,7 +247,7 @@ QSurfaceFormat QCocoaWindow::format() const void QCocoaWindow::setGeometry(const QRect &rectIn) { - qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::setGeometry" << window() << rectIn; + qCDebug(lcQpaWindow) << "QCocoaWindow::setGeometry" << window() << rectIn; QBoolBlocker inSetGeometry(m_inSetGeometry, true); @@ -297,7 +289,7 @@ QRect QCocoaWindow::geometry() const void QCocoaWindow::setCocoaGeometry(const QRect &rect) { - qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::setCocoaGeometry" << window() << rect; + qCDebug(lcQpaWindow) << "QCocoaWindow::setCocoaGeometry" << window() << rect; QMacAutoReleasePool pool; QPlatformWindow::setGeometry(rect); @@ -321,7 +313,7 @@ void QCocoaWindow::setCocoaGeometry(const QRect &rect) void QCocoaWindow::setVisible(bool visible) { - qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::setVisible" << window() << visible; + qCDebug(lcQpaWindow) << "QCocoaWindow::setVisible" << window() << visible; m_inSetVisible = true; @@ -555,46 +547,49 @@ void QCocoaWindow::setWindowZoomButton(Qt::WindowFlags flags) void QCocoaWindow::setWindowFlags(Qt::WindowFlags flags) { - if (isContentView()) { - // While setting style mask we can have handleGeometryChange calls on a content - // view with null geometry, reporting an invalid coordinates as a result. - m_inSetStyleMask = true; - m_view.window.styleMask = windowStyleMask(flags); - m_inSetStyleMask = false; - m_view.window.level = this->windowLevel(flags); - - m_view.window.hasShadow = !(flags & Qt::NoDropShadowWindowHint); - - if (!(flags & Qt::FramelessWindowHint)) - setWindowTitle(window()->title()); - - Qt::WindowType type = window()->type(); - if ((type & Qt::Popup) != Qt::Popup && (type & Qt::Dialog) != Qt::Dialog) { - NSWindowCollectionBehavior behavior = m_view.window.collectionBehavior; - if (flags & Qt::WindowFullscreenButtonHint) { - behavior |= NSWindowCollectionBehaviorFullScreenPrimary; - behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary; - } else { - behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary; - behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary; - } - m_view.window.collectionBehavior = behavior; + if (!isContentView()) + return; + + // While setting style mask we can have handleGeometryChange calls on a content + // view with null geometry, reporting an invalid coordinates as a result. + m_inSetStyleMask = true; + m_view.window.styleMask = windowStyleMask(flags); + m_inSetStyleMask = false; + + Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask)); + if ((type & Qt::Popup) != Qt::Popup && (type & Qt::Dialog) != Qt::Dialog) { + NSWindowCollectionBehavior behavior = m_view.window.collectionBehavior; + if (flags & Qt::WindowFullscreenButtonHint) { + behavior |= NSWindowCollectionBehaviorFullScreenPrimary; + behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary; + } else { + behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary; + behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary; } - setWindowZoomButton(flags); - - // Make window ignore mouse events if WindowTransparentForInput is set. - // Note that ignoresMouseEvents has a special initial state where events - // are ignored (passed through) based on window transparency, and that - // setting the property to false does not return us to that state. Instead, - // this makes the window capture all mouse events. Take care to only - // set the property if needed. FIXME: recreate window if needed or find - // some other way to implement WindowTransparentForInput. - bool ignoreMouse = flags & Qt::WindowTransparentForInput; - if (m_view.window.ignoresMouseEvents != ignoreMouse) - m_view.window.ignoresMouseEvents = ignoreMouse; + m_view.window.collectionBehavior = behavior; } - m_windowFlags = flags; + // Set styleMask and collectionBehavior before applying window level, as + // the window level change will trigger verification of the two properties. + m_view.window.level = this->windowLevel(flags); + + m_view.window.hasShadow = !(flags & Qt::NoDropShadowWindowHint); + + if (!(flags & Qt::FramelessWindowHint)) + setWindowTitle(window()->title()); + + setWindowZoomButton(flags); + + // Make window ignore mouse events if WindowTransparentForInput is set. + // Note that ignoresMouseEvents has a special initial state where events + // are ignored (passed through) based on window transparency, and that + // setting the property to false does not return us to that state. Instead, + // this makes the window capture all mouse events. Take care to only + // set the property if needed. FIXME: recreate window if needed or find + // some other way to implement WindowTransparentForInput. + bool ignoreMouse = flags & Qt::WindowTransparentForInput; + if (m_view.window.ignoresMouseEvents != ignoreMouse) + m_view.window.ignoresMouseEvents = ignoreMouse; } void QCocoaWindow::setWindowState(Qt::WindowStates state) @@ -673,7 +668,7 @@ bool QCocoaWindow::isAlertState() const void QCocoaWindow::raise() { - qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::raise" << window(); + qCDebug(lcQpaWindow) << "QCocoaWindow::raise" << window(); // ### handle spaces (see Qt 4 raise_sys in qwidget_mac.mm) if (!isContentView()) @@ -698,7 +693,7 @@ void QCocoaWindow::raise() void QCocoaWindow::lower() { - qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::lower" << window(); + qCDebug(lcQpaWindow) << "QCocoaWindow::lower" << window(); if (!isContentView()) return; @@ -730,7 +725,7 @@ void QCocoaWindow::propagateSizeHints() if (!isContentView()) return; - qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::propagateSizeHints" << window() + qCDebug(lcQpaWindow) << "QCocoaWindow::propagateSizeHints" << window() << "min:" << windowMinimumSize() << "max:" << windowMaximumSize() << "increment:" << windowSizeIncrement() << "base:" << windowBaseSize(); @@ -747,7 +742,7 @@ void QCocoaWindow::propagateSizeHints() window.contentMaxSize = NSSizeFromCGSize(windowMaximumSize().toCGSize()); // The window may end up with a fixed size; in this case the zoom button should be disabled. - setWindowZoomButton(m_windowFlags); + setWindowZoomButton(this->window()->flags()); // sizeIncrement is observed to take values of (-1, -1) and (0, 0) for windows that should be // resizable and that have no specific size increment set. Cocoa expects (1.0, 1.0) in this case. @@ -764,7 +759,7 @@ void QCocoaWindow::propagateSizeHints() void QCocoaWindow::setOpacity(qreal level) { - qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::setOpacity" << level; + qCDebug(lcQpaWindow) << "QCocoaWindow::setOpacity" << level; if (!isContentView()) return; @@ -773,7 +768,7 @@ void QCocoaWindow::setOpacity(qreal level) void QCocoaWindow::setMask(const QRegion ®ion) { - qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::setMask" << window() << region; + qCDebug(lcQpaWindow) << "QCocoaWindow::setMask" << window() << region; if (m_view.layer) { if (!region.isEmpty()) { @@ -807,7 +802,7 @@ void QCocoaWindow::setMask(const QRegion ®ion) bool QCocoaWindow::setKeyboardGrabEnabled(bool grab) { - qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::setKeyboardGrabEnabled" << window() << grab; + qCDebug(lcQpaWindow) << "QCocoaWindow::setKeyboardGrabEnabled" << window() << grab; if (!isContentView()) return false; @@ -819,7 +814,7 @@ bool QCocoaWindow::setKeyboardGrabEnabled(bool grab) bool QCocoaWindow::setMouseGrabEnabled(bool grab) { - qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::setMouseGrabEnabled" << window() << grab; + qCDebug(lcQpaWindow) << "QCocoaWindow::setMouseGrabEnabled" << window() << grab; if (!isContentView()) return false; @@ -836,7 +831,7 @@ WId QCocoaWindow::winId() const void QCocoaWindow::setParent(const QPlatformWindow *parentWindow) { - qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::setParent" << window() << (parentWindow ? parentWindow->window() : 0); + qCDebug(lcQpaWindow) << "QCocoaWindow::setParent" << window() << (parentWindow ? parentWindow->window() : 0); // recreate the window for compatibility bool unhideAfterRecreate = parentWindow && !m_viewIsToBeEmbedded && ![m_view isHidden]; @@ -1000,7 +995,7 @@ void QCocoaWindow::windowDidEnterFullScreen() "FullScreen category processes window notifications first"); // Reset to original styleMask - setWindowFlags(m_windowFlags); + setWindowFlags(window()->flags()); handleWindowStateChanged(); } @@ -1024,7 +1019,7 @@ void QCocoaWindow::windowDidExitFullScreen() "FullScreen category processes window notifications first"); // Reset to original styleMask - setWindowFlags(m_windowFlags); + setWindowFlags(window()->flags()); Qt::WindowState requestedState = window()->windowState(); @@ -1076,7 +1071,7 @@ void QCocoaWindow::windowWillClose() bool QCocoaWindow::windowShouldClose() { - qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::windowShouldClose" << window(); + qCDebug(lcQpaWindow) << "QCocoaWindow::windowShouldClose" << window(); // This callback should technically only determine if the window // should (be allowed to) close, but since our QPA API to determine // that also involves actually closing the window we do both at the @@ -1123,7 +1118,7 @@ void QCocoaWindow::handleGeometryChange() newGeometry = QRectF::fromCGRect(m_view.frame).toRect(); } - qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::handleGeometryChange" << window() + qCDebug(lcQpaWindow) << "QCocoaWindow::handleGeometryChange" << window() << "current" << geometry() << "new" << newGeometry; QWindowSystemInterface::handleGeometryChange(window(), newGeometry); @@ -1155,7 +1150,7 @@ void QCocoaWindow::handleExposeEvent(const QRegion ®ion) m_exposedRect = QRect(); } - qCDebug(lcQpaCocoaDrawing) << "QCocoaWindow::handleExposeEvent" << window() << region << "isExposed" << isExposed(); + qCDebug(lcQpaDrawing) << "QCocoaWindow::handleExposeEvent" << window() << region << "isExposed" << isExposed(); QWindowSystemInterface::handleExposeEvent<QWindowSystemInterface::SynchronousDelivery>(window(), region); } @@ -1165,7 +1160,7 @@ void QCocoaWindow::handleWindowStateChanged(HandleFlags flags) if (!(flags & HandleUnconditionally) && currentState == m_lastReportedWindowState) return; - qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::handleWindowStateChanged" << + qCDebug(lcQpaWindow) << "QCocoaWindow::handleWindowStateChanged" << m_lastReportedWindowState << "-->" << currentState; QWindowSystemInterface::handleWindowStateChanged<QWindowSystemInterface::SynchronousDelivery>( @@ -1251,7 +1246,7 @@ void QCocoaWindow::recreateWindowIfNeeded() if (isPanel != shouldBePanel) recreateReason |= PanelChanged; - qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::recreateWindowIfNeeded" << window() << recreateReason; + qCDebug(lcQpaWindow) << "QCocoaWindow::recreateWindowIfNeeded" << window() << recreateReason; if (recreateReason == RecreationNotNeeded) return; @@ -1261,9 +1256,9 @@ void QCocoaWindow::recreateWindowIfNeeded() // Remove current window (if any) if ((isContentView() && !shouldBeContentView) || (recreateReason & PanelChanged)) { if (m_nsWindow) { - qCDebug(lcQpaCocoaWindow) << "Getting rid of existing window" << m_nsWindow; + qCDebug(lcQpaWindow) << "Getting rid of existing window" << m_nsWindow; if (m_nsWindow.observationInfo) { - qCCritical(lcQpaCocoaWindow) << m_nsWindow << "has active key-value observers (KVO)!" + qCCritical(lcQpaWindow) << m_nsWindow << "has active key-value observers (KVO)!" << "These will stop working now that the window is recreated, and will result in exceptions" << "when the observers are removed. Break in QCocoaWindow::recreateWindowIfNeeded to debug."; } @@ -1287,7 +1282,7 @@ void QCocoaWindow::recreateWindowIfNeeded() // Move view to new NSWindow if needed if (newWindow) { - qCDebug(lcQpaCocoaWindow) << "Ensuring that" << m_view << "is content view for" << newWindow; + qCDebug(lcQpaWindow) << "Ensuring that" << m_view << "is content view for" << newWindow; [m_view setPostsFrameChangedNotifications:NO]; [newWindow setContentView:m_view]; [m_view setPostsFrameChangedNotifications:YES]; @@ -1333,7 +1328,7 @@ void QCocoaWindow::recreateWindowIfNeeded() void QCocoaWindow::requestUpdate() { - qCDebug(lcQpaCocoaDrawing) << "QCocoaWindow::requestUpdate" << window(); + qCDebug(lcQpaDrawing) << "QCocoaWindow::requestUpdate" << window(); [qnsview_cast(m_view) requestUpdate]; } @@ -1359,7 +1354,7 @@ QCocoaNSWindow *QCocoaWindow::createNSWindow(bool shouldBePanel) } if (!targetScreen) { - qCWarning(lcQpaCocoaWindow) << "Window position outside any known screen, using primary screen"; + qCWarning(lcQpaWindow) << "Window position outside any known screen, using primary screen"; targetScreen = QGuiApplication::primaryScreen(); } diff --git a/src/plugins/platforms/cocoa/qnsview.h b/src/plugins/platforms/cocoa/qnsview.h index e2ea862cd5..5b92624e81 100644 --- a/src/plugins/platforms/cocoa/qnsview.h +++ b/src/plugins/platforms/cocoa/qnsview.h @@ -42,105 +42,45 @@ #include <AppKit/AppKit.h> -#include <QtCore/QPointer> -#include <QtCore/QSet> -#include <QtGui/QImage> -#include <QtGui/QAccessible> - #include "private/qcore_mac_p.h" QT_BEGIN_NAMESPACE class QCocoaWindow; class QCocoaGLContext; +class QPointF; QT_END_NAMESPACE Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper)); -@interface QT_MANGLE_NAMESPACE(QNSView) : NSView <NSTextInputClient> { - QPointer<QCocoaWindow> m_platformWindow; - NSTrackingArea *m_trackingArea; - Qt::MouseButtons m_buttons; - Qt::MouseButtons m_acceptedMouseDowns; - Qt::MouseButtons m_frameStrutButtons; - QString m_composingText; - QPointer<QObject> m_composingFocusObject; - bool m_sendKeyEvent; - QStringList *currentCustomDragTypes; - bool m_dontOverrideCtrlLMB; - bool m_sendUpAsRightButton; - Qt::KeyboardModifiers currentWheelModifiers; -#ifndef QT_NO_OPENGL - QCocoaGLContext *m_glContext; - bool m_shouldSetGLContextinDrawRect; -#endif - NSString *m_inputSource; - QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper) *m_mouseMoveHelper; - bool m_resendKeyEvent; - bool m_scrolling; - bool m_updatingDrag; - NSEvent *m_currentlyInterpretedKeyEvent; - bool m_isMenuView; - QSet<quint32> m_acceptedKeyDowns; - bool m_updateRequested; -} +@interface QT_MANGLE_NAMESPACE(QNSView) : NSView @property (nonatomic, retain) NSCursor *cursor; -- (id)init; -- (id)initWithCocoaWindow:(QCocoaWindow *)platformWindow; -#ifndef QT_NO_OPENGL -- (void)setQCocoaGLContext:(QCocoaGLContext *)context; -#endif -- (void)drawRect:(NSRect)dirtyRect; -- (void)textInputContextKeyboardSelectionDidChangeNotification : (NSNotification *) textInputContextKeyboardSelectionDidChangeNotification; -- (void)viewDidHide; -- (void)removeFromSuperview; -- (void)cancelComposingText; - -- (BOOL)isFlipped; -- (BOOL)acceptsFirstResponder; -- (BOOL)becomeFirstResponder; -- (BOOL)isOpaque; +- (instancetype)initWithCocoaWindow:(QCocoaWindow *)platformWindow; - (void)convertFromScreen:(NSPoint)mouseLocation toWindowPoint:(QPointF *)qtWindowPoint andScreenPoint:(QPointF *)qtScreenPoint; -- (void)resetMouseButtons; +@end +@interface QT_MANGLE_NAMESPACE(QNSView) (DrawingAPI) - (void)requestUpdate; - -- (void)handleMouseEvent:(NSEvent *)theEvent; -- (bool)handleMouseDownEvent:(NSEvent *)theEvent withButton:(int)buttonNumber; -- (bool)handleMouseDraggedEvent:(NSEvent *)theEvent withButton:(int)buttonNumber; -- (bool)handleMouseUpEvent:(NSEvent *)theEvent withButton:(int)buttonNumber; -- (void)mouseDown:(NSEvent *)theEvent; -- (void)mouseDragged:(NSEvent *)theEvent; -- (void)mouseUp:(NSEvent *)theEvent; -- (void)mouseMovedImpl:(NSEvent *)theEvent; -- (void)mouseEnteredImpl:(NSEvent *)theEvent; -- (void)mouseExitedImpl:(NSEvent *)theEvent; -- (void)rightMouseDown:(NSEvent *)theEvent; -- (void)rightMouseDragged:(NSEvent *)theEvent; -- (void)rightMouseUp:(NSEvent *)theEvent; -- (void)otherMouseDown:(NSEvent *)theEvent; -- (void)otherMouseDragged:(NSEvent *)theEvent; -- (void)otherMouseUp:(NSEvent *)theEvent; -- (void)handleFrameStrutMouseEvent:(NSEvent *)theEvent; - -#ifndef QT_NO_TABLETEVENT -- (bool)handleTabletEvent: (NSEvent *)theEvent; -- (void)tabletPoint: (NSEvent *)theEvent; -- (void)tabletProximity: (NSEvent *)theEvent; +#ifndef QT_NO_OPENGL +- (void)setQCocoaGLContext:(QCocoaGLContext *)context; #endif +@end -- (int) convertKeyCode : (QChar)keyCode; -+ (Qt::KeyboardModifiers) convertKeyModifiers : (ulong)modifierFlags; -- (bool)handleKeyEvent:(NSEvent *)theEvent eventType:(int)eventType; -- (void)keyDown:(NSEvent *)theEvent; -- (void)keyUp:(NSEvent *)theEvent; +@interface QT_MANGLE_NAMESPACE(QNSView) (MouseAPI) +- (void)handleFrameStrutMouseEvent:(NSEvent *)theEvent; +- (void)resetMouseButtons; +@end -- (void)registerDragTypes; -- (NSDragOperation)handleDrag:(id <NSDraggingInfo>)sender; +@interface QT_MANGLE_NAMESPACE(QNSView) (KeysAPI) ++ (Qt::KeyboardModifiers)convertKeyModifiers:(ulong)modifierFlags; +@end +@interface QT_MANGLE_NAMESPACE(QNSView) (ComplexTextAPI) +- (void)unmarkText; +- (void)cancelComposingText; @end @interface QT_MANGLE_NAMESPACE(QNSView) (QtExtras) diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index 7b7b3f53d5..7c23c03218 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -51,7 +51,11 @@ #include <qpa/qwindowsysteminterface.h> #include <QtGui/QTextFormat> #include <QtCore/QDebug> +#include <QtCore/QPointer> +#include <QtCore/QSet> #include <QtCore/qsysinfo.h> +#include <QtGui/QAccessible> +#include <QtGui/QImage> #include <private/qguiapplication_p.h> #include <private/qcoregraphics_p.h> #include <private/qwindow_p.h> @@ -65,69 +69,81 @@ #include <accessibilityinspector.h> #endif -Q_LOGGING_CATEGORY(lcQpaTouch, "qt.qpa.input.touch") -#ifndef QT_NO_GESTURES -Q_LOGGING_CATEGORY(lcQpaGestures, "qt.qpa.input.gestures") -#endif -Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") - -@interface QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper) : NSObject -{ - QNSView *view; -} +// Private interface +@interface QT_MANGLE_NAMESPACE(QNSView) () +- (BOOL)isTransparentForUserInput; +@end -- (id)initWithView:(QNSView *)theView; +@interface QT_MANGLE_NAMESPACE(QNSView) (Drawing) <CALayerDelegate> +@end +@interface QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper) : NSObject +- (instancetype)initWithView:(QNSView *)theView; - (void)mouseMoved:(NSEvent *)theEvent; - (void)mouseEntered:(NSEvent *)theEvent; - (void)mouseExited:(NSEvent *)theEvent; - (void)cursorUpdate:(NSEvent *)theEvent; - @end -@implementation QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper) - -- (id)initWithView:(QNSView *)theView -{ - self = [super init]; - if (self) { - view = theView; - } - return self; -} +@interface QT_MANGLE_NAMESPACE(QNSView) (Mouse) +- (NSPoint)screenMousePoint:(NSEvent *)theEvent; +- (void)mouseMovedImpl:(NSEvent *)theEvent; +- (void)mouseEnteredImpl:(NSEvent *)theEvent; +- (void)mouseExitedImpl:(NSEvent *)theEvent; +@end -- (void)mouseMoved:(NSEvent *)theEvent -{ - [view mouseMovedImpl:theEvent]; -} +@interface QT_MANGLE_NAMESPACE(QNSView) (Touch) +@end -- (void)mouseEntered:(NSEvent *)theEvent -{ - [view mouseEnteredImpl:theEvent]; -} +@interface QT_MANGLE_NAMESPACE(QNSView) (Tablet) +- (bool)handleTabletEvent:(NSEvent *)theEvent; +@end -- (void)mouseExited:(NSEvent *)theEvent -{ - [view mouseExitedImpl:theEvent]; -} +@interface QT_MANGLE_NAMESPACE(QNSView) (Gestures) +@end -- (void)cursorUpdate:(NSEvent *)theEvent -{ - [view cursorUpdate:theEvent]; -} +@interface QT_MANGLE_NAMESPACE(QNSView) (Dragging) +-(void)registerDragTypes; +@end +@interface QT_MANGLE_NAMESPACE(QNSView) (Keys) @end -// Private interface -@interface QT_MANGLE_NAMESPACE(QNSView) () -- (BOOL)isTransparentForUserInput; +@interface QT_MANGLE_NAMESPACE(QNSView) (ComplexText) <NSTextInputClient> +- (void)textInputContextKeyboardSelectionDidChangeNotification:(NSNotification *)textInputContextKeyboardSelectionDidChangeNotification; @end -@implementation QT_MANGLE_NAMESPACE(QNSView) +@implementation QT_MANGLE_NAMESPACE(QNSView) { + QPointer<QCocoaWindow> m_platformWindow; + NSTrackingArea *m_trackingArea; + Qt::MouseButtons m_buttons; + Qt::MouseButtons m_acceptedMouseDowns; + Qt::MouseButtons m_frameStrutButtons; + QString m_composingText; + QPointer<QObject> m_composingFocusObject; + bool m_sendKeyEvent; + QStringList *currentCustomDragTypes; + bool m_dontOverrideCtrlLMB; + bool m_sendUpAsRightButton; + Qt::KeyboardModifiers currentWheelModifiers; +#ifndef QT_NO_OPENGL + QCocoaGLContext *m_glContext; + bool m_shouldSetGLContextinDrawRect; +#endif + NSString *m_inputSource; + QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper) *m_mouseMoveHelper; + bool m_resendKeyEvent; + bool m_scrolling; + bool m_updatingDrag; + NSEvent *m_currentlyInterpretedKeyEvent; + bool m_isMenuView; + QSet<quint32> m_acceptedKeyDowns; + bool m_updateRequested; +} -- (id) init +- (instancetype)init { - if (self = [super initWithFrame:NSZeroRect]) { + if ((self = [super initWithFrame:NSZeroRect])) { m_buttons = Qt::NoButton; m_acceptedMouseDowns = Qt::NoButton; m_frameStrutButtons = Qt::NoButton; @@ -168,38 +184,36 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") [super dealloc]; } -- (id)initWithCocoaWindow:(QCocoaWindow *)platformWindow +- (instancetype)initWithCocoaWindow:(QCocoaWindow *)platformWindow { - self = [self init]; - if (!self) - return 0; - - m_platformWindow = platformWindow; - m_sendKeyEvent = false; - m_dontOverrideCtrlLMB = qt_mac_resolveOption(false, platformWindow->window(), "_q_platform_MacDontOverrideCtrlLMB", "QT_MAC_DONT_OVERRIDE_CTRL_LMB"); - m_trackingArea = nil; + if ((self = [self init])) { + m_platformWindow = platformWindow; + m_sendKeyEvent = false; + m_dontOverrideCtrlLMB = qt_mac_resolveOption(false, platformWindow->window(), "_q_platform_MacDontOverrideCtrlLMB", "QT_MAC_DONT_OVERRIDE_CTRL_LMB"); + m_trackingArea = nil; #ifdef QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR - // prevent rift in space-time continuum, disable - // accessibility for the accessibility inspector's windows. - static bool skipAccessibilityForInspectorWindows = false; - if (!skipAccessibilityForInspectorWindows) { + // prevent rift in space-time continuum, disable + // accessibility for the accessibility inspector's windows. + static bool skipAccessibilityForInspectorWindows = false; + if (!skipAccessibilityForInspectorWindows) { - // m_accessibleRoot = window->accessibleRoot(); + // m_accessibleRoot = window->accessibleRoot(); - AccessibilityInspector *inspector = new AccessibilityInspector(window); - skipAccessibilityForInspectorWindows = true; - inspector->inspectWindow(window); - skipAccessibilityForInspectorWindows = false; - } + AccessibilityInspector *inspector = new AccessibilityInspector(window); + skipAccessibilityForInspectorWindows = true; + inspector->inspectWindow(window); + skipAccessibilityForInspectorWindows = false; + } #endif - [self registerDragTypes]; + [self registerDragTypes]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(textInputContextKeyboardSelectionDidChangeNotification:) - name:NSTextInputContextKeyboardSelectionDidChangeNotification - object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(textInputContextKeyboardSelectionDidChangeNotification:) + name:NSTextInputContextKeyboardSelectionDidChangeNotification + object:nil]; + } return self; } @@ -220,18 +234,6 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") return description; } -#ifndef QT_NO_OPENGL -- (void) setQCocoaGLContext:(QCocoaGLContext *)context -{ - m_glContext = context; - [m_glContext->nsOpenGLContext() setView:self]; - if (![m_glContext->nsOpenGLContext() view]) { - //was unable to set view - m_shouldSetGLContextinDrawRect = true; - } -} -#endif - - (void)viewDidMoveToSuperview { if (!m_platformWindow) @@ -268,15 +270,6 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") return focusWindow; } -- (void)textInputContextKeyboardSelectionDidChangeNotification : (NSNotification *) textInputContextKeyboardSelectionDidChangeNotification -{ - Q_UNUSED(textInputContextKeyboardSelectionDidChangeNotification) - if (([NSApp keyWindow] == [self window]) && [[self window] firstResponder] == self) { - QCocoaInputContext *ic = qobject_cast<QCocoaInputContext *>(QCocoaIntegration::instance()->inputContext()); - ic->updateLocale(); - } -} - - (void)viewDidHide { if (!m_platformWindow->isExposed()) @@ -294,107 +287,6 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") [super removeFromSuperview]; } -- (BOOL) isOpaque -{ - if (!m_platformWindow) - return true; - return m_platformWindow->isOpaque(); -} - -- (void)requestUpdate -{ - if (self.needsDisplay) { - // If the view already has needsDisplay set it means that there may be code waiting for - // a real expose event, so we can't issue setNeedsDisplay now as a way to trigger an - // update request. We will re-trigger requestUpdate from drawRect. - return; - } - - [self setNeedsDisplay:YES]; - m_updateRequested = true; -} - -- (void)setNeedsDisplayInRect:(NSRect)rect -{ - [super setNeedsDisplayInRect:rect]; - m_updateRequested = false; -} - -- (void)drawRect:(NSRect)dirtyRect -{ - Q_UNUSED(dirtyRect); - - if (!m_platformWindow) - return; - - QRegion exposedRegion; - const NSRect *dirtyRects; - NSInteger numDirtyRects; - [self getRectsBeingDrawn:&dirtyRects count:&numDirtyRects]; - for (int i = 0; i < numDirtyRects; ++i) - exposedRegion += QRectF::fromCGRect(dirtyRects[i]).toRect(); - - qCDebug(lcQpaCocoaDrawing) << "[QNSView drawRect:]" << m_platformWindow->window() << exposedRegion; - [self updateRegion:exposedRegion]; -} - -- (void)updateRegion:(QRegion)dirtyRegion -{ -#ifndef QT_NO_OPENGL - if (m_glContext && m_shouldSetGLContextinDrawRect) { - [m_glContext->nsOpenGLContext() setView:self]; - m_shouldSetGLContextinDrawRect = false; - } -#endif - - QWindowPrivate *windowPrivate = qt_window_private(m_platformWindow->window()); - - if (m_updateRequested) { - Q_ASSERT(windowPrivate->updateRequestPending); - qCDebug(lcQpaCocoaWindow) << "Delivering update request to" << m_platformWindow->window(); - windowPrivate->deliverUpdateRequest(); - m_updateRequested = false; - } else { - m_platformWindow->handleExposeEvent(dirtyRegion); - } - - if (windowPrivate->updateRequestPending) { - // A call to QWindow::requestUpdate was issued during event delivery above, - // but AppKit will reset the needsDisplay state of the view after completing - // the current display cycle, so we need to defer the request to redisplay. - // FIXME: Perhaps this should be a trigger to enable CADisplayLink? - qCDebug(lcQpaCocoaDrawing) << "[QNSView drawRect:] issuing deferred setNeedsDisplay due to pending update request"; - dispatch_async(dispatch_get_main_queue (), ^{ [self requestUpdate]; }); - } -} - -- (BOOL)wantsUpdateLayer -{ - return YES; -} - -- (void)updateLayer -{ - if (!m_platformWindow) - return; - - qCDebug(lcQpaCocoaDrawing) << "[QNSView updateLayer]" << m_platformWindow->window(); - - // FIXME: Find out if there's a way to resolve the dirty rect like in drawRect: - [self updateRegion:QRectF::fromCGRect(self.bounds).toRect()]; -} - -- (void)viewDidChangeBackingProperties -{ - if (self.layer) - self.layer.contentsScale = self.window.backingScaleFactor; -} - -- (BOOL)isFlipped -{ - return YES; -} - - (BOOL)isTransparentForUserInput { return m_platformWindow->window() && @@ -427,16 +319,6 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") return YES; } -- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent -{ - Q_UNUSED(theEvent) - if (!m_platformWindow) - return NO; - if ([self isTransparentForUserInput]) - return NO; - return YES; -} - - (NSView *)hitTest:(NSPoint)aPoint { NSView *candidate = [super hitTest:aPoint]; @@ -476,1603 +358,18 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") *qtScreenPoint = QCocoaScreen::mapFromNative(mouseLocation); } -- (void)resetMouseButtons -{ - m_buttons = Qt::NoButton; - m_frameStrutButtons = Qt::NoButton; -} - -- (NSPoint) screenMousePoint:(NSEvent *)theEvent -{ - NSPoint screenPoint; - if (theEvent) { - NSPoint windowPoint = [theEvent locationInWindow]; - NSRect screenRect = [[theEvent window] convertRectToScreen:NSMakeRect(windowPoint.x, windowPoint.y, 1, 1)]; - screenPoint = screenRect.origin; - } else { - screenPoint = [NSEvent mouseLocation]; - } - return screenPoint; -} - -- (void)handleMouseEvent:(NSEvent *)theEvent -{ - if (!m_platformWindow) - return; - -#ifndef QT_NO_TABLETEVENT - // Tablet events may come in via the mouse event handlers, - // check if this is a valid tablet event first. - if ([self handleTabletEvent: theEvent]) - return; -#endif - - QPointF qtWindowPoint; - QPointF qtScreenPoint; - QNSView *targetView = self; - if (!targetView.platformWindow) - return; - - // Popups implicitly grap mouse events; forward to the active popup if there is one - if (QCocoaWindow *popup = QCocoaIntegration::instance()->activePopupWindow()) { - // Tooltips must be transparent for mouse events - // The bug reference is QTBUG-46379 - if (!popup->m_windowFlags.testFlag(Qt::ToolTip)) { - if (QNSView *popupView = qnsview_cast(popup->view())) - targetView = popupView; - } - } - - [targetView convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint]; - ulong timestamp = [theEvent timestamp] * 1000; - - QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); - nativeDrag->setLastMouseEvent(theEvent, self); - - Qt::KeyboardModifiers keyboardModifiers = [QNSView convertKeyModifiers:[theEvent modifierFlags]]; - QWindowSystemInterface::handleMouseEvent(targetView->m_platformWindow->window(), timestamp, qtWindowPoint, qtScreenPoint, - m_buttons, keyboardModifiers, Qt::MouseEventNotSynthesized); -} - -- (void)handleFrameStrutMouseEvent:(NSEvent *)theEvent -{ - if (!m_platformWindow) - return; - - // get m_buttons in sync - // Don't send frme strut events if we are in the middle of a mouse drag. - if (m_buttons != Qt::NoButton) - return; - - NSEventType ty = [theEvent type]; - switch (ty) { - case NSLeftMouseDown: - m_frameStrutButtons |= Qt::LeftButton; - break; - case NSLeftMouseUp: - m_frameStrutButtons &= ~Qt::LeftButton; - break; - case NSRightMouseDown: - m_frameStrutButtons |= Qt::RightButton; - break; - case NSLeftMouseDragged: - m_frameStrutButtons |= Qt::LeftButton; - break; - case NSRightMouseDragged: - m_frameStrutButtons |= Qt::RightButton; - break; - case NSRightMouseUp: - m_frameStrutButtons &= ~Qt::RightButton; - break; - case NSOtherMouseDown: - m_frameStrutButtons |= cocoaButton2QtButton([theEvent buttonNumber]); - break; - case NSOtherMouseUp: - m_frameStrutButtons &= ~cocoaButton2QtButton([theEvent buttonNumber]); - default: - break; - } - - NSWindow *window = [self window]; - NSPoint windowPoint = [theEvent locationInWindow]; - - int windowScreenY = [window frame].origin.y + [window frame].size.height; - NSPoint windowCoord = [self convertPoint:[self frame].origin toView:nil]; - int viewScreenY = [window convertRectToScreen:NSMakeRect(windowCoord.x, windowCoord.y, 0, 0)].origin.y; - int titleBarHeight = windowScreenY - viewScreenY; - - NSPoint nsViewPoint = [self convertPoint: windowPoint fromView: nil]; - QPoint qtWindowPoint = QPoint(nsViewPoint.x, titleBarHeight + nsViewPoint.y); - NSPoint screenPoint = [window convertRectToScreen:NSMakeRect(windowPoint.x, windowPoint.y, 0, 0)].origin; - QPoint qtScreenPoint = QCocoaScreen::mapFromNative(screenPoint).toPoint(); - - ulong timestamp = [theEvent timestamp] * 1000; - QWindowSystemInterface::handleFrameStrutMouseEvent(m_platformWindow->window(), timestamp, qtWindowPoint, qtScreenPoint, m_frameStrutButtons); -} - -- (bool)handleMouseDownEvent:(NSEvent *)theEvent withButton:(int)buttonNumber -{ - if ([self isTransparentForUserInput]) - return false; - - Qt::MouseButton button = cocoaButton2QtButton(buttonNumber); - - QPointF qtWindowPoint; - QPointF qtScreenPoint; - [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint]; - Q_UNUSED(qtScreenPoint); - - // Maintain masked state for the button for use by MouseDragged and MouseUp. - QRegion mask = m_platformWindow->window()->mask(); - const bool masked = !mask.isEmpty() && !mask.contains(qtWindowPoint.toPoint()); - if (masked) - m_acceptedMouseDowns &= ~button; - else - m_acceptedMouseDowns |= button; - - // Forward masked out events to the next responder - if (masked) - return false; - - m_buttons |= button; - - [self handleMouseEvent:theEvent]; - return true; -} - -- (bool)handleMouseDraggedEvent:(NSEvent *)theEvent withButton:(int)buttonNumber -{ - if ([self isTransparentForUserInput]) - return false; - - Qt::MouseButton button = cocoaButton2QtButton(buttonNumber); - - // Forward the event to the next responder if Qt did not accept the - // corresponding mouse down for this button - if (!(m_acceptedMouseDowns & button) == button) - return false; - - [self handleMouseEvent:theEvent]; - return true; -} - -- (bool)handleMouseUpEvent:(NSEvent *)theEvent withButton:(int)buttonNumber -{ - if ([self isTransparentForUserInput]) - return false; - - Qt::MouseButton button = cocoaButton2QtButton(buttonNumber); - - // Forward the event to the next responder if Qt did not accept the - // corresponding mouse down for this button - if (!(m_acceptedMouseDowns & button) == button) - return false; - - if (m_sendUpAsRightButton && button == Qt::LeftButton) - button = Qt::RightButton; - if (button == Qt::RightButton) - m_sendUpAsRightButton = false; - - m_buttons &= ~button; - - [self handleMouseEvent:theEvent]; - return true; -} - -- (void)mouseDown:(NSEvent *)theEvent -{ - if ([self isTransparentForUserInput]) - return [super mouseDown:theEvent]; - m_sendUpAsRightButton = false; - - // Handle any active poup windows; clicking outisde them should close them - // all. Don't do anything or clicks inside one of the menus, let Cocoa - // handle that case. Note that in practice many windows of the Qt::Popup type - // will actually close themselves in this case using logic implemented in - // that particular poup type (for example context menus). However, Qt expects - // that plain popup QWindows will also be closed, so we implement the logic - // here as well. - QList<QCocoaWindow *> *popups = QCocoaIntegration::instance()->popupWindowStack(); - if (!popups->isEmpty()) { - // Check if the click is outside all popups. - bool inside = false; - QPointF qtScreenPoint = QCocoaScreen::mapFromNative([self screenMousePoint:theEvent]); - for (QList<QCocoaWindow *>::const_iterator it = popups->begin(); it != popups->end(); ++it) { - if ((*it)->geometry().contains(qtScreenPoint.toPoint())) { - inside = true; - break; - } - } - // Close the popups if the click was outside. - if (!inside) { - Qt::WindowType type = QCocoaIntegration::instance()->activePopupWindow()->window()->type(); - while (QCocoaWindow *popup = QCocoaIntegration::instance()->popPopupWindow()) { - QWindowSystemInterface::handleCloseEvent(popup->window()); - QWindowSystemInterface::flushWindowSystemEvents(); - } - // Consume the mouse event when closing the popup, except for tool tips - // were it's expected that the event is processed normally. - if (type != Qt::ToolTip) - return; - } - } - - QPointF qtWindowPoint; - QPointF qtScreenPoint; - [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint]; - Q_UNUSED(qtScreenPoint); - - QRegion mask = m_platformWindow->window()->mask(); - const bool masked = !mask.isEmpty() && !mask.contains(qtWindowPoint.toPoint()); - // Maintain masked state for the button for use by MouseDragged and Up. - if (masked) - m_acceptedMouseDowns &= ~Qt::LeftButton; - else - m_acceptedMouseDowns |= Qt::LeftButton; - - // Forward masked out events to the next responder - if (masked) { - [super mouseDown:theEvent]; - return; - } - - if ([self hasMarkedText]) { - [[NSTextInputContext currentInputContext] handleEvent:theEvent]; - } else { - auto ctrlOrMetaModifier = qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta) ? Qt::ControlModifier : Qt::MetaModifier; - if (!m_dontOverrideCtrlLMB && [QNSView convertKeyModifiers:[theEvent modifierFlags]] & ctrlOrMetaModifier) { - m_buttons |= Qt::RightButton; - m_sendUpAsRightButton = true; - } else { - m_buttons |= Qt::LeftButton; - } - [self handleMouseEvent:theEvent]; - } -} - -- (void)mouseDragged:(NSEvent *)theEvent -{ - const bool accepted = [self handleMouseDraggedEvent:theEvent withButton:[theEvent buttonNumber]]; - if (!accepted) - [super mouseDragged:theEvent]; -} - -- (void)mouseUp:(NSEvent *)theEvent -{ - const bool accepted = [self handleMouseUpEvent:theEvent withButton:[theEvent buttonNumber]]; - if (!accepted) - [super mouseUp:theEvent]; -} - -- (void)rightMouseDown:(NSEvent *)theEvent -{ - // Wacom tablet might not return the correct button number for NSEvent buttonNumber - // on right clicks. Decide here that the button is the "right" button and forward - // the button number to the mouse (and tablet) handler. - const bool accepted = [self handleMouseDownEvent:theEvent withButton:1]; - if (!accepted) - [super rightMouseDown:theEvent]; -} - -- (void)rightMouseDragged:(NSEvent *)theEvent -{ - const bool accepted = [self handleMouseDraggedEvent:theEvent withButton:1]; - if (!accepted) - [super rightMouseDragged:theEvent]; -} - -- (void)rightMouseUp:(NSEvent *)theEvent -{ - const bool accepted = [self handleMouseUpEvent:theEvent withButton:1]; - if (!accepted) - [super rightMouseUp:theEvent]; -} - -- (void)otherMouseDown:(NSEvent *)theEvent -{ - const bool accepted = [self handleMouseDownEvent:theEvent withButton:[theEvent buttonNumber]]; - if (!accepted) - [super otherMouseDown:theEvent]; -} - -- (void)otherMouseDragged:(NSEvent *)theEvent -{ - const bool accepted = [self handleMouseDraggedEvent:theEvent withButton:[theEvent buttonNumber]]; - if (!accepted) - [super otherMouseDragged:theEvent]; -} - -- (void)otherMouseUp:(NSEvent *)theEvent -{ - const bool accepted = [self handleMouseUpEvent:theEvent withButton:[theEvent buttonNumber]]; - if (!accepted) - [super otherMouseUp:theEvent]; -} - -- (void)updateTrackingAreas -{ - [super updateTrackingAreas]; - - QMacAutoReleasePool pool; - - // NSTrackingInVisibleRect keeps care of updating once the tracking is set up, so bail out early - if (m_trackingArea && [[self trackingAreas] containsObject:m_trackingArea]) - return; - - // Ideally, we shouldn't have NSTrackingMouseMoved events included below, it should - // only be turned on if mouseTracking, hover is on or a tool tip is set. - // Unfortunately, Qt will send "tooltip" events on mouse moves, so we need to - // turn it on in ALL case. That means EVERY QWindow gets to pay the cost of - // mouse moves delivered to it (Apple recommends keeping it OFF because there - // is a performance hit). So it goes. - NSUInteger trackingOptions = NSTrackingMouseEnteredAndExited | NSTrackingActiveInActiveApp - | NSTrackingInVisibleRect | NSTrackingMouseMoved | NSTrackingCursorUpdate; - [m_trackingArea release]; - m_trackingArea = [[NSTrackingArea alloc] initWithRect:[self frame] - options:trackingOptions - owner:m_mouseMoveHelper - userInfo:nil]; - [self addTrackingArea:m_trackingArea]; -} - -- (void)cursorUpdate:(NSEvent *)theEvent -{ - qCDebug(lcQpaCocoaMouse) << "[QNSView cursorUpdate:]" << self.cursor; - - // Note: We do not get this callback when moving from a subview that - // uses the legacy cursorRect API, so the cursor is reset to the arrow - // cursor. See rdar://34183708 - - if (self.cursor) - [self.cursor set]; - else - [super cursorUpdate:theEvent]; -} - -- (void)mouseMovedImpl:(NSEvent *)theEvent -{ - if (!m_platformWindow) - return; - - if ([self isTransparentForUserInput]) - return; - - QPointF windowPoint; - QPointF screenPoint; - [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; - QWindow *childWindow = m_platformWindow->childWindowAt(windowPoint.toPoint()); - - // Top-level windows generate enter-leave events for sub-windows. - // Qt wants to know which window (if any) will be entered at the - // the time of the leave. This is dificult to accomplish by - // handling mouseEnter and mouseLeave envents, since they are sent - // individually to different views. - if (m_platformWindow->isContentView() && childWindow) { - if (childWindow != m_platformWindow->m_enterLeaveTargetWindow) { - QWindowSystemInterface::handleEnterLeaveEvent(childWindow, m_platformWindow->m_enterLeaveTargetWindow, windowPoint, screenPoint); - m_platformWindow->m_enterLeaveTargetWindow = childWindow; - } - } - - // Cocoa keeps firing mouse move events for obscured parent views. Qt should not - // send those events so filter them out here. - if (childWindow != m_platformWindow->window()) - return; - - [self handleMouseEvent: theEvent]; -} - -- (void)mouseEnteredImpl:(NSEvent *)theEvent -{ - Q_UNUSED(theEvent) - if (!m_platformWindow) - return; - - m_platformWindow->m_windowUnderMouse = true; - - if ([self isTransparentForUserInput]) - return; - - // Top-level windows generate enter events for sub-windows. - if (!m_platformWindow->isContentView()) - return; - - QPointF windowPoint; - QPointF screenPoint; - [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; - m_platformWindow->m_enterLeaveTargetWindow = m_platformWindow->childWindowAt(windowPoint.toPoint()); - QWindowSystemInterface::handleEnterEvent(m_platformWindow->m_enterLeaveTargetWindow, windowPoint, screenPoint); -} - -- (void)mouseExitedImpl:(NSEvent *)theEvent -{ - Q_UNUSED(theEvent); - if (!m_platformWindow) - return; - - m_platformWindow->m_windowUnderMouse = false; - - if ([self isTransparentForUserInput]) - return; - - // Top-level windows generate leave events for sub-windows. - if (!m_platformWindow->isContentView()) - return; - - QWindowSystemInterface::handleLeaveEvent(m_platformWindow->m_enterLeaveTargetWindow); - m_platformWindow->m_enterLeaveTargetWindow = 0; -} - -#ifndef QT_NO_TABLETEVENT -struct QCocoaTabletDeviceData -{ - QTabletEvent::TabletDevice device; - QTabletEvent::PointerType pointerType; - uint capabilityMask; - qint64 uid; -}; - -typedef QHash<uint, QCocoaTabletDeviceData> QCocoaTabletDeviceDataHash; -Q_GLOBAL_STATIC(QCocoaTabletDeviceDataHash, tabletDeviceDataHash) - -- (bool)handleTabletEvent: (NSEvent *)theEvent -{ - static bool ignoreButtonMapping = qEnvironmentVariableIsSet("QT_MAC_TABLET_IGNORE_BUTTON_MAPPING"); - - if (!m_platformWindow) - return false; - - NSEventType eventType = [theEvent type]; - if (eventType != NSTabletPoint && [theEvent subtype] != NSTabletPointEventSubtype) - return false; // Not a tablet event. - - ulong timestamp = [theEvent timestamp] * 1000; - - QPointF windowPoint; - QPointF screenPoint; - [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint: &windowPoint andScreenPoint: &screenPoint]; - - uint deviceId = [theEvent deviceID]; - if (!tabletDeviceDataHash->contains(deviceId)) { - // Error: Unknown tablet device. Qt also gets into this state - // when running on a VM. This appears to be harmless; don't - // print a warning. - return false; - } - const QCocoaTabletDeviceData &deviceData = tabletDeviceDataHash->value(deviceId); - - bool down = (eventType != NSMouseMoved); - - qreal pressure; - if (down) { - pressure = [theEvent pressure]; - } else { - pressure = 0.0; - } - - NSPoint tilt = [theEvent tilt]; - int xTilt = qRound(tilt.x * 60.0); - int yTilt = qRound(tilt.y * -60.0); - qreal tangentialPressure = 0; - qreal rotation = 0; - int z = 0; - if (deviceData.capabilityMask & 0x0200) - z = [theEvent absoluteZ]; - - if (deviceData.capabilityMask & 0x0800) - tangentialPressure = ([theEvent tangentialPressure] * 2.0) - 1.0; - - rotation = 360.0 - [theEvent rotation]; - if (rotation > 180.0) - rotation -= 360.0; - - Qt::KeyboardModifiers keyboardModifiers = [QNSView convertKeyModifiers:[theEvent modifierFlags]]; - Qt::MouseButtons buttons = ignoreButtonMapping ? static_cast<Qt::MouseButtons>(static_cast<uint>([theEvent buttonMask])) : m_buttons; - - qCDebug(lcQpaTablet, "event on tablet %d with tool %d type %d unique ID %lld pos %6.1f, %6.1f root pos %6.1f, %6.1f buttons 0x%x pressure %4.2lf tilt %d, %d rotation %6.2lf", - deviceId, deviceData.device, deviceData.pointerType, deviceData.uid, - windowPoint.x(), windowPoint.y(), screenPoint.x(), screenPoint.y(), - static_cast<uint>(buttons), pressure, xTilt, yTilt, rotation); - - QWindowSystemInterface::handleTabletEvent(m_platformWindow->window(), timestamp, windowPoint, screenPoint, - deviceData.device, deviceData.pointerType, buttons, pressure, xTilt, yTilt, - tangentialPressure, rotation, z, deviceData.uid, - keyboardModifiers); - return true; -} - -- (void)tabletPoint:(NSEvent *)theEvent -{ - if ([self isTransparentForUserInput]) - return [super tabletPoint:theEvent]; - - [self handleTabletEvent: theEvent]; -} - -static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) -{ - qint64 uid = [theEvent uniqueID]; - uint bits = [theEvent vendorPointingDeviceType]; - if (bits == 0 && uid != 0) { - // Fallback. It seems that the driver doesn't always include all the information. - // High-End Wacom devices store their "type" in the uper bits of the Unique ID. - // I'm not sure how to handle it for consumer devices, but I'll test that in a bit. - bits = uid >> 32; - } - - QTabletEvent::TabletDevice device; - // Defined in the "EN0056-NxtGenImpGuideX" - // on Wacom's Developer Website (www.wacomeng.com) - if (((bits & 0x0006) == 0x0002) && ((bits & 0x0F06) != 0x0902)) { - device = QTabletEvent::Stylus; - } else { - switch (bits & 0x0F06) { - case 0x0802: - device = QTabletEvent::Stylus; - break; - case 0x0902: - device = QTabletEvent::Airbrush; - break; - case 0x0004: - device = QTabletEvent::FourDMouse; - break; - case 0x0006: - device = QTabletEvent::Puck; - break; - case 0x0804: - device = QTabletEvent::RotationStylus; - break; - default: - device = QTabletEvent::NoDevice; - } - } - return device; -} - -- (void)tabletProximity:(NSEvent *)theEvent -{ - if ([self isTransparentForUserInput]) - return [super tabletProximity:theEvent]; - - ulong timestamp = [theEvent timestamp] * 1000; - - QCocoaTabletDeviceData deviceData; - deviceData.uid = [theEvent uniqueID]; - deviceData.capabilityMask = [theEvent capabilityMask]; - - switch ([theEvent pointingDeviceType]) { - case NSUnknownPointingDevice: - default: - deviceData.pointerType = QTabletEvent::UnknownPointer; - break; - case NSPenPointingDevice: - deviceData.pointerType = QTabletEvent::Pen; - break; - case NSCursorPointingDevice: - deviceData.pointerType = QTabletEvent::Cursor; - break; - case NSEraserPointingDevice: - deviceData.pointerType = QTabletEvent::Eraser; - break; - } - - deviceData.device = wacomTabletDevice(theEvent); - - // The deviceID is "unique" while in the proximity, it's a key that we can use for - // linking up QCocoaTabletDeviceData to an event (especially if there are two devices in action). - bool entering = [theEvent isEnteringProximity]; - uint deviceId = [theEvent deviceID]; - if (entering) { - tabletDeviceDataHash->insert(deviceId, deviceData); - } else { - tabletDeviceDataHash->remove(deviceId); - } - - qCDebug(lcQpaTablet, "proximity change on tablet %d: current tool %d type %d unique ID %lld", - deviceId, deviceData.device, deviceData.pointerType, deviceData.uid); - - if (entering) { - QWindowSystemInterface::handleTabletEnterProximityEvent(timestamp, deviceData.device, deviceData.pointerType, deviceData.uid); - } else { - QWindowSystemInterface::handleTabletLeaveProximityEvent(timestamp, deviceData.device, deviceData.pointerType, deviceData.uid); - } -} -#endif - -- (bool)shouldSendSingleTouch -{ - if (!m_platformWindow) - return true; - - // QtWidgets expects single-point touch events, QtDeclarative does not. - // Until there is an API we solve this by looking at the window class type. - return m_platformWindow->window()->inherits("QWidgetWindow"); -} - -- (void)touchesBeganWithEvent:(NSEvent *)event -{ - if (!m_platformWindow) - return; - - const NSTimeInterval timestamp = [event timestamp]; - const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, [self shouldSendSingleTouch]); - qCDebug(lcQpaTouch) << "touchesBeganWithEvent" << points << "from device" << hex << [event deviceID]; - QWindowSystemInterface::handleTouchEvent(m_platformWindow->window(), timestamp * 1000, QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), points); -} - -- (void)touchesMovedWithEvent:(NSEvent *)event -{ - if (!m_platformWindow) - return; - - const NSTimeInterval timestamp = [event timestamp]; - const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, [self shouldSendSingleTouch]); - qCDebug(lcQpaTouch) << "touchesMovedWithEvent" << points << "from device" << hex << [event deviceID]; - QWindowSystemInterface::handleTouchEvent(m_platformWindow->window(), timestamp * 1000, QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), points); -} - -- (void)touchesEndedWithEvent:(NSEvent *)event -{ - if (!m_platformWindow) - return; - - const NSTimeInterval timestamp = [event timestamp]; - const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, [self shouldSendSingleTouch]); - qCDebug(lcQpaTouch) << "touchesEndedWithEvent" << points << "from device" << hex << [event deviceID]; - QWindowSystemInterface::handleTouchEvent(m_platformWindow->window(), timestamp * 1000, QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), points); -} - -- (void)touchesCancelledWithEvent:(NSEvent *)event -{ - if (!m_platformWindow) - return; - - const NSTimeInterval timestamp = [event timestamp]; - const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, [self shouldSendSingleTouch]); - qCDebug(lcQpaTouch) << "touchesCancelledWithEvent" << points << "from device" << hex << [event deviceID]; - QWindowSystemInterface::handleTouchEvent(m_platformWindow->window(), timestamp * 1000, QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), points); -} - -#ifndef QT_NO_GESTURES - -- (bool)handleGestureAsBeginEnd:(NSEvent *)event -{ - if (QOperatingSystemVersion::current() < QOperatingSystemVersion::OSXElCapitan) - return false; - - if ([event phase] == NSEventPhaseBegan) { - [self beginGestureWithEvent:event]; - return true; - } - - if ([event phase] == NSEventPhaseEnded) { - [self endGestureWithEvent:event]; - return true; - } - - return false; -} -- (void)magnifyWithEvent:(NSEvent *)event -{ - if (!m_platformWindow) - return; - - if ([self handleGestureAsBeginEnd:event]) - return; - - qCDebug(lcQpaGestures) << "magnifyWithEvent" << [event magnification] << "from device" << hex << [event deviceID]; - const NSTimeInterval timestamp = [event timestamp]; - QPointF windowPoint; - QPointF screenPoint; - [self convertFromScreen:[self screenMousePoint:event] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; - QWindowSystemInterface::handleGestureEventWithRealValue(m_platformWindow->window(), QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), timestamp, Qt::ZoomNativeGesture, - [event magnification], windowPoint, screenPoint); -} - -- (void)smartMagnifyWithEvent:(NSEvent *)event -{ - if (!m_platformWindow) - return; - - static bool zoomIn = true; - qCDebug(lcQpaGestures) << "smartMagnifyWithEvent" << zoomIn << "from device" << hex << [event deviceID]; - const NSTimeInterval timestamp = [event timestamp]; - QPointF windowPoint; - QPointF screenPoint; - [self convertFromScreen:[self screenMousePoint:event] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; - QWindowSystemInterface::handleGestureEventWithRealValue(m_platformWindow->window(), QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), timestamp, Qt::SmartZoomNativeGesture, - zoomIn ? 1.0f : 0.0f, windowPoint, screenPoint); - zoomIn = !zoomIn; -} - -- (void)rotateWithEvent:(NSEvent *)event -{ - if (!m_platformWindow) - return; - - if ([self handleGestureAsBeginEnd:event]) - return; - - const NSTimeInterval timestamp = [event timestamp]; - QPointF windowPoint; - QPointF screenPoint; - [self convertFromScreen:[self screenMousePoint:event] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; - QWindowSystemInterface::handleGestureEventWithRealValue(m_platformWindow->window(), QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), timestamp, Qt::RotateNativeGesture, - -[event rotation], windowPoint, screenPoint); -} - -- (void)swipeWithEvent:(NSEvent *)event -{ - if (!m_platformWindow) - return; - - qCDebug(lcQpaGestures) << "swipeWithEvent" << [event deltaX] << [event deltaY] << "from device" << hex << [event deviceID]; - const NSTimeInterval timestamp = [event timestamp]; - QPointF windowPoint; - QPointF screenPoint; - [self convertFromScreen:[self screenMousePoint:event] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; - - qreal angle = 0.0f; - if ([event deltaX] == 1) - angle = 180.0f; - else if ([event deltaX] == -1) - angle = 0.0f; - else if ([event deltaY] == 1) - angle = 90.0f; - else if ([event deltaY] == -1) - angle = 270.0f; - - QWindowSystemInterface::handleGestureEventWithRealValue(m_platformWindow->window(), QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), timestamp, Qt::SwipeNativeGesture, - angle, windowPoint, screenPoint); -} - -- (void)beginGestureWithEvent:(NSEvent *)event -{ - if (!m_platformWindow) - return; - - const NSTimeInterval timestamp = [event timestamp]; - QPointF windowPoint; - QPointF screenPoint; - [self convertFromScreen:[self screenMousePoint:event] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; - qCDebug(lcQpaGestures) << "beginGestureWithEvent @" << windowPoint << "from device" << hex << [event deviceID]; - QWindowSystemInterface::handleGestureEvent(m_platformWindow->window(), QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), timestamp, Qt::BeginNativeGesture, - windowPoint, screenPoint); -} - -- (void)endGestureWithEvent:(NSEvent *)event -{ - if (!m_platformWindow) - return; - - qCDebug(lcQpaGestures) << "endGestureWithEvent" << "from device" << hex << [event deviceID]; - const NSTimeInterval timestamp = [event timestamp]; - QPointF windowPoint; - QPointF screenPoint; - [self convertFromScreen:[self screenMousePoint:event] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; - QWindowSystemInterface::handleGestureEvent(m_platformWindow->window(), QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), timestamp, Qt::EndNativeGesture, - windowPoint, screenPoint); -} -#endif // QT_NO_GESTURES - -#if QT_CONFIG(wheelevent) -- (void)scrollWheel:(NSEvent *)theEvent -{ - if (!m_platformWindow) - return; - - if ([self isTransparentForUserInput]) - return [super scrollWheel:theEvent]; - - QPoint angleDelta; - Qt::MouseEventSource source = Qt::MouseEventNotSynthesized; - if ([theEvent hasPreciseScrollingDeltas]) { - // The mouse device contains pixel scroll wheel support (Mighty Mouse, Trackpad). - // Since deviceDelta is delivered as pixels rather than degrees, we need to - // convert from pixels to degrees in a sensible manner. - // It looks like 1/4 degrees per pixel behaves most native. - // (NB: Qt expects the unit for delta to be 8 per degree): - const int pixelsToDegrees = 2; // 8 * 1/4 - angleDelta.setX([theEvent scrollingDeltaX] * pixelsToDegrees); - angleDelta.setY([theEvent scrollingDeltaY] * pixelsToDegrees); - source = Qt::MouseEventSynthesizedBySystem; - } else { - // Remove acceleration, and use either -120 or 120 as delta: - angleDelta.setX(qBound(-120, int([theEvent deltaX] * 10000), 120)); - angleDelta.setY(qBound(-120, int([theEvent deltaY] * 10000), 120)); - } - - QPoint pixelDelta; - if ([theEvent hasPreciseScrollingDeltas]) { - pixelDelta.setX([theEvent scrollingDeltaX]); - pixelDelta.setY([theEvent scrollingDeltaY]); - } else { - // docs: "In the case of !hasPreciseScrollingDeltas, multiply the delta with the line width." - // scrollingDeltaX seems to return a minimum value of 0.1 in this case, map that to two pixels. - const CGFloat lineWithEstimate = 20.0; - pixelDelta.setX([theEvent scrollingDeltaX] * lineWithEstimate); - pixelDelta.setY([theEvent scrollingDeltaY] * lineWithEstimate); - } - - QPointF qt_windowPoint; - QPointF qt_screenPoint; - [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qt_windowPoint andScreenPoint:&qt_screenPoint]; - NSTimeInterval timestamp = [theEvent timestamp]; - ulong qt_timestamp = timestamp * 1000; - - // Prevent keyboard modifier state from changing during scroll event streams. - // A two-finger trackpad flick generates a stream of scroll events. We want - // the keyboard modifier state to be the state at the beginning of the - // flick in order to avoid changing the interpretation of the events - // mid-stream. One example of this happening would be when pressing cmd - // after scrolling in Qt Creator: not taking the phase into account causes - // the end of the event stream to be interpreted as font size changes. - NSEventPhase momentumPhase = [theEvent momentumPhase]; - if (momentumPhase == NSEventPhaseNone) { - currentWheelModifiers = [QNSView convertKeyModifiers:[theEvent modifierFlags]]; - } - - NSEventPhase phase = [theEvent phase]; - Qt::ScrollPhase ph = Qt::ScrollUpdate; - - // MayBegin is likely to happen. We treat it the same as an actual begin. - if (phase == NSEventPhaseMayBegin) { - m_scrolling = true; - ph = Qt::ScrollBegin; - } else if (phase == NSEventPhaseBegan) { - // If MayBegin did not happen, Began is the actual beginning. - if (!m_scrolling) - ph = Qt::ScrollBegin; - m_scrolling = true; - } else if (phase == NSEventPhaseEnded || phase == NSEventPhaseCancelled || - momentumPhase == NSEventPhaseEnded || momentumPhase == NSEventPhaseCancelled) { - ph = Qt::ScrollEnd; - m_scrolling = false; - } else if (phase == NSEventPhaseNone && momentumPhase == NSEventPhaseNone) { - ph = Qt::NoScrollPhase; - } - // "isInverted": natural OS X scrolling, inverted from the Qt/other platform/Jens perspective. - bool isInverted = [theEvent isDirectionInvertedFromDevice]; - - qCDebug(lcQpaCocoaMouse) << "scroll wheel @ window pos" << qt_windowPoint << "delta px" << pixelDelta << "angle" << angleDelta << "phase" << ph << (isInverted ? "inverted" : ""); - QWindowSystemInterface::handleWheelEvent(m_platformWindow->window(), qt_timestamp, qt_windowPoint, qt_screenPoint, pixelDelta, angleDelta, currentWheelModifiers, ph, source, isInverted); -} -#endif // QT_CONFIG(wheelevent) - -- (int) convertKeyCode : (QChar)keyChar -{ - return qt_mac_cocoaKey2QtKey(keyChar); -} - -+ (Qt::KeyboardModifiers) convertKeyModifiers : (ulong)modifierFlags -{ - const bool dontSwapCtrlAndMeta = qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta); - Qt::KeyboardModifiers qtMods =Qt::NoModifier; - if (modifierFlags & NSShiftKeyMask) - qtMods |= Qt::ShiftModifier; - if (modifierFlags & NSControlKeyMask) - qtMods |= dontSwapCtrlAndMeta ? Qt::ControlModifier : Qt::MetaModifier; - if (modifierFlags & NSAlternateKeyMask) - qtMods |= Qt::AltModifier; - if (modifierFlags & NSCommandKeyMask) - qtMods |= dontSwapCtrlAndMeta ? Qt::MetaModifier : Qt::ControlModifier; - if (modifierFlags & NSNumericPadKeyMask) - qtMods |= Qt::KeypadModifier; - return qtMods; -} - -- (bool)handleKeyEvent:(NSEvent *)nsevent eventType:(int)eventType -{ - ulong timestamp = [nsevent timestamp] * 1000; - ulong nativeModifiers = [nsevent modifierFlags]; - Qt::KeyboardModifiers modifiers = [QNSView convertKeyModifiers: nativeModifiers]; - NSString *charactersIgnoringModifiers = [nsevent charactersIgnoringModifiers]; - NSString *characters = [nsevent characters]; - if (m_inputSource != characters) { - [m_inputSource release]; - m_inputSource = [characters retain]; - } - - // There is no way to get the scan code from carbon/cocoa. But we cannot - // use the value 0, since it indicates that the event originates from somewhere - // else than the keyboard. - quint32 nativeScanCode = 1; - quint32 nativeVirtualKey = [nsevent keyCode]; - - QChar ch = QChar::ReplacementCharacter; - int keyCode = Qt::Key_unknown; - - // If a dead key occurs as a result of pressing a key combination then - // characters will have 0 length, but charactersIgnoringModifiers will - // have a valid character in it. This enables key combinations such as - // ALT+E to be used as a shortcut with an English keyboard even though - // pressing ALT+E will give a dead key while doing normal text input. - if ([characters length] != 0 || [charactersIgnoringModifiers length] != 0) { - auto ctrlOrMetaModifier = qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta) ? Qt::ControlModifier : Qt::MetaModifier; - if (((modifiers & ctrlOrMetaModifier) || (modifiers & Qt::AltModifier)) && ([charactersIgnoringModifiers length] != 0)) - ch = QChar([charactersIgnoringModifiers characterAtIndex:0]); - else if ([characters length] != 0) - ch = QChar([characters characterAtIndex:0]); - keyCode = [self convertKeyCode:ch]; - } - - // we will send a key event unless the input method sets m_sendKeyEvent to false - m_sendKeyEvent = true; - QString text; - // ignore text for the U+F700-U+F8FF range. This is used by Cocoa when - // delivering function keys (e.g. arrow keys, backspace, F1-F35, etc.) - if (!(modifiers & (Qt::ControlModifier | Qt::MetaModifier)) && (ch.unicode() < 0xf700 || ch.unicode() > 0xf8ff)) - text = QString::fromNSString(characters); - - QWindow *window = [self topLevelWindow]; - - // Popups implicitly grab key events; forward to the active popup if there is one. - // This allows popups to e.g. intercept shortcuts and close the popup in response. - if (QCocoaWindow *popup = QCocoaIntegration::instance()->activePopupWindow()) { - if (!popup->m_windowFlags.testFlag(Qt::ToolTip)) - window = popup->window(); - } - - if (eventType == QEvent::KeyPress) { - - if (m_composingText.isEmpty()) { - m_sendKeyEvent = !QWindowSystemInterface::handleShortcutEvent(window, timestamp, keyCode, - modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers, text, [nsevent isARepeat], 1); - - // Handling a shortcut may result in closing the window - if (!m_platformWindow) - return true; - } - - QObject *fo = m_platformWindow->window()->focusObject(); - if (m_sendKeyEvent && fo) { - QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImHints); - if (QCoreApplication::sendEvent(fo, &queryEvent)) { - bool imEnabled = queryEvent.value(Qt::ImEnabled).toBool(); - Qt::InputMethodHints hints = static_cast<Qt::InputMethodHints>(queryEvent.value(Qt::ImHints).toUInt()); - if (imEnabled && !(hints & Qt::ImhDigitsOnly || hints & Qt::ImhFormattedNumbersOnly || hints & Qt::ImhHiddenText)) { - // pass the key event to the input method. note that m_sendKeyEvent may be set to false during this call - m_currentlyInterpretedKeyEvent = nsevent; - [self interpretKeyEvents:[NSArray arrayWithObject:nsevent]]; - m_currentlyInterpretedKeyEvent = 0; - } - } - } - if (m_resendKeyEvent) - m_sendKeyEvent = true; - } - - bool accepted = true; - if (m_sendKeyEvent && m_composingText.isEmpty()) { - QWindowSystemInterface::handleExtendedKeyEvent(window, timestamp, QEvent::Type(eventType), keyCode, modifiers, - nativeScanCode, nativeVirtualKey, nativeModifiers, text, [nsevent isARepeat], 1, false); - accepted = QWindowSystemInterface::flushWindowSystemEvents(); - } - m_sendKeyEvent = false; - m_resendKeyEvent = false; - return accepted; -} - -- (void)keyDown:(NSEvent *)nsevent -{ - if ([self isTransparentForUserInput]) - return [super keyDown:nsevent]; - - const bool accepted = [self handleKeyEvent:nsevent eventType:int(QEvent::KeyPress)]; - - // When Qt is used to implement a plugin for a native application we - // want to propagate unhandled events to other native views. However, - // Qt does not always set the accepted state correctly (in particular - // for return key events), so do this for plugin applications only - // to prevent incorrect forwarding in the general case. - const bool shouldPropagate = QCoreApplication::testAttribute(Qt::AA_PluginApplication) && !accepted; - - // Track keyDown acceptance/forward state for later acceptance of the keyUp. - if (!shouldPropagate) - m_acceptedKeyDowns.insert([nsevent keyCode]); - - if (shouldPropagate) - [super keyDown:nsevent]; -} - -- (void)keyUp:(NSEvent *)nsevent -{ - if ([self isTransparentForUserInput]) - return [super keyUp:nsevent]; - - const bool keyUpAccepted = [self handleKeyEvent:nsevent eventType:int(QEvent::KeyRelease)]; - - // Propagate the keyUp if neither Qt accepted it nor the corresponding KeyDown was - // accepted. Qt text controls wil often not use and ignore keyUp events, but we - // want to avoid propagating unmatched keyUps. - const bool keyDownAccepted = m_acceptedKeyDowns.remove([nsevent keyCode]); - if (!keyUpAccepted && !keyDownAccepted) - [super keyUp:nsevent]; -} - -- (void)cancelOperation:(id)sender -{ - Q_UNUSED(sender); - - NSEvent *currentEvent = [NSApp currentEvent]; - if (!currentEvent || currentEvent.type != NSKeyDown) - return; - - // Handling the key event may recurse back here through interpretKeyEvents - // (when IM is enabled), so we need to guard against that. - if (currentEvent == m_currentlyInterpretedKeyEvent) - return; - - // Send Command+Key_Period and Escape as normal keypresses so that - // the key sequence is delivered through Qt. That way clients can - // intercept the shortcut and override its effect. - [self handleKeyEvent:currentEvent eventType:int(QEvent::KeyPress)]; -} - -- (void)flagsChanged:(NSEvent *)nsevent -{ - ulong timestamp = [nsevent timestamp] * 1000; - ulong modifiers = [nsevent modifierFlags]; - Qt::KeyboardModifiers qmodifiers = [QNSView convertKeyModifiers:modifiers]; - - // calculate the delta and remember the current modifiers for next time - static ulong m_lastKnownModifiers; - ulong lastKnownModifiers = m_lastKnownModifiers; - ulong delta = lastKnownModifiers ^ modifiers; - m_lastKnownModifiers = modifiers; - - struct qt_mac_enum_mapper - { - ulong mac_mask; - Qt::Key qt_code; - }; - static qt_mac_enum_mapper modifier_key_symbols[] = { - { NSShiftKeyMask, Qt::Key_Shift }, - { NSControlKeyMask, Qt::Key_Meta }, - { NSCommandKeyMask, Qt::Key_Control }, - { NSAlternateKeyMask, Qt::Key_Alt }, - { NSAlphaShiftKeyMask, Qt::Key_CapsLock }, - { 0ul, Qt::Key_unknown } }; - for (int i = 0; modifier_key_symbols[i].mac_mask != 0u; ++i) { - uint mac_mask = modifier_key_symbols[i].mac_mask; - if ((delta & mac_mask) == 0u) - continue; - - Qt::Key qtCode = modifier_key_symbols[i].qt_code; - if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) { - if (qtCode == Qt::Key_Meta) - qtCode = Qt::Key_Control; - else if (qtCode == Qt::Key_Control) - qtCode = Qt::Key_Meta; - } - QWindowSystemInterface::handleKeyEvent(m_platformWindow->window(), - timestamp, - (lastKnownModifiers & mac_mask) ? QEvent::KeyRelease : QEvent::KeyPress, - qtCode, - qmodifiers ^ [QNSView convertKeyModifiers:mac_mask]); - } -} - -- (void) insertNewline:(id)sender -{ - Q_UNUSED(sender); - m_resendKeyEvent = true; -} - -- (void) doCommandBySelector:(SEL)aSelector -{ - [self tryToPerform:aSelector with:self]; -} - -- (void) insertText:(id)aString replacementRange:(NSRange)replacementRange -{ - Q_UNUSED(replacementRange) - - if (m_sendKeyEvent && m_composingText.isEmpty() && [aString isEqualToString:m_inputSource]) { - // don't send input method events for simple text input (let handleKeyEvent send key events instead) - return; - } - - QString commitString; - if ([aString length]) { - if ([aString isKindOfClass:[NSAttributedString class]]) { - commitString = QString::fromCFString(reinterpret_cast<CFStringRef>([aString string])); - } else { - commitString = QString::fromCFString(reinterpret_cast<CFStringRef>(aString)); - }; - } - if (QObject *fo = m_platformWindow->window()->focusObject()) { - QInputMethodQueryEvent queryEvent(Qt::ImEnabled); - if (QCoreApplication::sendEvent(fo, &queryEvent)) { - if (queryEvent.value(Qt::ImEnabled).toBool()) { - QInputMethodEvent e; - e.setCommitString(commitString); - QCoreApplication::sendEvent(fo, &e); - // prevent handleKeyEvent from sending a key event - m_sendKeyEvent = false; - } - } - } - - m_composingText.clear(); - m_composingFocusObject = nullptr; -} - -- (void) setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange -{ - Q_UNUSED(replacementRange) - QString preeditString; - - QList<QInputMethodEvent::Attribute> attrs; - attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, selectedRange.location + selectedRange.length, 1, QVariant()); - - if ([aString isKindOfClass:[NSAttributedString class]]) { - // Preedit string has attribution - preeditString = QString::fromCFString(reinterpret_cast<CFStringRef>([aString string])); - int composingLength = preeditString.length(); - int index = 0; - // Create attributes for individual sections of preedit text - while (index < composingLength) { - NSRange effectiveRange; - NSRange range = NSMakeRange(index, composingLength-index); - NSDictionary *attributes = [aString attributesAtIndex:index - longestEffectiveRange:&effectiveRange - inRange:range]; - NSNumber *underlineStyle = [attributes objectForKey:NSUnderlineStyleAttributeName]; - if (underlineStyle) { - QColor clr (Qt::black); - NSColor *color = [attributes objectForKey:NSUnderlineColorAttributeName]; - if (color) { - clr = qt_mac_toQColor(color); - } - QTextCharFormat format; - format.setFontUnderline(true); - format.setUnderlineColor(clr); - attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, - effectiveRange.location, - effectiveRange.length, - format); - } - index = effectiveRange.location + effectiveRange.length; - } - } else { - // No attributes specified, take only the preedit text. - preeditString = QString::fromCFString(reinterpret_cast<CFStringRef>(aString)); - } - - if (attrs.isEmpty()) { - QTextCharFormat format; - format.setFontUnderline(true); - attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, - 0, preeditString.length(), format); - } - - m_composingText = preeditString; - - if (QObject *fo = m_platformWindow->window()->focusObject()) { - m_composingFocusObject = fo; - QInputMethodQueryEvent queryEvent(Qt::ImEnabled); - if (QCoreApplication::sendEvent(fo, &queryEvent)) { - if (queryEvent.value(Qt::ImEnabled).toBool()) { - QInputMethodEvent e(preeditString, attrs); - QCoreApplication::sendEvent(fo, &e); - // prevent handleKeyEvent from sending a key event - m_sendKeyEvent = false; - } - } - } -} - -- (void)cancelComposingText -{ - if (m_composingText.isEmpty()) - return; - - if (m_composingFocusObject) { - QInputMethodQueryEvent queryEvent(Qt::ImEnabled); - if (QCoreApplication::sendEvent(m_composingFocusObject, &queryEvent)) { - if (queryEvent.value(Qt::ImEnabled).toBool()) { - QInputMethodEvent e; - QCoreApplication::sendEvent(m_composingFocusObject, &e); - } - } - } - - m_composingText.clear(); - m_composingFocusObject = nullptr; -} - -- (void) unmarkText -{ - if (!m_composingText.isEmpty()) { - if (QObject *fo = m_platformWindow->window()->focusObject()) { - QInputMethodQueryEvent queryEvent(Qt::ImEnabled); - if (QCoreApplication::sendEvent(fo, &queryEvent)) { - if (queryEvent.value(Qt::ImEnabled).toBool()) { - QInputMethodEvent e; - e.setCommitString(m_composingText); - QCoreApplication::sendEvent(fo, &e); - } - } - } - } - m_composingText.clear(); - m_composingFocusObject = nullptr; -} - -- (BOOL) hasMarkedText -{ - return (m_composingText.isEmpty() ? NO: YES); -} - -- (NSAttributedString *) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange -{ - Q_UNUSED(actualRange) - QObject *fo = m_platformWindow->window()->focusObject(); - if (!fo) - return nil; - QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImCurrentSelection); - if (!QCoreApplication::sendEvent(fo, &queryEvent)) - return nil; - if (!queryEvent.value(Qt::ImEnabled).toBool()) - return nil; - - QString selectedText = queryEvent.value(Qt::ImCurrentSelection).toString(); - if (selectedText.isEmpty()) - return nil; - - QCFString string(selectedText.mid(aRange.location, aRange.length)); - const NSString *tmpString = reinterpret_cast<const NSString *>((CFStringRef)string); - return [[[NSAttributedString alloc] initWithString:const_cast<NSString *>(tmpString)] autorelease]; -} - -- (NSRange) markedRange -{ - NSRange range; - if (!m_composingText.isEmpty()) { - range.location = 0; - range.length = m_composingText.length(); - } else { - range.location = NSNotFound; - range.length = 0; - } - return range; -} - -- (NSRange) selectedRange -{ - NSRange selectedRange = {0, 0}; - - QObject *fo = m_platformWindow->window()->focusObject(); - if (!fo) - return selectedRange; - QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImCurrentSelection); - if (!QCoreApplication::sendEvent(fo, &queryEvent)) - return selectedRange; - if (!queryEvent.value(Qt::ImEnabled).toBool()) - return selectedRange; - - QString selectedText = queryEvent.value(Qt::ImCurrentSelection).toString(); - - if (!selectedText.isEmpty()) { - selectedRange.location = 0; - selectedRange.length = selectedText.length(); - } - return selectedRange; -} - -- (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange -{ - Q_UNUSED(aRange) - Q_UNUSED(actualRange) - QObject *fo = m_platformWindow->window()->focusObject(); - if (!fo) - return NSZeroRect; - - QInputMethodQueryEvent queryEvent(Qt::ImEnabled); - if (!QCoreApplication::sendEvent(fo, &queryEvent)) - return NSZeroRect; - if (!queryEvent.value(Qt::ImEnabled).toBool()) - return NSZeroRect; - - if (!m_platformWindow->window()) - return NSZeroRect; - - // The returned rect is always based on the internal cursor. - QRect mr = qApp->inputMethod()->cursorRectangle().toRect(); - mr.moveBottomLeft(m_platformWindow->window()->mapToGlobal(mr.bottomLeft())); - return QCocoaScreen::mapToNative(mr); -} - -- (NSUInteger)characterIndexForPoint:(NSPoint)aPoint -{ - // We don't support cursor movements using mouse while composing. - Q_UNUSED(aPoint); - return NSNotFound; -} - -- (NSArray*)validAttributesForMarkedText -{ - if (!m_platformWindow) - return nil; - - if (m_platformWindow->window() != QGuiApplication::focusWindow()) - return nil; - - QObject *fo = m_platformWindow->window()->focusObject(); - if (!fo) - return nil; - - QInputMethodQueryEvent queryEvent(Qt::ImEnabled); - if (!QCoreApplication::sendEvent(fo, &queryEvent)) - return nil; - if (!queryEvent.value(Qt::ImEnabled).toBool()) - return nil; - - // Support only underline color/style. - return [NSArray arrayWithObjects:NSUnderlineColorAttributeName, - NSUnderlineStyleAttributeName, nil]; -} - --(void)registerDragTypes -{ - QMacAutoReleasePool pool; - QStringList customTypes = qt_mac_enabledDraggedTypes(); - if (currentCustomDragTypes == 0 || *currentCustomDragTypes != customTypes) { - if (currentCustomDragTypes == 0) - currentCustomDragTypes = new QStringList(); - *currentCustomDragTypes = customTypes; - const NSString* mimeTypeGeneric = @"com.trolltech.qt.MimeTypeName"; - NSMutableArray *supportedTypes = [NSMutableArray arrayWithObjects:NSColorPboardType, - NSFilenamesPboardType, NSStringPboardType, - NSFilenamesPboardType, NSPostScriptPboardType, NSTIFFPboardType, - NSRTFPboardType, NSTabularTextPboardType, NSFontPboardType, - NSRulerPboardType, NSFileContentsPboardType, NSColorPboardType, - NSRTFDPboardType, NSHTMLPboardType, - NSURLPboardType, NSPDFPboardType, NSVCardPboardType, - NSFilesPromisePboardType, NSInkTextPboardType, - NSMultipleTextSelectionPboardType, mimeTypeGeneric, nil]; - // Add custom types supported by the application. - for (int i = 0; i < customTypes.size(); i++) { - [supportedTypes addObject:customTypes[i].toNSString()]; - } - [self registerForDraggedTypes:supportedTypes]; - } -} - -static QWindow *findEventTargetWindow(QWindow *candidate) -{ - while (candidate) { - if (!(candidate->flags() & Qt::WindowTransparentForInput)) - return candidate; - candidate = candidate->parent(); - } - return candidate; -} - -static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint point) -{ - return target->mapFromGlobal(source->mapToGlobal(point)); -} - -- (NSDragOperation)draggingSession:(NSDraggingSession *)session - sourceOperationMaskForDraggingContext:(NSDraggingContext)context -{ - Q_UNUSED(session); - Q_UNUSED(context); - QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); - return qt_mac_mapDropActions(nativeDrag->currentDrag()->supportedActions()); -} - -- (BOOL)ignoreModifierKeysForDraggingSession:(NSDraggingSession *)session -{ - Q_UNUSED(session); - // According to the "Dragging Sources" chapter on Cocoa DnD Programming - // (https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/DragandDrop/Concepts/dragsource.html), - // if the control, option, or command key is pressed, the source’s - // operation mask is filtered to only contain a reduced set of operations. - // - // Since Qt already takes care of tracking the keyboard modifiers, we - // don't need (or want) Cocoa to filter anything. Instead, we'll let - // the application do the actual filtering. - return YES; -} - -- (BOOL)wantsPeriodicDraggingUpdates -{ - // From the documentation: - // - // "If the destination returns NO, these messages are sent only when the mouse moves - // or a modifier flag changes. Otherwise the destination gets the default behavior, - // where it receives periodic dragging-updated messages even if nothing changes." - // - // We do not want these constant drag update events while mouse is stationary, - // since we do all animations (autoscroll) with timers. - return NO; -} - - -- (BOOL)wantsPeriodicDraggingUpdates:(void *)dummy -{ - // This method never gets called. It's a workaround for Apple's - // bug: they first respondsToSelector : @selector(wantsPeriodicDraggingUpdates:) - // (note ':') and then call -wantsPeriodicDraggingUpdate (without colon). - // So, let's make them happy. - Q_UNUSED(dummy); - - return NO; -} - -- (void)updateCursorFromDragResponse:(QPlatformDragQtResponse)response drag:(QCocoaDrag *)drag -{ - const QPixmap pixmapCursor = drag->currentDrag()->dragCursor(response.acceptedAction()); - NSCursor *nativeCursor = nil; - - if (pixmapCursor.isNull()) { - switch (response.acceptedAction()) { - case Qt::CopyAction: - nativeCursor = [NSCursor dragCopyCursor]; - break; - case Qt::LinkAction: - nativeCursor = [NSCursor dragLinkCursor]; - break; - case Qt::IgnoreAction: - // Uncomment the next lines if forbiden cursor wanted on non droppable targets. - /*nativeCursor = [NSCursor operationNotAllowedCursor]; - break;*/ - case Qt::MoveAction: - default: - nativeCursor = [NSCursor arrowCursor]; - break; - } - } - else { - NSImage *nsimage = qt_mac_create_nsimage(pixmapCursor); - nsimage.size = NSSizeFromCGSize((pixmapCursor.size() / pixmapCursor.devicePixelRatioF()).toCGSize()); - nativeCursor = [[NSCursor alloc] initWithImage:nsimage hotSpot:NSZeroPoint]; - [nsimage release]; - } - - // change the cursor - [nativeCursor set]; - - // Make sure the cursor is updated correctly if the mouse does not move and window is under cursor - // by creating a fake move event - if (m_updatingDrag) - return; - - const QPoint mousePos(QCursor::pos()); - CGEventRef moveEvent(CGEventCreateMouseEvent( - NULL, kCGEventMouseMoved, - CGPointMake(mousePos.x(), mousePos.y()), - kCGMouseButtonLeft // ignored - )); - CGEventPost(kCGHIDEventTap, moveEvent); - CFRelease(moveEvent); -} - -- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender -{ - return [self handleDrag : sender]; -} - -- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender -{ - m_updatingDrag = true; - const NSDragOperation ret([self handleDrag : sender]); - m_updatingDrag = false; - - return ret; -} - -// Sends drag update to Qt, return the action -- (NSDragOperation)handleDrag:(id <NSDraggingInfo>)sender -{ - if (!m_platformWindow) - return NSDragOperationNone; - - NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil]; - QPoint qt_windowPoint(windowPoint.x, windowPoint.y); - Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]); - - QWindow *target = findEventTargetWindow(m_platformWindow->window()); - if (!target) - return NSDragOperationNone; - - // update these so selecting move/copy/link works - QGuiApplicationPrivate::modifier_buttons = [QNSView convertKeyModifiers: [[NSApp currentEvent] modifierFlags]]; - - QPlatformDragQtResponse response(false, Qt::IgnoreAction, QRect()); - QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); - if (nativeDrag->currentDrag()) { - // The drag was started from within the application - response = QWindowSystemInterface::handleDrag(target, nativeDrag->dragMimeData(), mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), qtAllowed); - [self updateCursorFromDragResponse:response drag:nativeDrag]; - } else { - QCocoaDropData mimeData([sender draggingPasteboard]); - response = QWindowSystemInterface::handleDrag(target, &mimeData, mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), qtAllowed); - } - - return qt_mac_mapDropAction(response.acceptedAction()); -} - -- (void)draggingExited:(id <NSDraggingInfo>)sender -{ - if (!m_platformWindow) - return; - - QWindow *target = findEventTargetWindow(m_platformWindow->window()); - if (!target) - return; - - NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil]; - QPoint qt_windowPoint(windowPoint.x, windowPoint.y); - - // Send 0 mime data to indicate drag exit - QWindowSystemInterface::handleDrag(target, 0, mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), Qt::IgnoreAction); -} - -// called on drop, send the drop to Qt and return if it was accepted. -- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender -{ - if (!m_platformWindow) - return false; - - QWindow *target = findEventTargetWindow(m_platformWindow->window()); - if (!target) - return false; - - NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil]; - QPoint qt_windowPoint(windowPoint.x, windowPoint.y); - Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]); - - QPlatformDropQtResponse response(false, Qt::IgnoreAction); - QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); - if (nativeDrag->currentDrag()) { - // The drag was started from within the application - response = QWindowSystemInterface::handleDrop(target, nativeDrag->dragMimeData(), mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), qtAllowed); - } else { - QCocoaDropData mimeData([sender draggingPasteboard]); - response = QWindowSystemInterface::handleDrop(target, &mimeData, mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), qtAllowed); - } - if (response.isAccepted()) { - QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); - nativeDrag->setAcceptedAction(response.acceptedAction()); - } - return response.isAccepted(); -} - -- (void)draggingSession:(NSDraggingSession *)session - endedAtPoint:(NSPoint)screenPoint - operation:(NSDragOperation)operation -{ - Q_UNUSED(session); - Q_UNUSED(operation); - - if (!m_platformWindow) - return; - - QWindow *target = findEventTargetWindow(m_platformWindow->window()); - if (!target) - return; - - // keep our state, and QGuiApplication state (buttons member) in-sync, - // or future mouse events will be processed incorrectly - NSUInteger pmb = [NSEvent pressedMouseButtons]; - for (int buttonNumber = 0; buttonNumber < 32; buttonNumber++) { // see cocoaButton2QtButton() for the 32 value - if (!(pmb & (1 << buttonNumber))) - m_buttons &= ~cocoaButton2QtButton(buttonNumber); - } +@end - NSPoint windowPoint = [self.window convertRectFromScreen:NSMakeRect(screenPoint.x, screenPoint.y, 1, 1)].origin; - NSPoint nsViewPoint = [self convertPoint: windowPoint fromView: nil]; // NSView/QWindow coordinates - QPoint qtWindowPoint(nsViewPoint.x, nsViewPoint.y); - QPoint qtScreenPoint = QCocoaScreen::mapFromNative(screenPoint).toPoint(); +#include "qnsview_drawing.mm" +#include "qnsview_mouse.mm" +#include "qnsview_touch.mm" +#include "qnsview_gestures.mm" +#include "qnsview_tablet.mm" +#include "qnsview_dragging.mm" +#include "qnsview_keys.mm" +#include "qnsview_complextext.mm" - QWindowSystemInterface::handleMouseEvent(target, mapWindowCoordinates(m_platformWindow->window(), target, qtWindowPoint), qtScreenPoint, m_buttons); -} - -@end +// ----------------------------------------------------- @implementation QT_MANGLE_NAMESPACE(QNSView) (QtExtras) diff --git a/src/plugins/platforms/cocoa/qnsview_complextext.mm b/src/plugins/platforms/cocoa/qnsview_complextext.mm new file mode 100644 index 0000000000..f0d544c63d --- /dev/null +++ b/src/plugins/platforms/cocoa/qnsview_complextext.mm @@ -0,0 +1,317 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// This file is included from qnsview.mm, and only used to organize the code + +@implementation QT_MANGLE_NAMESPACE(QNSView) (ComplexTextAPI) + +- (void)cancelComposingText +{ + if (m_composingText.isEmpty()) + return; + + if (m_composingFocusObject) { + QInputMethodQueryEvent queryEvent(Qt::ImEnabled); + if (QCoreApplication::sendEvent(m_composingFocusObject, &queryEvent)) { + if (queryEvent.value(Qt::ImEnabled).toBool()) { + QInputMethodEvent e; + QCoreApplication::sendEvent(m_composingFocusObject, &e); + } + } + } + + m_composingText.clear(); + m_composingFocusObject = nullptr; +} + +- (void)unmarkText +{ + if (!m_composingText.isEmpty()) { + if (QObject *fo = m_platformWindow->window()->focusObject()) { + QInputMethodQueryEvent queryEvent(Qt::ImEnabled); + if (QCoreApplication::sendEvent(fo, &queryEvent)) { + if (queryEvent.value(Qt::ImEnabled).toBool()) { + QInputMethodEvent e; + e.setCommitString(m_composingText); + QCoreApplication::sendEvent(fo, &e); + } + } + } + } + m_composingText.clear(); + m_composingFocusObject = nullptr; +} + +@end + +@implementation QT_MANGLE_NAMESPACE(QNSView) (ComplexText) + +- (void)insertNewline:(id)sender +{ + Q_UNUSED(sender); + m_resendKeyEvent = true; +} + +- (void)doCommandBySelector:(SEL)aSelector +{ + [self tryToPerform:aSelector with:self]; +} + +- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange +{ + Q_UNUSED(replacementRange) + + if (m_sendKeyEvent && m_composingText.isEmpty() && [aString isEqualToString:m_inputSource]) { + // don't send input method events for simple text input (let handleKeyEvent send key events instead) + return; + } + + QString commitString; + if ([aString length]) { + if ([aString isKindOfClass:[NSAttributedString class]]) { + commitString = QString::fromCFString(reinterpret_cast<CFStringRef>([aString string])); + } else { + commitString = QString::fromCFString(reinterpret_cast<CFStringRef>(aString)); + }; + } + if (QObject *fo = m_platformWindow->window()->focusObject()) { + QInputMethodQueryEvent queryEvent(Qt::ImEnabled); + if (QCoreApplication::sendEvent(fo, &queryEvent)) { + if (queryEvent.value(Qt::ImEnabled).toBool()) { + QInputMethodEvent e; + e.setCommitString(commitString); + QCoreApplication::sendEvent(fo, &e); + // prevent handleKeyEvent from sending a key event + m_sendKeyEvent = false; + } + } + } + + m_composingText.clear(); + m_composingFocusObject = nullptr; +} + +- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange +{ + Q_UNUSED(replacementRange) + QString preeditString; + + QList<QInputMethodEvent::Attribute> attrs; + attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, selectedRange.location + selectedRange.length, 1, QVariant()); + + if ([aString isKindOfClass:[NSAttributedString class]]) { + // Preedit string has attribution + preeditString = QString::fromCFString(reinterpret_cast<CFStringRef>([aString string])); + int composingLength = preeditString.length(); + int index = 0; + // Create attributes for individual sections of preedit text + while (index < composingLength) { + NSRange effectiveRange; + NSRange range = NSMakeRange(index, composingLength-index); + NSDictionary *attributes = [aString attributesAtIndex:index + longestEffectiveRange:&effectiveRange + inRange:range]; + NSNumber *underlineStyle = [attributes objectForKey:NSUnderlineStyleAttributeName]; + if (underlineStyle) { + QColor clr (Qt::black); + NSColor *color = [attributes objectForKey:NSUnderlineColorAttributeName]; + if (color) { + clr = qt_mac_toQColor(color); + } + QTextCharFormat format; + format.setFontUnderline(true); + format.setUnderlineColor(clr); + attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, + effectiveRange.location, + effectiveRange.length, + format); + } + index = effectiveRange.location + effectiveRange.length; + } + } else { + // No attributes specified, take only the preedit text. + preeditString = QString::fromCFString(reinterpret_cast<CFStringRef>(aString)); + } + + if (attrs.isEmpty()) { + QTextCharFormat format; + format.setFontUnderline(true); + attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, + 0, preeditString.length(), format); + } + + m_composingText = preeditString; + + if (QObject *fo = m_platformWindow->window()->focusObject()) { + m_composingFocusObject = fo; + QInputMethodQueryEvent queryEvent(Qt::ImEnabled); + if (QCoreApplication::sendEvent(fo, &queryEvent)) { + if (queryEvent.value(Qt::ImEnabled).toBool()) { + QInputMethodEvent e(preeditString, attrs); + QCoreApplication::sendEvent(fo, &e); + // prevent handleKeyEvent from sending a key event + m_sendKeyEvent = false; + } + } + } +} + +- (BOOL)hasMarkedText +{ + return (m_composingText.isEmpty() ? NO: YES); +} + +- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange +{ + Q_UNUSED(actualRange) + QObject *fo = m_platformWindow->window()->focusObject(); + if (!fo) + return nil; + QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImCurrentSelection); + if (!QCoreApplication::sendEvent(fo, &queryEvent)) + return nil; + if (!queryEvent.value(Qt::ImEnabled).toBool()) + return nil; + + QString selectedText = queryEvent.value(Qt::ImCurrentSelection).toString(); + if (selectedText.isEmpty()) + return nil; + + QCFString string(selectedText.mid(aRange.location, aRange.length)); + const NSString *tmpString = reinterpret_cast<const NSString *>((CFStringRef)string); + return [[[NSAttributedString alloc] initWithString:const_cast<NSString *>(tmpString)] autorelease]; +} + +- (NSRange)markedRange +{ + NSRange range; + if (!m_composingText.isEmpty()) { + range.location = 0; + range.length = m_composingText.length(); + } else { + range.location = NSNotFound; + range.length = 0; + } + return range; +} + +- (NSRange)selectedRange +{ + NSRange selectedRange = {0, 0}; + + QObject *fo = m_platformWindow->window()->focusObject(); + if (!fo) + return selectedRange; + QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImCurrentSelection); + if (!QCoreApplication::sendEvent(fo, &queryEvent)) + return selectedRange; + if (!queryEvent.value(Qt::ImEnabled).toBool()) + return selectedRange; + + QString selectedText = queryEvent.value(Qt::ImCurrentSelection).toString(); + + if (!selectedText.isEmpty()) { + selectedRange.location = 0; + selectedRange.length = selectedText.length(); + } + return selectedRange; +} + +- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange +{ + Q_UNUSED(aRange) + Q_UNUSED(actualRange) + QObject *fo = m_platformWindow->window()->focusObject(); + if (!fo) + return NSZeroRect; + + QInputMethodQueryEvent queryEvent(Qt::ImEnabled); + if (!QCoreApplication::sendEvent(fo, &queryEvent)) + return NSZeroRect; + if (!queryEvent.value(Qt::ImEnabled).toBool()) + return NSZeroRect; + + if (!m_platformWindow->window()) + return NSZeroRect; + + // The returned rect is always based on the internal cursor. + QRect mr = qApp->inputMethod()->cursorRectangle().toRect(); + mr.moveBottomLeft(m_platformWindow->window()->mapToGlobal(mr.bottomLeft())); + return QCocoaScreen::mapToNative(mr); +} + +- (NSUInteger)characterIndexForPoint:(NSPoint)aPoint +{ + // We don't support cursor movements using mouse while composing. + Q_UNUSED(aPoint); + return NSNotFound; +} + +- (NSArray<NSString *> *)validAttributesForMarkedText +{ + if (!m_platformWindow) + return nil; + + if (m_platformWindow->window() != QGuiApplication::focusWindow()) + return nil; + + QObject *fo = m_platformWindow->window()->focusObject(); + if (!fo) + return nil; + + QInputMethodQueryEvent queryEvent(Qt::ImEnabled); + if (!QCoreApplication::sendEvent(fo, &queryEvent)) + return nil; + if (!queryEvent.value(Qt::ImEnabled).toBool()) + return nil; + + // Support only underline color/style. + return @[NSUnderlineColorAttributeName, NSUnderlineStyleAttributeName]; +} + +- (void)textInputContextKeyboardSelectionDidChangeNotification:(NSNotification *)textInputContextKeyboardSelectionDidChangeNotification +{ + Q_UNUSED(textInputContextKeyboardSelectionDidChangeNotification) + if (([NSApp keyWindow] == self.window) && self.window.firstResponder == self) { + QCocoaInputContext *ic = qobject_cast<QCocoaInputContext *>(QCocoaIntegration::instance()->inputContext()); + ic->updateLocale(); + } +} + +@end diff --git a/src/plugins/platforms/cocoa/qnsview_dragging.mm b/src/plugins/platforms/cocoa/qnsview_dragging.mm new file mode 100644 index 0000000000..e77a26eed0 --- /dev/null +++ b/src/plugins/platforms/cocoa/qnsview_dragging.mm @@ -0,0 +1,302 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// This file is included from qnsview.mm, and only used to organize the code + +@implementation QT_MANGLE_NAMESPACE(QNSView) (Dragging) + +-(void)registerDragTypes +{ + QMacAutoReleasePool pool; + QStringList customTypes = qt_mac_enabledDraggedTypes(); + if (currentCustomDragTypes == 0 || *currentCustomDragTypes != customTypes) { + if (currentCustomDragTypes == 0) + currentCustomDragTypes = new QStringList(); + *currentCustomDragTypes = customTypes; + NSString * const mimeTypeGeneric = @"com.trolltech.qt.MimeTypeName"; + NSMutableArray<NSString *> *supportedTypes = [NSMutableArray<NSString *> arrayWithArray:@[ + NSColorPboardType, + NSFilenamesPboardType, NSStringPboardType, + NSFilenamesPboardType, NSPostScriptPboardType, NSTIFFPboardType, + NSRTFPboardType, NSTabularTextPboardType, NSFontPboardType, + NSRulerPboardType, NSFileContentsPboardType, NSColorPboardType, + NSRTFDPboardType, NSHTMLPboardType, + NSURLPboardType, NSPDFPboardType, NSVCardPboardType, + NSFilesPromisePboardType, NSInkTextPboardType, + NSMultipleTextSelectionPboardType, mimeTypeGeneric]]; + // Add custom types supported by the application. + for (int i = 0; i < customTypes.size(); i++) { + [supportedTypes addObject:customTypes[i].toNSString()]; + } + [self registerForDraggedTypes:supportedTypes]; + } +} + +static QWindow *findEventTargetWindow(QWindow *candidate) +{ + while (candidate) { + if (!(candidate->flags() & Qt::WindowTransparentForInput)) + return candidate; + candidate = candidate->parent(); + } + return candidate; +} + +static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint point) +{ + return target->mapFromGlobal(source->mapToGlobal(point)); +} + +- (NSDragOperation)draggingSession:(NSDraggingSession *)session + sourceOperationMaskForDraggingContext:(NSDraggingContext)context +{ + Q_UNUSED(session); + Q_UNUSED(context); + QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); + return qt_mac_mapDropActions(nativeDrag->currentDrag()->supportedActions()); +} + +- (BOOL)ignoreModifierKeysForDraggingSession:(NSDraggingSession *)session +{ + Q_UNUSED(session); + // According to the "Dragging Sources" chapter on Cocoa DnD Programming + // (https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/DragandDrop/Concepts/dragsource.html), + // if the control, option, or command key is pressed, the source’s + // operation mask is filtered to only contain a reduced set of operations. + // + // Since Qt already takes care of tracking the keyboard modifiers, we + // don't need (or want) Cocoa to filter anything. Instead, we'll let + // the application do the actual filtering. + return YES; +} + +- (BOOL)wantsPeriodicDraggingUpdates +{ + // From the documentation: + // + // "If the destination returns NO, these messages are sent only when the mouse moves + // or a modifier flag changes. Otherwise the destination gets the default behavior, + // where it receives periodic dragging-updated messages even if nothing changes." + // + // We do not want these constant drag update events while mouse is stationary, + // since we do all animations (autoscroll) with timers. + return NO; +} + + +- (BOOL)wantsPeriodicDraggingUpdates:(void *)dummy +{ + // This method never gets called. It's a workaround for Apple's + // bug: they first respondsToSelector : @selector(wantsPeriodicDraggingUpdates:) + // (note ':') and then call -wantsPeriodicDraggingUpdate (without colon). + // So, let's make them happy. + Q_UNUSED(dummy); + + return NO; +} + +- (void)updateCursorFromDragResponse:(QPlatformDragQtResponse)response drag:(QCocoaDrag *)drag +{ + const QPixmap pixmapCursor = drag->currentDrag()->dragCursor(response.acceptedAction()); + NSCursor *nativeCursor = nil; + + if (pixmapCursor.isNull()) { + switch (response.acceptedAction()) { + case Qt::CopyAction: + nativeCursor = [NSCursor dragCopyCursor]; + break; + case Qt::LinkAction: + nativeCursor = [NSCursor dragLinkCursor]; + break; + case Qt::IgnoreAction: + // Uncomment the next lines if forbiden cursor wanted on non droppable targets. + /*nativeCursor = [NSCursor operationNotAllowedCursor]; + break;*/ + case Qt::MoveAction: + default: + nativeCursor = [NSCursor arrowCursor]; + break; + } + } + else { + NSImage *nsimage = qt_mac_create_nsimage(pixmapCursor); + nsimage.size = NSSizeFromCGSize((pixmapCursor.size() / pixmapCursor.devicePixelRatioF()).toCGSize()); + nativeCursor = [[NSCursor alloc] initWithImage:nsimage hotSpot:NSZeroPoint]; + [nsimage release]; + } + + // change the cursor + [nativeCursor set]; + + // Make sure the cursor is updated correctly if the mouse does not move and window is under cursor + // by creating a fake move event + if (m_updatingDrag) + return; + + const QPoint mousePos(QCursor::pos()); + CGEventRef moveEvent(CGEventCreateMouseEvent( + NULL, kCGEventMouseMoved, + CGPointMake(mousePos.x(), mousePos.y()), + kCGMouseButtonLeft // ignored + )); + CGEventPost(kCGHIDEventTap, moveEvent); + CFRelease(moveEvent); +} + +- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender +{ + return [self handleDrag : sender]; +} + +- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender +{ + m_updatingDrag = true; + const NSDragOperation ret([self handleDrag : sender]); + m_updatingDrag = false; + + return ret; +} + +// Sends drag update to Qt, return the action +- (NSDragOperation)handleDrag:(id <NSDraggingInfo>)sender +{ + if (!m_platformWindow) + return NSDragOperationNone; + + NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil]; + QPoint qt_windowPoint(windowPoint.x, windowPoint.y); + Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]); + + QWindow *target = findEventTargetWindow(m_platformWindow->window()); + if (!target) + return NSDragOperationNone; + + // update these so selecting move/copy/link works + QGuiApplicationPrivate::modifier_buttons = [QNSView convertKeyModifiers: [[NSApp currentEvent] modifierFlags]]; + + QPlatformDragQtResponse response(false, Qt::IgnoreAction, QRect()); + QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); + if (nativeDrag->currentDrag()) { + // The drag was started from within the application + response = QWindowSystemInterface::handleDrag(target, nativeDrag->dragMimeData(), mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), qtAllowed); + [self updateCursorFromDragResponse:response drag:nativeDrag]; + } else { + QCocoaDropData mimeData([sender draggingPasteboard]); + response = QWindowSystemInterface::handleDrag(target, &mimeData, mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), qtAllowed); + } + + return qt_mac_mapDropAction(response.acceptedAction()); +} + +- (void)draggingExited:(id <NSDraggingInfo>)sender +{ + if (!m_platformWindow) + return; + + QWindow *target = findEventTargetWindow(m_platformWindow->window()); + if (!target) + return; + + NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil]; + QPoint qt_windowPoint(windowPoint.x, windowPoint.y); + + // Send 0 mime data to indicate drag exit + QWindowSystemInterface::handleDrag(target, 0, mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), Qt::IgnoreAction); +} + +// called on drop, send the drop to Qt and return if it was accepted. +- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender +{ + if (!m_platformWindow) + return false; + + QWindow *target = findEventTargetWindow(m_platformWindow->window()); + if (!target) + return false; + + NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil]; + QPoint qt_windowPoint(windowPoint.x, windowPoint.y); + Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]); + + QPlatformDropQtResponse response(false, Qt::IgnoreAction); + QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); + if (nativeDrag->currentDrag()) { + // The drag was started from within the application + response = QWindowSystemInterface::handleDrop(target, nativeDrag->dragMimeData(), mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), qtAllowed); + } else { + QCocoaDropData mimeData([sender draggingPasteboard]); + response = QWindowSystemInterface::handleDrop(target, &mimeData, mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), qtAllowed); + } + return response.isAccepted(); +} + +- (void)draggingSession:(NSDraggingSession *)session + endedAtPoint:(NSPoint)screenPoint + operation:(NSDragOperation)operation +{ + Q_UNUSED(session); + Q_UNUSED(operation); + + if (!m_platformWindow) + return; + + QWindow *target = findEventTargetWindow(m_platformWindow->window()); + if (!target) + return; + + QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); + nativeDrag->setAcceptedAction(qt_mac_mapNSDragOperation(operation)); + + // keep our state, and QGuiApplication state (buttons member) in-sync, + // or future mouse events will be processed incorrectly + NSUInteger pmb = [NSEvent pressedMouseButtons]; + for (int buttonNumber = 0; buttonNumber < 32; buttonNumber++) { // see cocoaButton2QtButton() for the 32 value + if (!(pmb & (1 << buttonNumber))) + m_buttons &= ~cocoaButton2QtButton(buttonNumber); + } + + NSPoint windowPoint = [self.window convertRectFromScreen:NSMakeRect(screenPoint.x, screenPoint.y, 1, 1)].origin; + NSPoint nsViewPoint = [self convertPoint: windowPoint fromView: nil]; // NSView/QWindow coordinates + QPoint qtWindowPoint(nsViewPoint.x, nsViewPoint.y); + QPoint qtScreenPoint = QCocoaScreen::mapFromNative(screenPoint).toPoint(); + + QWindowSystemInterface::handleMouseEvent(target, mapWindowCoordinates(m_platformWindow->window(), target, qtWindowPoint), qtScreenPoint, m_buttons); +} + +@end diff --git a/src/plugins/platforms/cocoa/qnsview_drawing.mm b/src/plugins/platforms/cocoa/qnsview_drawing.mm new file mode 100644 index 0000000000..a2509ccb72 --- /dev/null +++ b/src/plugins/platforms/cocoa/qnsview_drawing.mm @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// This file is included from qnsview.mm, and only used to organize the code + +@implementation QT_MANGLE_NAMESPACE(QNSView) (DrawingAPI) + +- (void)requestUpdate +{ + if (self.needsDisplay) { + // If the view already has needsDisplay set it means that there may be code waiting for + // a real expose event, so we can't issue setNeedsDisplay now as a way to trigger an + // update request. We will re-trigger requestUpdate from drawRect. + return; + } + + [self setNeedsDisplay:YES]; + m_updateRequested = true; +} + +#ifndef QT_NO_OPENGL +- (void)setQCocoaGLContext:(QCocoaGLContext *)context +{ + m_glContext = context; + [m_glContext->nsOpenGLContext() setView:self]; + if (![m_glContext->nsOpenGLContext() view]) { + //was unable to set view + m_shouldSetGLContextinDrawRect = true; + } +} +#endif + +@end + +@implementation QT_MANGLE_NAMESPACE(QNSView) (Drawing) + +- (BOOL)isOpaque +{ + if (!m_platformWindow) + return true; + return m_platformWindow->isOpaque(); +} + +- (BOOL)isFlipped +{ + return YES; +} + +- (void)setNeedsDisplayInRect:(NSRect)rect +{ + [super setNeedsDisplayInRect:rect]; + m_updateRequested = false; +} + +- (void)drawRect:(NSRect)dirtyRect +{ + Q_UNUSED(dirtyRect); + + if (!m_platformWindow) + return; + + QRegion exposedRegion; + const NSRect *dirtyRects; + NSInteger numDirtyRects; + [self getRectsBeingDrawn:&dirtyRects count:&numDirtyRects]; + for (int i = 0; i < numDirtyRects; ++i) + exposedRegion += QRectF::fromCGRect(dirtyRects[i]).toRect(); + + qCDebug(lcQpaDrawing) << "[QNSView drawRect:]" << m_platformWindow->window() << exposedRegion; + [self updateRegion:exposedRegion]; +} + +- (void)updateRegion:(QRegion)dirtyRegion +{ +#ifndef QT_NO_OPENGL + if (m_glContext && m_shouldSetGLContextinDrawRect) { + [m_glContext->nsOpenGLContext() setView:self]; + m_shouldSetGLContextinDrawRect = false; + } +#endif + + QWindowPrivate *windowPrivate = qt_window_private(m_platformWindow->window()); + + if (m_updateRequested) { + Q_ASSERT(windowPrivate->updateRequestPending); + qCDebug(lcQpaWindow) << "Delivering update request to" << m_platformWindow->window(); + windowPrivate->deliverUpdateRequest(); + m_updateRequested = false; + } else { + m_platformWindow->handleExposeEvent(dirtyRegion); + } + + if (windowPrivate->updateRequestPending) { + // A call to QWindow::requestUpdate was issued during event delivery above, + // but AppKit will reset the needsDisplay state of the view after completing + // the current display cycle, so we need to defer the request to redisplay. + // FIXME: Perhaps this should be a trigger to enable CADisplayLink? + qCDebug(lcQpaDrawing) << "[QNSView drawRect:] issuing deferred setNeedsDisplay due to pending update request"; + dispatch_async(dispatch_get_main_queue (), ^{ [self requestUpdate]; }); + } +} + +- (BOOL)wantsLayer +{ + Q_ASSERT(m_platformWindow); + + // Toggling the private QWindow property or the environment variable + // on and off is not a supported use-case, so this code is effectively + // returning a constant for the lifetime of our QSNSView, which means + // we don't care about emitting KVO signals for @"wantsLayer". + return qt_mac_resolveOption(false, m_platformWindow->window(), + "_q_mac_wantsLayer", "QT_MAC_WANTS_LAYER"); +} + +- (void)displayLayer:(CALayer *)layer +{ + Q_ASSERT(layer == self.layer); + + if (!m_platformWindow) + return; + + qCDebug(lcQpaDrawing) << "[QNSView displayLayer]" << m_platformWindow->window(); + + // FIXME: Find out if there's a way to resolve the dirty rect like in drawRect: + [self updateRegion:QRectF::fromCGRect(self.bounds).toRect()]; +} + +- (void)viewDidChangeBackingProperties +{ + if (self.layer) + self.layer.contentsScale = self.window.backingScaleFactor; +} + +@end diff --git a/src/plugins/platforms/cocoa/qnsview_gestures.mm b/src/plugins/platforms/cocoa/qnsview_gestures.mm new file mode 100644 index 0000000000..61d551ee0e --- /dev/null +++ b/src/plugins/platforms/cocoa/qnsview_gestures.mm @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// This file is included from qnsview.mm, and only used to organize the code + +#ifndef QT_NO_GESTURES + +Q_LOGGING_CATEGORY(lcQpaGestures, "qt.qpa.input.gestures") + +@implementation QT_MANGLE_NAMESPACE(QNSView) (Gestures) + +- (bool)handleGestureAsBeginEnd:(NSEvent *)event +{ + if (QOperatingSystemVersion::current() < QOperatingSystemVersion::OSXElCapitan) + return false; + + if ([event phase] == NSEventPhaseBegan) { + [self beginGestureWithEvent:event]; + return true; + } + + if ([event phase] == NSEventPhaseEnded) { + [self endGestureWithEvent:event]; + return true; + } + + return false; +} +- (void)magnifyWithEvent:(NSEvent *)event +{ + if (!m_platformWindow) + return; + + if ([self handleGestureAsBeginEnd:event]) + return; + + qCDebug(lcQpaGestures) << "magnifyWithEvent" << [event magnification] << "from device" << hex << [event deviceID]; + const NSTimeInterval timestamp = [event timestamp]; + QPointF windowPoint; + QPointF screenPoint; + [self convertFromScreen:[self screenMousePoint:event] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; + QWindowSystemInterface::handleGestureEventWithRealValue(m_platformWindow->window(), QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), timestamp, Qt::ZoomNativeGesture, + [event magnification], windowPoint, screenPoint); +} + +- (void)smartMagnifyWithEvent:(NSEvent *)event +{ + if (!m_platformWindow) + return; + + static bool zoomIn = true; + qCDebug(lcQpaGestures) << "smartMagnifyWithEvent" << zoomIn << "from device" << hex << [event deviceID]; + const NSTimeInterval timestamp = [event timestamp]; + QPointF windowPoint; + QPointF screenPoint; + [self convertFromScreen:[self screenMousePoint:event] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; + QWindowSystemInterface::handleGestureEventWithRealValue(m_platformWindow->window(), QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), timestamp, Qt::SmartZoomNativeGesture, + zoomIn ? 1.0f : 0.0f, windowPoint, screenPoint); + zoomIn = !zoomIn; +} + +- (void)rotateWithEvent:(NSEvent *)event +{ + if (!m_platformWindow) + return; + + if ([self handleGestureAsBeginEnd:event]) + return; + + const NSTimeInterval timestamp = [event timestamp]; + QPointF windowPoint; + QPointF screenPoint; + [self convertFromScreen:[self screenMousePoint:event] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; + QWindowSystemInterface::handleGestureEventWithRealValue(m_platformWindow->window(), QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), timestamp, Qt::RotateNativeGesture, + -[event rotation], windowPoint, screenPoint); +} + +- (void)swipeWithEvent:(NSEvent *)event +{ + if (!m_platformWindow) + return; + + qCDebug(lcQpaGestures) << "swipeWithEvent" << [event deltaX] << [event deltaY] << "from device" << hex << [event deviceID]; + const NSTimeInterval timestamp = [event timestamp]; + QPointF windowPoint; + QPointF screenPoint; + [self convertFromScreen:[self screenMousePoint:event] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; + + qreal angle = 0.0f; + if ([event deltaX] == 1) + angle = 180.0f; + else if ([event deltaX] == -1) + angle = 0.0f; + else if ([event deltaY] == 1) + angle = 90.0f; + else if ([event deltaY] == -1) + angle = 270.0f; + + QWindowSystemInterface::handleGestureEventWithRealValue(m_platformWindow->window(), QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), timestamp, Qt::SwipeNativeGesture, + angle, windowPoint, screenPoint); +} + +- (void)beginGestureWithEvent:(NSEvent *)event +{ + if (!m_platformWindow) + return; + + const NSTimeInterval timestamp = [event timestamp]; + QPointF windowPoint; + QPointF screenPoint; + [self convertFromScreen:[self screenMousePoint:event] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; + qCDebug(lcQpaGestures) << "beginGestureWithEvent @" << windowPoint << "from device" << hex << [event deviceID]; + QWindowSystemInterface::handleGestureEvent(m_platformWindow->window(), QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), timestamp, Qt::BeginNativeGesture, + windowPoint, screenPoint); +} + +- (void)endGestureWithEvent:(NSEvent *)event +{ + if (!m_platformWindow) + return; + + qCDebug(lcQpaGestures) << "endGestureWithEvent" << "from device" << hex << [event deviceID]; + const NSTimeInterval timestamp = [event timestamp]; + QPointF windowPoint; + QPointF screenPoint; + [self convertFromScreen:[self screenMousePoint:event] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; + QWindowSystemInterface::handleGestureEvent(m_platformWindow->window(), QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), timestamp, Qt::EndNativeGesture, + windowPoint, screenPoint); +} + +@end + +#endif // QT_NO_GESTURES diff --git a/src/plugins/platforms/cocoa/qnsview_keys.mm b/src/plugins/platforms/cocoa/qnsview_keys.mm new file mode 100644 index 0000000000..57ce6a009f --- /dev/null +++ b/src/plugins/platforms/cocoa/qnsview_keys.mm @@ -0,0 +1,262 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// This file is included from qnsview.mm, and only used to organize the code + +@implementation QT_MANGLE_NAMESPACE(QNSView) (KeysAPI) + ++ (Qt::KeyboardModifiers)convertKeyModifiers:(ulong)modifierFlags +{ + const bool dontSwapCtrlAndMeta = qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta); + Qt::KeyboardModifiers qtMods =Qt::NoModifier; + if (modifierFlags & NSShiftKeyMask) + qtMods |= Qt::ShiftModifier; + if (modifierFlags & NSControlKeyMask) + qtMods |= dontSwapCtrlAndMeta ? Qt::ControlModifier : Qt::MetaModifier; + if (modifierFlags & NSAlternateKeyMask) + qtMods |= Qt::AltModifier; + if (modifierFlags & NSCommandKeyMask) + qtMods |= dontSwapCtrlAndMeta ? Qt::MetaModifier : Qt::ControlModifier; + if (modifierFlags & NSNumericPadKeyMask) + qtMods |= Qt::KeypadModifier; + return qtMods; +} + +@end + +@implementation QT_MANGLE_NAMESPACE(QNSView) (Keys) + +- (int)convertKeyCode:(QChar)keyChar +{ + return qt_mac_cocoaKey2QtKey(keyChar); +} + +- (bool)handleKeyEvent:(NSEvent *)nsevent eventType:(int)eventType +{ + ulong timestamp = [nsevent timestamp] * 1000; + ulong nativeModifiers = [nsevent modifierFlags]; + Qt::KeyboardModifiers modifiers = [QNSView convertKeyModifiers: nativeModifiers]; + NSString *charactersIgnoringModifiers = [nsevent charactersIgnoringModifiers]; + NSString *characters = [nsevent characters]; + if (m_inputSource != characters) { + [m_inputSource release]; + m_inputSource = [characters retain]; + } + + // There is no way to get the scan code from carbon/cocoa. But we cannot + // use the value 0, since it indicates that the event originates from somewhere + // else than the keyboard. + quint32 nativeScanCode = 1; + quint32 nativeVirtualKey = [nsevent keyCode]; + + QChar ch = QChar::ReplacementCharacter; + int keyCode = Qt::Key_unknown; + + // If a dead key occurs as a result of pressing a key combination then + // characters will have 0 length, but charactersIgnoringModifiers will + // have a valid character in it. This enables key combinations such as + // ALT+E to be used as a shortcut with an English keyboard even though + // pressing ALT+E will give a dead key while doing normal text input. + if ([characters length] != 0 || [charactersIgnoringModifiers length] != 0) { + auto ctrlOrMetaModifier = qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta) ? Qt::ControlModifier : Qt::MetaModifier; + if (((modifiers & ctrlOrMetaModifier) || (modifiers & Qt::AltModifier)) && ([charactersIgnoringModifiers length] != 0)) + ch = QChar([charactersIgnoringModifiers characterAtIndex:0]); + else if ([characters length] != 0) + ch = QChar([characters characterAtIndex:0]); + keyCode = [self convertKeyCode:ch]; + } + + // we will send a key event unless the input method sets m_sendKeyEvent to false + m_sendKeyEvent = true; + QString text; + // ignore text for the U+F700-U+F8FF range. This is used by Cocoa when + // delivering function keys (e.g. arrow keys, backspace, F1-F35, etc.) + if (!(modifiers & (Qt::ControlModifier | Qt::MetaModifier)) && (ch.unicode() < 0xf700 || ch.unicode() > 0xf8ff)) + text = QString::fromNSString(characters); + + QWindow *window = [self topLevelWindow]; + + // Popups implicitly grab key events; forward to the active popup if there is one. + // This allows popups to e.g. intercept shortcuts and close the popup in response. + if (QCocoaWindow *popup = QCocoaIntegration::instance()->activePopupWindow()) { + if (!popup->window()->flags().testFlag(Qt::ToolTip)) + window = popup->window(); + } + + if (eventType == QEvent::KeyPress) { + + if (m_composingText.isEmpty()) { + m_sendKeyEvent = !QWindowSystemInterface::handleShortcutEvent(window, timestamp, keyCode, + modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers, text, [nsevent isARepeat], 1); + + // Handling a shortcut may result in closing the window + if (!m_platformWindow) + return true; + } + + QObject *fo = m_platformWindow->window()->focusObject(); + if (m_sendKeyEvent && fo) { + QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImHints); + if (QCoreApplication::sendEvent(fo, &queryEvent)) { + bool imEnabled = queryEvent.value(Qt::ImEnabled).toBool(); + Qt::InputMethodHints hints = static_cast<Qt::InputMethodHints>(queryEvent.value(Qt::ImHints).toUInt()); + if (imEnabled && !(hints & Qt::ImhDigitsOnly || hints & Qt::ImhFormattedNumbersOnly || hints & Qt::ImhHiddenText)) { + // pass the key event to the input method. note that m_sendKeyEvent may be set to false during this call + m_currentlyInterpretedKeyEvent = nsevent; + [self interpretKeyEvents:@[nsevent]]; + m_currentlyInterpretedKeyEvent = 0; + } + } + } + if (m_resendKeyEvent) + m_sendKeyEvent = true; + } + + bool accepted = true; + if (m_sendKeyEvent && m_composingText.isEmpty()) { + QWindowSystemInterface::handleExtendedKeyEvent(window, timestamp, QEvent::Type(eventType), keyCode, modifiers, + nativeScanCode, nativeVirtualKey, nativeModifiers, text, [nsevent isARepeat], 1, false); + accepted = QWindowSystemInterface::flushWindowSystemEvents(); + } + m_sendKeyEvent = false; + m_resendKeyEvent = false; + return accepted; +} + +- (void)keyDown:(NSEvent *)nsevent +{ + if ([self isTransparentForUserInput]) + return [super keyDown:nsevent]; + + const bool accepted = [self handleKeyEvent:nsevent eventType:int(QEvent::KeyPress)]; + + // When Qt is used to implement a plugin for a native application we + // want to propagate unhandled events to other native views. However, + // Qt does not always set the accepted state correctly (in particular + // for return key events), so do this for plugin applications only + // to prevent incorrect forwarding in the general case. + const bool shouldPropagate = QCoreApplication::testAttribute(Qt::AA_PluginApplication) && !accepted; + + // Track keyDown acceptance/forward state for later acceptance of the keyUp. + if (!shouldPropagate) + m_acceptedKeyDowns.insert([nsevent keyCode]); + + if (shouldPropagate) + [super keyDown:nsevent]; +} + +- (void)keyUp:(NSEvent *)nsevent +{ + if ([self isTransparentForUserInput]) + return [super keyUp:nsevent]; + + const bool keyUpAccepted = [self handleKeyEvent:nsevent eventType:int(QEvent::KeyRelease)]; + + // Propagate the keyUp if neither Qt accepted it nor the corresponding KeyDown was + // accepted. Qt text controls wil often not use and ignore keyUp events, but we + // want to avoid propagating unmatched keyUps. + const bool keyDownAccepted = m_acceptedKeyDowns.remove([nsevent keyCode]); + if (!keyUpAccepted && !keyDownAccepted) + [super keyUp:nsevent]; +} + +- (void)cancelOperation:(id)sender +{ + Q_UNUSED(sender); + + NSEvent *currentEvent = [NSApp currentEvent]; + if (!currentEvent || currentEvent.type != NSKeyDown) + return; + + // Handling the key event may recurse back here through interpretKeyEvents + // (when IM is enabled), so we need to guard against that. + if (currentEvent == m_currentlyInterpretedKeyEvent) + return; + + // Send Command+Key_Period and Escape as normal keypresses so that + // the key sequence is delivered through Qt. That way clients can + // intercept the shortcut and override its effect. + [self handleKeyEvent:currentEvent eventType:int(QEvent::KeyPress)]; +} + +- (void)flagsChanged:(NSEvent *)nsevent +{ + ulong timestamp = [nsevent timestamp] * 1000; + ulong modifiers = [nsevent modifierFlags]; + Qt::KeyboardModifiers qmodifiers = [QNSView convertKeyModifiers:modifiers]; + + // calculate the delta and remember the current modifiers for next time + static ulong m_lastKnownModifiers; + ulong lastKnownModifiers = m_lastKnownModifiers; + ulong delta = lastKnownModifiers ^ modifiers; + m_lastKnownModifiers = modifiers; + + struct qt_mac_enum_mapper + { + ulong mac_mask; + Qt::Key qt_code; + }; + static qt_mac_enum_mapper modifier_key_symbols[] = { + { NSShiftKeyMask, Qt::Key_Shift }, + { NSControlKeyMask, Qt::Key_Meta }, + { NSCommandKeyMask, Qt::Key_Control }, + { NSAlternateKeyMask, Qt::Key_Alt }, + { NSAlphaShiftKeyMask, Qt::Key_CapsLock }, + { 0ul, Qt::Key_unknown } }; + for (int i = 0; modifier_key_symbols[i].mac_mask != 0u; ++i) { + uint mac_mask = modifier_key_symbols[i].mac_mask; + if ((delta & mac_mask) == 0u) + continue; + + Qt::Key qtCode = modifier_key_symbols[i].qt_code; + if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) { + if (qtCode == Qt::Key_Meta) + qtCode = Qt::Key_Control; + else if (qtCode == Qt::Key_Control) + qtCode = Qt::Key_Meta; + } + QWindowSystemInterface::handleKeyEvent(m_platformWindow->window(), + timestamp, + (lastKnownModifiers & mac_mask) ? QEvent::KeyRelease : QEvent::KeyPress, + qtCode, + qmodifiers ^ [QNSView convertKeyModifiers:mac_mask]); + } +} + +@end diff --git a/src/plugins/platforms/cocoa/qnsview_mouse.mm b/src/plugins/platforms/cocoa/qnsview_mouse.mm new file mode 100644 index 0000000000..77af50119d --- /dev/null +++ b/src/plugins/platforms/cocoa/qnsview_mouse.mm @@ -0,0 +1,602 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// This file is included from qnsview.mm, and only used to organize the code + +@implementation QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper) { + QNSView *view; +} + +- (instancetype)initWithView:(QNSView *)theView +{ + if ((self = [super init])) + view = theView; + + return self; +} + +- (void)mouseMoved:(NSEvent *)theEvent +{ + [view mouseMovedImpl:theEvent]; +} + +- (void)mouseEntered:(NSEvent *)theEvent +{ + [view mouseEnteredImpl:theEvent]; +} + +- (void)mouseExited:(NSEvent *)theEvent +{ + [view mouseExitedImpl:theEvent]; +} + +- (void)cursorUpdate:(NSEvent *)theEvent +{ + [view cursorUpdate:theEvent]; +} + +@end + +@implementation QT_MANGLE_NAMESPACE(QNSView) (MouseAPI) + +- (void)resetMouseButtons +{ + m_buttons = Qt::NoButton; + m_frameStrutButtons = Qt::NoButton; +} + +- (void)handleFrameStrutMouseEvent:(NSEvent *)theEvent +{ + if (!m_platformWindow) + return; + + // get m_buttons in sync + // Don't send frme strut events if we are in the middle of a mouse drag. + if (m_buttons != Qt::NoButton) + return; + + NSEventType ty = [theEvent type]; + switch (ty) { + case NSLeftMouseDown: + m_frameStrutButtons |= Qt::LeftButton; + break; + case NSLeftMouseUp: + m_frameStrutButtons &= ~Qt::LeftButton; + break; + case NSRightMouseDown: + m_frameStrutButtons |= Qt::RightButton; + break; + case NSLeftMouseDragged: + m_frameStrutButtons |= Qt::LeftButton; + break; + case NSRightMouseDragged: + m_frameStrutButtons |= Qt::RightButton; + break; + case NSRightMouseUp: + m_frameStrutButtons &= ~Qt::RightButton; + break; + case NSOtherMouseDown: + m_frameStrutButtons |= cocoaButton2QtButton([theEvent buttonNumber]); + break; + case NSOtherMouseUp: + m_frameStrutButtons &= ~cocoaButton2QtButton([theEvent buttonNumber]); + default: + break; + } + + NSWindow *window = [self window]; + NSPoint windowPoint = [theEvent locationInWindow]; + + int windowScreenY = [window frame].origin.y + [window frame].size.height; + NSPoint windowCoord = [self convertPoint:[self frame].origin toView:nil]; + int viewScreenY = [window convertRectToScreen:NSMakeRect(windowCoord.x, windowCoord.y, 0, 0)].origin.y; + int titleBarHeight = windowScreenY - viewScreenY; + + NSPoint nsViewPoint = [self convertPoint: windowPoint fromView: nil]; + QPoint qtWindowPoint = QPoint(nsViewPoint.x, titleBarHeight + nsViewPoint.y); + NSPoint screenPoint = [window convertRectToScreen:NSMakeRect(windowPoint.x, windowPoint.y, 0, 0)].origin; + QPoint qtScreenPoint = QCocoaScreen::mapFromNative(screenPoint).toPoint(); + + ulong timestamp = [theEvent timestamp] * 1000; + QWindowSystemInterface::handleFrameStrutMouseEvent(m_platformWindow->window(), timestamp, qtWindowPoint, qtScreenPoint, m_frameStrutButtons); +} +@end + +@implementation QT_MANGLE_NAMESPACE(QNSView) (Mouse) + +- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent +{ + Q_UNUSED(theEvent) + if (!m_platformWindow) + return NO; + if ([self isTransparentForUserInput]) + return NO; + return YES; +} + +- (NSPoint)screenMousePoint:(NSEvent *)theEvent +{ + NSPoint screenPoint; + if (theEvent) { + NSPoint windowPoint = [theEvent locationInWindow]; + NSRect screenRect = [[theEvent window] convertRectToScreen:NSMakeRect(windowPoint.x, windowPoint.y, 1, 1)]; + screenPoint = screenRect.origin; + } else { + screenPoint = [NSEvent mouseLocation]; + } + return screenPoint; +} + +- (void)handleMouseEvent:(NSEvent *)theEvent +{ + if (!m_platformWindow) + return; + +#ifndef QT_NO_TABLETEVENT + // Tablet events may come in via the mouse event handlers, + // check if this is a valid tablet event first. + if ([self handleTabletEvent: theEvent]) + return; +#endif + + QPointF qtWindowPoint; + QPointF qtScreenPoint; + QNSView *targetView = self; + if (!targetView.platformWindow) + return; + + // Popups implicitly grap mouse events; forward to the active popup if there is one + if (QCocoaWindow *popup = QCocoaIntegration::instance()->activePopupWindow()) { + // Tooltips must be transparent for mouse events + // The bug reference is QTBUG-46379 + if (!popup->window()->flags().testFlag(Qt::ToolTip)) { + if (QNSView *popupView = qnsview_cast(popup->view())) + targetView = popupView; + } + } + + [targetView convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint]; + ulong timestamp = [theEvent timestamp] * 1000; + + QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); + nativeDrag->setLastMouseEvent(theEvent, self); + + Qt::KeyboardModifiers keyboardModifiers = [QNSView convertKeyModifiers:[theEvent modifierFlags]]; + QWindowSystemInterface::handleMouseEvent(targetView->m_platformWindow->window(), timestamp, qtWindowPoint, qtScreenPoint, + m_buttons, keyboardModifiers, Qt::MouseEventNotSynthesized); +} + +- (bool)handleMouseDownEvent:(NSEvent *)theEvent withButton:(int)buttonNumber +{ + if ([self isTransparentForUserInput]) + return false; + + Qt::MouseButton button = cocoaButton2QtButton(buttonNumber); + + QPointF qtWindowPoint; + QPointF qtScreenPoint; + [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint]; + Q_UNUSED(qtScreenPoint); + + // Maintain masked state for the button for use by MouseDragged and MouseUp. + QRegion mask = m_platformWindow->window()->mask(); + const bool masked = !mask.isEmpty() && !mask.contains(qtWindowPoint.toPoint()); + if (masked) + m_acceptedMouseDowns &= ~button; + else + m_acceptedMouseDowns |= button; + + // Forward masked out events to the next responder + if (masked) + return false; + + m_buttons |= button; + + [self handleMouseEvent:theEvent]; + return true; +} + +- (bool)handleMouseDraggedEvent:(NSEvent *)theEvent withButton:(int)buttonNumber +{ + if ([self isTransparentForUserInput]) + return false; + + Qt::MouseButton button = cocoaButton2QtButton(buttonNumber); + + // Forward the event to the next responder if Qt did not accept the + // corresponding mouse down for this button + if (!(m_acceptedMouseDowns & button) == button) + return false; + + [self handleMouseEvent:theEvent]; + return true; +} + +- (bool)handleMouseUpEvent:(NSEvent *)theEvent withButton:(int)buttonNumber +{ + if ([self isTransparentForUserInput]) + return false; + + Qt::MouseButton button = cocoaButton2QtButton(buttonNumber); + + // Forward the event to the next responder if Qt did not accept the + // corresponding mouse down for this button + if (!(m_acceptedMouseDowns & button) == button) + return false; + + if (m_sendUpAsRightButton && button == Qt::LeftButton) + button = Qt::RightButton; + if (button == Qt::RightButton) + m_sendUpAsRightButton = false; + + m_buttons &= ~button; + + [self handleMouseEvent:theEvent]; + return true; +} + +- (void)mouseDown:(NSEvent *)theEvent +{ + if ([self isTransparentForUserInput]) + return [super mouseDown:theEvent]; + m_sendUpAsRightButton = false; + + // Handle any active poup windows; clicking outisde them should close them + // all. Don't do anything or clicks inside one of the menus, let Cocoa + // handle that case. Note that in practice many windows of the Qt::Popup type + // will actually close themselves in this case using logic implemented in + // that particular poup type (for example context menus). However, Qt expects + // that plain popup QWindows will also be closed, so we implement the logic + // here as well. + QList<QCocoaWindow *> *popups = QCocoaIntegration::instance()->popupWindowStack(); + if (!popups->isEmpty()) { + // Check if the click is outside all popups. + bool inside = false; + QPointF qtScreenPoint = QCocoaScreen::mapFromNative([self screenMousePoint:theEvent]); + for (QList<QCocoaWindow *>::const_iterator it = popups->begin(); it != popups->end(); ++it) { + if ((*it)->geometry().contains(qtScreenPoint.toPoint())) { + inside = true; + break; + } + } + // Close the popups if the click was outside. + if (!inside) { + Qt::WindowType type = QCocoaIntegration::instance()->activePopupWindow()->window()->type(); + while (QCocoaWindow *popup = QCocoaIntegration::instance()->popPopupWindow()) { + QWindowSystemInterface::handleCloseEvent(popup->window()); + QWindowSystemInterface::flushWindowSystemEvents(); + } + // Consume the mouse event when closing the popup, except for tool tips + // were it's expected that the event is processed normally. + if (type != Qt::ToolTip) + return; + } + } + + QPointF qtWindowPoint; + QPointF qtScreenPoint; + [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint]; + Q_UNUSED(qtScreenPoint); + + QRegion mask = m_platformWindow->window()->mask(); + const bool masked = !mask.isEmpty() && !mask.contains(qtWindowPoint.toPoint()); + // Maintain masked state for the button for use by MouseDragged and Up. + if (masked) + m_acceptedMouseDowns &= ~Qt::LeftButton; + else + m_acceptedMouseDowns |= Qt::LeftButton; + + // Forward masked out events to the next responder + if (masked) { + [super mouseDown:theEvent]; + return; + } + + if ([self hasMarkedText]) { + [[NSTextInputContext currentInputContext] handleEvent:theEvent]; + } else { + auto ctrlOrMetaModifier = qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta) ? Qt::ControlModifier : Qt::MetaModifier; + if (!m_dontOverrideCtrlLMB && [QNSView convertKeyModifiers:[theEvent modifierFlags]] & ctrlOrMetaModifier) { + m_buttons |= Qt::RightButton; + m_sendUpAsRightButton = true; + } else { + m_buttons |= Qt::LeftButton; + } + [self handleMouseEvent:theEvent]; + } +} + +- (void)mouseDragged:(NSEvent *)theEvent +{ + const bool accepted = [self handleMouseDraggedEvent:theEvent withButton:[theEvent buttonNumber]]; + if (!accepted) + [super mouseDragged:theEvent]; +} + +- (void)mouseUp:(NSEvent *)theEvent +{ + const bool accepted = [self handleMouseUpEvent:theEvent withButton:[theEvent buttonNumber]]; + if (!accepted) + [super mouseUp:theEvent]; +} + +- (void)rightMouseDown:(NSEvent *)theEvent +{ + // Wacom tablet might not return the correct button number for NSEvent buttonNumber + // on right clicks. Decide here that the button is the "right" button and forward + // the button number to the mouse (and tablet) handler. + const bool accepted = [self handleMouseDownEvent:theEvent withButton:1]; + if (!accepted) + [super rightMouseDown:theEvent]; +} + +- (void)rightMouseDragged:(NSEvent *)theEvent +{ + const bool accepted = [self handleMouseDraggedEvent:theEvent withButton:1]; + if (!accepted) + [super rightMouseDragged:theEvent]; +} + +- (void)rightMouseUp:(NSEvent *)theEvent +{ + const bool accepted = [self handleMouseUpEvent:theEvent withButton:1]; + if (!accepted) + [super rightMouseUp:theEvent]; +} + +- (void)otherMouseDown:(NSEvent *)theEvent +{ + const bool accepted = [self handleMouseDownEvent:theEvent withButton:[theEvent buttonNumber]]; + if (!accepted) + [super otherMouseDown:theEvent]; +} + +- (void)otherMouseDragged:(NSEvent *)theEvent +{ + const bool accepted = [self handleMouseDraggedEvent:theEvent withButton:[theEvent buttonNumber]]; + if (!accepted) + [super otherMouseDragged:theEvent]; +} + +- (void)otherMouseUp:(NSEvent *)theEvent +{ + const bool accepted = [self handleMouseUpEvent:theEvent withButton:[theEvent buttonNumber]]; + if (!accepted) + [super otherMouseUp:theEvent]; +} + +- (void)updateTrackingAreas +{ + [super updateTrackingAreas]; + + QMacAutoReleasePool pool; + + // NSTrackingInVisibleRect keeps care of updating once the tracking is set up, so bail out early + if (m_trackingArea && [[self trackingAreas] containsObject:m_trackingArea]) + return; + + // Ideally, we shouldn't have NSTrackingMouseMoved events included below, it should + // only be turned on if mouseTracking, hover is on or a tool tip is set. + // Unfortunately, Qt will send "tooltip" events on mouse moves, so we need to + // turn it on in ALL case. That means EVERY QWindow gets to pay the cost of + // mouse moves delivered to it (Apple recommends keeping it OFF because there + // is a performance hit). So it goes. + NSUInteger trackingOptions = NSTrackingMouseEnteredAndExited | NSTrackingActiveInActiveApp + | NSTrackingInVisibleRect | NSTrackingMouseMoved | NSTrackingCursorUpdate; + [m_trackingArea release]; + m_trackingArea = [[NSTrackingArea alloc] initWithRect:[self frame] + options:trackingOptions + owner:m_mouseMoveHelper + userInfo:nil]; + [self addTrackingArea:m_trackingArea]; +} + +- (void)cursorUpdate:(NSEvent *)theEvent +{ + qCDebug(lcQpaMouse) << "[QNSView cursorUpdate:]" << self.cursor; + + // Note: We do not get this callback when moving from a subview that + // uses the legacy cursorRect API, so the cursor is reset to the arrow + // cursor. See rdar://34183708 + + if (self.cursor) + [self.cursor set]; + else + [super cursorUpdate:theEvent]; +} + +- (void)mouseMovedImpl:(NSEvent *)theEvent +{ + if (!m_platformWindow) + return; + + if ([self isTransparentForUserInput]) + return; + + QPointF windowPoint; + QPointF screenPoint; + [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; + QWindow *childWindow = m_platformWindow->childWindowAt(windowPoint.toPoint()); + + // Top-level windows generate enter-leave events for sub-windows. + // Qt wants to know which window (if any) will be entered at the + // the time of the leave. This is dificult to accomplish by + // handling mouseEnter and mouseLeave envents, since they are sent + // individually to different views. + if (m_platformWindow->isContentView() && childWindow) { + if (childWindow != m_platformWindow->m_enterLeaveTargetWindow) { + QWindowSystemInterface::handleEnterLeaveEvent(childWindow, m_platformWindow->m_enterLeaveTargetWindow, windowPoint, screenPoint); + m_platformWindow->m_enterLeaveTargetWindow = childWindow; + } + } + + // Cocoa keeps firing mouse move events for obscured parent views. Qt should not + // send those events so filter them out here. + if (childWindow != m_platformWindow->window()) + return; + + [self handleMouseEvent: theEvent]; +} + +- (void)mouseEnteredImpl:(NSEvent *)theEvent +{ + Q_UNUSED(theEvent) + if (!m_platformWindow) + return; + + m_platformWindow->m_windowUnderMouse = true; + + if ([self isTransparentForUserInput]) + return; + + // Top-level windows generate enter events for sub-windows. + if (!m_platformWindow->isContentView()) + return; + + QPointF windowPoint; + QPointF screenPoint; + [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; + m_platformWindow->m_enterLeaveTargetWindow = m_platformWindow->childWindowAt(windowPoint.toPoint()); + QWindowSystemInterface::handleEnterEvent(m_platformWindow->m_enterLeaveTargetWindow, windowPoint, screenPoint); +} + +- (void)mouseExitedImpl:(NSEvent *)theEvent +{ + Q_UNUSED(theEvent); + if (!m_platformWindow) + return; + + m_platformWindow->m_windowUnderMouse = false; + + if ([self isTransparentForUserInput]) + return; + + // Top-level windows generate leave events for sub-windows. + if (!m_platformWindow->isContentView()) + return; + + QWindowSystemInterface::handleLeaveEvent(m_platformWindow->m_enterLeaveTargetWindow); + m_platformWindow->m_enterLeaveTargetWindow = 0; +} + +#if QT_CONFIG(wheelevent) +- (void)scrollWheel:(NSEvent *)theEvent +{ + if (!m_platformWindow) + return; + + if ([self isTransparentForUserInput]) + return [super scrollWheel:theEvent]; + + QPoint angleDelta; + Qt::MouseEventSource source = Qt::MouseEventNotSynthesized; + if ([theEvent hasPreciseScrollingDeltas]) { + // The mouse device contains pixel scroll wheel support (Mighty Mouse, Trackpad). + // Since deviceDelta is delivered as pixels rather than degrees, we need to + // convert from pixels to degrees in a sensible manner. + // It looks like 1/4 degrees per pixel behaves most native. + // (NB: Qt expects the unit for delta to be 8 per degree): + const int pixelsToDegrees = 2; // 8 * 1/4 + angleDelta.setX([theEvent scrollingDeltaX] * pixelsToDegrees); + angleDelta.setY([theEvent scrollingDeltaY] * pixelsToDegrees); + source = Qt::MouseEventSynthesizedBySystem; + } else { + // Remove acceleration, and use either -120 or 120 as delta: + angleDelta.setX(qBound(-120, int([theEvent deltaX] * 10000), 120)); + angleDelta.setY(qBound(-120, int([theEvent deltaY] * 10000), 120)); + } + + QPoint pixelDelta; + if ([theEvent hasPreciseScrollingDeltas]) { + pixelDelta.setX([theEvent scrollingDeltaX]); + pixelDelta.setY([theEvent scrollingDeltaY]); + } else { + // docs: "In the case of !hasPreciseScrollingDeltas, multiply the delta with the line width." + // scrollingDeltaX seems to return a minimum value of 0.1 in this case, map that to two pixels. + const CGFloat lineWithEstimate = 20.0; + pixelDelta.setX([theEvent scrollingDeltaX] * lineWithEstimate); + pixelDelta.setY([theEvent scrollingDeltaY] * lineWithEstimate); + } + + QPointF qt_windowPoint; + QPointF qt_screenPoint; + [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qt_windowPoint andScreenPoint:&qt_screenPoint]; + NSTimeInterval timestamp = [theEvent timestamp]; + ulong qt_timestamp = timestamp * 1000; + + // Prevent keyboard modifier state from changing during scroll event streams. + // A two-finger trackpad flick generates a stream of scroll events. We want + // the keyboard modifier state to be the state at the beginning of the + // flick in order to avoid changing the interpretation of the events + // mid-stream. One example of this happening would be when pressing cmd + // after scrolling in Qt Creator: not taking the phase into account causes + // the end of the event stream to be interpreted as font size changes. + NSEventPhase momentumPhase = [theEvent momentumPhase]; + if (momentumPhase == NSEventPhaseNone) { + currentWheelModifiers = [QNSView convertKeyModifiers:[theEvent modifierFlags]]; + } + + NSEventPhase phase = [theEvent phase]; + Qt::ScrollPhase ph = Qt::ScrollUpdate; + + // MayBegin is likely to happen. We treat it the same as an actual begin. + if (phase == NSEventPhaseMayBegin) { + m_scrolling = true; + ph = Qt::ScrollBegin; + } else if (phase == NSEventPhaseBegan) { + // If MayBegin did not happen, Began is the actual beginning. + if (!m_scrolling) + ph = Qt::ScrollBegin; + m_scrolling = true; + } else if (phase == NSEventPhaseEnded || phase == NSEventPhaseCancelled || + momentumPhase == NSEventPhaseEnded || momentumPhase == NSEventPhaseCancelled) { + ph = Qt::ScrollEnd; + m_scrolling = false; + } else if (phase == NSEventPhaseNone && momentumPhase == NSEventPhaseNone) { + ph = Qt::NoScrollPhase; + } + // "isInverted": natural OS X scrolling, inverted from the Qt/other platform/Jens perspective. + bool isInverted = [theEvent isDirectionInvertedFromDevice]; + + qCDebug(lcQpaMouse) << "scroll wheel @ window pos" << qt_windowPoint << "delta px" << pixelDelta << "angle" << angleDelta << "phase" << ph << (isInverted ? "inverted" : ""); + QWindowSystemInterface::handleWheelEvent(m_platformWindow->window(), qt_timestamp, qt_windowPoint, qt_screenPoint, pixelDelta, angleDelta, currentWheelModifiers, ph, source, isInverted); +} +#endif // QT_CONFIG(wheelevent) + +@end diff --git a/src/plugins/platforms/cocoa/qnsview_tablet.mm b/src/plugins/platforms/cocoa/qnsview_tablet.mm new file mode 100644 index 0000000000..0b56123955 --- /dev/null +++ b/src/plugins/platforms/cocoa/qnsview_tablet.mm @@ -0,0 +1,223 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// This file is included from qnsview.mm, and only used to organize the code + +#ifndef QT_NO_TABLETEVENT + +Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") + +struct QCocoaTabletDeviceData +{ + QTabletEvent::TabletDevice device; + QTabletEvent::PointerType pointerType; + uint capabilityMask; + qint64 uid; +}; + +typedef QHash<uint, QCocoaTabletDeviceData> QCocoaTabletDeviceDataHash; +Q_GLOBAL_STATIC(QCocoaTabletDeviceDataHash, tabletDeviceDataHash) + +@implementation QT_MANGLE_NAMESPACE(QNSView) (Tablet) + +- (bool)handleTabletEvent:(NSEvent *)theEvent +{ + static bool ignoreButtonMapping = qEnvironmentVariableIsSet("QT_MAC_TABLET_IGNORE_BUTTON_MAPPING"); + + if (!m_platformWindow) + return false; + + NSEventType eventType = [theEvent type]; + if (eventType != NSTabletPoint && [theEvent subtype] != NSTabletPointEventSubtype) + return false; // Not a tablet event. + + ulong timestamp = [theEvent timestamp] * 1000; + + QPointF windowPoint; + QPointF screenPoint; + [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint: &windowPoint andScreenPoint: &screenPoint]; + + uint deviceId = [theEvent deviceID]; + if (!tabletDeviceDataHash->contains(deviceId)) { + // Error: Unknown tablet device. Qt also gets into this state + // when running on a VM. This appears to be harmless; don't + // print a warning. + return false; + } + const QCocoaTabletDeviceData &deviceData = tabletDeviceDataHash->value(deviceId); + + bool down = (eventType != NSMouseMoved); + + qreal pressure; + if (down) { + pressure = [theEvent pressure]; + } else { + pressure = 0.0; + } + + NSPoint tilt = [theEvent tilt]; + int xTilt = qRound(tilt.x * 60.0); + int yTilt = qRound(tilt.y * -60.0); + qreal tangentialPressure = 0; + qreal rotation = 0; + int z = 0; + if (deviceData.capabilityMask & 0x0200) + z = [theEvent absoluteZ]; + + if (deviceData.capabilityMask & 0x0800) + tangentialPressure = ([theEvent tangentialPressure] * 2.0) - 1.0; + + rotation = 360.0 - [theEvent rotation]; + if (rotation > 180.0) + rotation -= 360.0; + + Qt::KeyboardModifiers keyboardModifiers = [QNSView convertKeyModifiers:[theEvent modifierFlags]]; + Qt::MouseButtons buttons = ignoreButtonMapping ? static_cast<Qt::MouseButtons>(static_cast<uint>([theEvent buttonMask])) : m_buttons; + + qCDebug(lcQpaTablet, "event on tablet %d with tool %d type %d unique ID %lld pos %6.1f, %6.1f root pos %6.1f, %6.1f buttons 0x%x pressure %4.2lf tilt %d, %d rotation %6.2lf", + deviceId, deviceData.device, deviceData.pointerType, deviceData.uid, + windowPoint.x(), windowPoint.y(), screenPoint.x(), screenPoint.y(), + static_cast<uint>(buttons), pressure, xTilt, yTilt, rotation); + + QWindowSystemInterface::handleTabletEvent(m_platformWindow->window(), timestamp, windowPoint, screenPoint, + deviceData.device, deviceData.pointerType, buttons, pressure, xTilt, yTilt, + tangentialPressure, rotation, z, deviceData.uid, + keyboardModifiers); + return true; +} + +- (void)tabletPoint:(NSEvent *)theEvent +{ + if ([self isTransparentForUserInput]) + return [super tabletPoint:theEvent]; + + [self handleTabletEvent: theEvent]; +} + +static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) +{ + qint64 uid = [theEvent uniqueID]; + uint bits = [theEvent vendorPointingDeviceType]; + if (bits == 0 && uid != 0) { + // Fallback. It seems that the driver doesn't always include all the information. + // High-End Wacom devices store their "type" in the uper bits of the Unique ID. + // I'm not sure how to handle it for consumer devices, but I'll test that in a bit. + bits = uid >> 32; + } + + QTabletEvent::TabletDevice device; + // Defined in the "EN0056-NxtGenImpGuideX" + // on Wacom's Developer Website (www.wacomeng.com) + if (((bits & 0x0006) == 0x0002) && ((bits & 0x0F06) != 0x0902)) { + device = QTabletEvent::Stylus; + } else { + switch (bits & 0x0F06) { + case 0x0802: + device = QTabletEvent::Stylus; + break; + case 0x0902: + device = QTabletEvent::Airbrush; + break; + case 0x0004: + device = QTabletEvent::FourDMouse; + break; + case 0x0006: + device = QTabletEvent::Puck; + break; + case 0x0804: + device = QTabletEvent::RotationStylus; + break; + default: + device = QTabletEvent::NoDevice; + } + } + return device; +} + +- (void)tabletProximity:(NSEvent *)theEvent +{ + if ([self isTransparentForUserInput]) + return [super tabletProximity:theEvent]; + + ulong timestamp = [theEvent timestamp] * 1000; + + QCocoaTabletDeviceData deviceData; + deviceData.uid = [theEvent uniqueID]; + deviceData.capabilityMask = [theEvent capabilityMask]; + + switch ([theEvent pointingDeviceType]) { + case NSUnknownPointingDevice: + default: + deviceData.pointerType = QTabletEvent::UnknownPointer; + break; + case NSPenPointingDevice: + deviceData.pointerType = QTabletEvent::Pen; + break; + case NSCursorPointingDevice: + deviceData.pointerType = QTabletEvent::Cursor; + break; + case NSEraserPointingDevice: + deviceData.pointerType = QTabletEvent::Eraser; + break; + } + + deviceData.device = wacomTabletDevice(theEvent); + + // The deviceID is "unique" while in the proximity, it's a key that we can use for + // linking up QCocoaTabletDeviceData to an event (especially if there are two devices in action). + bool entering = [theEvent isEnteringProximity]; + uint deviceId = [theEvent deviceID]; + if (entering) { + tabletDeviceDataHash->insert(deviceId, deviceData); + } else { + tabletDeviceDataHash->remove(deviceId); + } + + qCDebug(lcQpaTablet, "proximity change on tablet %d: current tool %d type %d unique ID %lld", + deviceId, deviceData.device, deviceData.pointerType, deviceData.uid); + + if (entering) { + QWindowSystemInterface::handleTabletEnterProximityEvent(timestamp, deviceData.device, deviceData.pointerType, deviceData.uid); + } else { + QWindowSystemInterface::handleTabletLeaveProximityEvent(timestamp, deviceData.device, deviceData.pointerType, deviceData.uid); + } +} +@end + +#endif diff --git a/src/plugins/platforms/cocoa/qnsview_touch.mm b/src/plugins/platforms/cocoa/qnsview_touch.mm new file mode 100644 index 0000000000..e789213f70 --- /dev/null +++ b/src/plugins/platforms/cocoa/qnsview_touch.mm @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// This file is included from qnsview.mm, and only used to organize the code + +Q_LOGGING_CATEGORY(lcQpaTouch, "qt.qpa.input.touch") + +@implementation QT_MANGLE_NAMESPACE(QNSView) (Touch) + +- (bool)shouldSendSingleTouch +{ + if (!m_platformWindow) + return true; + + // QtWidgets expects single-point touch events, QtDeclarative does not. + // Until there is an API we solve this by looking at the window class type. + return m_platformWindow->window()->inherits("QWidgetWindow"); +} + +- (void)touchesBeganWithEvent:(NSEvent *)event +{ + if (!m_platformWindow) + return; + + const NSTimeInterval timestamp = [event timestamp]; + const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, [self shouldSendSingleTouch]); + qCDebug(lcQpaTouch) << "touchesBeganWithEvent" << points << "from device" << hex << [event deviceID]; + QWindowSystemInterface::handleTouchEvent(m_platformWindow->window(), timestamp * 1000, QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), points); +} + +- (void)touchesMovedWithEvent:(NSEvent *)event +{ + if (!m_platformWindow) + return; + + const NSTimeInterval timestamp = [event timestamp]; + const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, [self shouldSendSingleTouch]); + qCDebug(lcQpaTouch) << "touchesMovedWithEvent" << points << "from device" << hex << [event deviceID]; + QWindowSystemInterface::handleTouchEvent(m_platformWindow->window(), timestamp * 1000, QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), points); +} + +- (void)touchesEndedWithEvent:(NSEvent *)event +{ + if (!m_platformWindow) + return; + + const NSTimeInterval timestamp = [event timestamp]; + const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, [self shouldSendSingleTouch]); + qCDebug(lcQpaTouch) << "touchesEndedWithEvent" << points << "from device" << hex << [event deviceID]; + QWindowSystemInterface::handleTouchEvent(m_platformWindow->window(), timestamp * 1000, QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), points); +} + +- (void)touchesCancelledWithEvent:(NSEvent *)event +{ + if (!m_platformWindow) + return; + + const NSTimeInterval timestamp = [event timestamp]; + const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, [self shouldSendSingleTouch]); + qCDebug(lcQpaTouch) << "touchesCancelledWithEvent" << points << "from device" << hex << [event deviceID]; + QWindowSystemInterface::handleTouchEvent(m_platformWindow->window(), timestamp * 1000, QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), points); +} + +@end diff --git a/src/plugins/platforms/cocoa/qnsviewaccessibility.mm b/src/plugins/platforms/cocoa/qnsviewaccessibility.mm index 645a93edf7..ab0036e175 100644 --- a/src/plugins/platforms/cocoa/qnsviewaccessibility.mm +++ b/src/plugins/platforms/cocoa/qnsviewaccessibility.mm @@ -53,14 +53,12 @@ @implementation QNSView (QNSViewAccessibility) - (id)childAccessibleElement { - if (m_platformWindow.isNull()) + QCocoaWindow *platformWindow = self.platformWindow; + if (!platformWindow || !platformWindow->window()->accessibleRoot()) return nil; - if (!m_platformWindow->window()->accessibleRoot()) - return nil; - - QAccessible::Id childId = QAccessible::uniqueId(m_platformWindow->window()->accessibleRoot()); - return [QMacAccessibilityElement elementWithId: childId]; + QAccessible::Id childId = QAccessible::uniqueId(platformWindow->window()->accessibleRoot()); + return [QMacAccessibilityElement elementWithId:childId]; } // The QNSView is a container that the user does not interact directly with: diff --git a/src/plugins/platforms/cocoa/qnswindow.mm b/src/plugins/platforms/cocoa/qnswindow.mm index cb13b7d184..c959fdd917 100644 --- a/src/plugins/platforms/cocoa/qnswindow.mm +++ b/src/plugins/platforms/cocoa/qnswindow.mm @@ -38,7 +38,6 @@ ****************************************************************************/ #include "qnswindow.h" -#include "qnswindowdelegate.h" #include "qcocoawindow.h" #include "qcocoahelpers.h" #include "qcocoaeventdispatcher.h" @@ -46,7 +45,7 @@ #include <qpa/qwindowsysteminterface.h> #include <qoperatingsystemversion.h> -Q_LOGGING_CATEGORY(lcCocoaEvents, "qt.qpa.cocoa.events"); +Q_LOGGING_CATEGORY(lcQpaEvents, "qt.qpa.events"); static bool isMouseEvent(NSEvent *ev) { @@ -72,7 +71,7 @@ static bool isMouseEvent(NSEvent *ev) [center addObserverForName:NSWindowDidEnterFullScreenNotification object:nil queue:nil usingBlock:^(NSNotification *notification) { objc_setAssociatedObject(notification.object, @selector(qt_fullScreen), - [NSNumber numberWithBool:YES], OBJC_ASSOCIATION_RETAIN); + @(YES), OBJC_ASSOCIATION_RETAIN); } ]; [center addObserverForName:NSWindowDidExitFullScreenNotification object:nil queue:nil @@ -202,7 +201,7 @@ static bool isMouseEvent(NSEvent *ev) - (void)sendEvent:(NSEvent*)theEvent { - qCDebug(lcCocoaEvents) << "Sending" << theEvent << "to" << self; + qCDebug(lcQpaEvents) << "Sending" << theEvent << "to" << self; // We might get events for a NSWindow after the corresponding platform // window has been deleted, as the NSWindow can outlive the QCocoaWindow @@ -239,7 +238,7 @@ static bool isMouseEvent(NSEvent *ev) - (void)closeAndRelease { - qCDebug(lcQpaCocoaWindow) << "closeAndRelease" << self; + qCDebug(lcQpaWindow) << "closeAndRelease" << self; [self.delegate release]; self.delegate = nil; @@ -252,7 +251,7 @@ static bool isMouseEvent(NSEvent *ev) #pragma clang diagnostic ignored "-Wobjc-missing-super-calls" - (void)dealloc { - qCDebug(lcQpaCocoaWindow) << "dealloc" << self; + qCDebug(lcQpaWindow) << "dealloc" << self; qt_objcDynamicSuper(); } #pragma clang diagnostic pop @@ -267,14 +266,14 @@ static bool isMouseEvent(NSEvent *ev) if (__builtin_available(macOS 10.12, *)) { // Unfortunately there's no NSWindowListOrderedBackToFront, // so we have to manually reverse the order using an array. - NSMutableArray *windows = [[[NSMutableArray alloc] init] autorelease]; + NSMutableArray<NSWindow *> *windows = [NSMutableArray<NSWindow *> new]; [application enumerateWindowsWithOptions:NSWindowListOrderedFrontToBack usingBlock:^(NSWindow *window, BOOL *) { // For some reason AppKit will give us nil-windows, skip those if (!window) return; - [(NSMutableArray*)windows addObject:window]; + [windows addObject:window]; } ]; diff --git a/src/plugins/platforms/cocoa/qnswindowdelegate.h b/src/plugins/platforms/cocoa/qnswindowdelegate.h index d2078b5786..e71afcbb2a 100644 --- a/src/plugins/platforms/cocoa/qnswindowdelegate.h +++ b/src/plugins/platforms/cocoa/qnswindowdelegate.h @@ -41,20 +41,16 @@ #define QNSWINDOWDELEGATE_H #include <AppKit/AppKit.h> +#include <QtCore/private/qcore_mac_p.h> -#include "qcocoawindow.h" +QT_BEGIN_NAMESPACE +class QCocoaWindow; +QT_END_NAMESPACE @interface QT_MANGLE_NAMESPACE(QNSWindowDelegate) : NSObject <NSWindowDelegate> -{ - QCocoaWindow *m_cocoaWindow; -} -- (id)initWithQCocoaWindow:(QCocoaWindow *)cocoaWindow; +- (instancetype)initWithQCocoaWindow:(QT_PREPEND_NAMESPACE(QCocoaWindow) *)cocoaWindow; -- (BOOL)windowShouldClose:(NSNotification *)notification; - -- (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu; -- (BOOL)window:(NSWindow *)window shouldDragDocumentWithEvent:(NSEvent *)event from:(NSPoint)dragImageLocation withPasteboard:(NSPasteboard *)pasteboard; @end QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSWindowDelegate); diff --git a/src/plugins/platforms/cocoa/qnswindowdelegate.mm b/src/plugins/platforms/cocoa/qnswindowdelegate.mm index 6e5623d679..1888dc1c90 100644 --- a/src/plugins/platforms/cocoa/qnswindowdelegate.mm +++ b/src/plugins/platforms/cocoa/qnswindowdelegate.mm @@ -39,20 +39,23 @@ #include "qnswindowdelegate.h" #include "qcocoahelpers.h" +#include "qcocoawindow.h" #include <QDebug> +#include <QtCore/private/qcore_mac_p.h> #include <qpa/qplatformscreen.h> #include <qpa/qwindowsysteminterface.h> static QRegExp whitespaceRegex = QRegExp(QStringLiteral("\\s*")); -@implementation QNSWindowDelegate +@implementation QNSWindowDelegate { + QCocoaWindow *m_cocoaWindow; +} -- (id)initWithQCocoaWindow:(QCocoaWindow *)cocoaWindow +- (instancetype)initWithQCocoaWindow:(QCocoaWindow *)cocoaWindow { - if (self = [super init]) + if ((self = [self init])) m_cocoaWindow = cocoaWindow; - return self; } diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp index 4742143121..e773e85433 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp @@ -47,6 +47,7 @@ #include <QtCore/QLoggingCategory> #include <QtGui/private/qguiapplication_p.h> +#include <QtGui/private/qtguiglobal_p.h> #include <QtFbSupport/private/qfbvthandler_p.h> #include <errno.h> @@ -210,17 +211,29 @@ void QEglFSKmsGbmScreen::ensureModeSet(uint32_t fb) if (doModeSet) { qCDebug(qLcEglfsKmsDebug, "Setting mode for screen %s", qPrintable(name())); - int ret = drmModeSetCrtc(fd, - op.crtc_id, - fb, - 0, 0, - &op.connector_id, 1, - &op.modes[op.mode]); - - if (ret == 0) - setPowerState(PowerStateOn); - else - qErrnoWarning(errno, "Could not set DRM mode for screen %s", qPrintable(name())); + + if (device()->hasAtomicSupport()) { +#if QT_CONFIG(drm_atomic) + drmModeAtomicReq *request = device()->atomic_request(); + if (request) { + drmModeAtomicAddProperty(request, op.connector_id, op.crtcIdPropertyId, op.crtc_id); + drmModeAtomicAddProperty(request, op.crtc_id, op.modeIdPropertyId, op.mode_blob_id); + drmModeAtomicAddProperty(request, op.crtc_id, op.activePropertyId, 1); + } +#endif + } else { + int ret = drmModeSetCrtc(fd, + op.crtc_id, + fb, + 0, 0, + &op.connector_id, 1, + &op.modes[op.mode]); + + if (ret == 0) + setPowerState(PowerStateOn); + else + qErrnoWarning(errno, "Could not set DRM mode for screen %s", qPrintable(name())); + } } } } @@ -243,6 +256,11 @@ void QEglFSKmsGbmScreen::waitForFlip() drmEvent.page_flip_handler = pageFlipHandler; drmHandleEvent(device()->fd(), &drmEvent); } + +#if QT_CONFIG(drm_atomic) + if (device()->hasAtomicSupport()) + device()->atomicReset(); +#endif } void QEglFSKmsGbmScreen::flip() @@ -274,34 +292,63 @@ void QEglFSKmsGbmScreen::flip() QKmsOutput &op(output()); const int fd = device()->fd(); m_flipPending = true; - int ret = drmModePageFlip(fd, + + if (device()->hasAtomicSupport()) { +#if QT_CONFIG(drm_atomic) + drmModeAtomicReq *request = device()->atomic_request(); + if (request) { + drmModeAtomicAddProperty(request, op.eglfs_plane->id, op.eglfs_plane->framebufferPropertyId, fb->fb); + drmModeAtomicAddProperty(request, op.eglfs_plane->id, op.eglfs_plane->crtcPropertyId, op.crtc_id); + } +#endif + } else { + int ret = drmModePageFlip(fd, op.crtc_id, fb->fb, DRM_MODE_PAGE_FLIP_EVENT, this); - if (ret) { - qErrnoWarning("Could not queue DRM page flip on screen %s", qPrintable(name())); - m_flipPending = false; - gbm_surface_release_buffer(m_gbm_surface, m_gbm_bo_next); - m_gbm_bo_next = nullptr; - return; + if (ret) { + qErrnoWarning("Could not queue DRM page flip on screen %s", qPrintable(name())); + m_flipPending = false; + gbm_surface_release_buffer(m_gbm_surface, m_gbm_bo_next); + m_gbm_bo_next = nullptr; + return; + } } for (CloneDestination &d : m_cloneDests) { if (d.screen != this) { d.screen->ensureModeSet(fb->fb); d.cloneFlipPending = true; - int ret = drmModePageFlip(fd, - d.screen->output().crtc_id, - fb->fb, - DRM_MODE_PAGE_FLIP_EVENT, - d.screen); - if (ret) { - qErrnoWarning("Could not queue DRM page flip for clone screen %s", qPrintable(name())); - d.cloneFlipPending = false; + + if (device()->hasAtomicSupport()) { +#if QT_CONFIG(drm_atomic) + drmModeAtomicReq *request = device()->atomic_request(); + if (request) { + drmModeAtomicAddProperty(request, d.screen->output().eglfs_plane->id, + d.screen->output().eglfs_plane->framebufferPropertyId, fb->fb); + drmModeAtomicAddProperty(request, d.screen->output().eglfs_plane->id, + d.screen->output().eglfs_plane->crtcPropertyId, op.crtc_id); + } +#endif + } else { + int ret = drmModePageFlip(fd, + d.screen->output().crtc_id, + fb->fb, + DRM_MODE_PAGE_FLIP_EVENT, + d.screen); + if (ret) { + qErrnoWarning("Could not queue DRM page flip for clone screen %s", qPrintable(name())); + d.cloneFlipPending = false; + } } } } + +#if QT_CONFIG(drm_atomic) + if (device()->hasAtomicSupport()) + device()->atomicCommit(this); +#endif } void QEglFSKmsGbmScreen::pageFlipHandler(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, void *user_data) diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.cpp index 06bc272050..a6aac61506 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.cpp @@ -138,6 +138,10 @@ void *QEglFSKmsIntegration::nativeResourceForIntegration(const QByteArray &name) if (name == QByteArrayLiteral("dri_fd") && m_device) return (void *) (qintptr) m_device->fd(); +#if QT_CONFIG(drm_atomic) + if (name == QByteArrayLiteral("dri_atomic_request") && m_device) + return (void *) (qintptr) m_device->atomic_request(); +#endif return nullptr; } @@ -147,6 +151,8 @@ void *QEglFSKmsIntegration::nativeResourceForScreen(const QByteArray &resource, if (s) { if (resource == QByteArrayLiteral("dri_crtcid")) return (void *) (qintptr) s->output().crtc_id; + if (resource == QByteArrayLiteral("dri_connectorid")) + return (void *) (qintptr) s->output().connector_id; } return nullptr; } diff --git a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.h b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.h index c52d498cd4..201b277494 100644 --- a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.h +++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.h @@ -41,8 +41,6 @@ #include "../../qiosfiledialog.h" -@interface QIOSImagePickerController : UIImagePickerController <UIImagePickerControllerDelegate, UINavigationControllerDelegate> { - QIOSFileDialog *m_fileDialog; -} -- (id)initWithQIOSFileDialog:(QIOSFileDialog *)fileDialog; +@interface QIOSImagePickerController : UIImagePickerController <UIImagePickerControllerDelegate, UINavigationControllerDelegate> +- (instancetype)initWithQIOSFileDialog:(QIOSFileDialog *)fileDialog; @end diff --git a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.mm b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.mm index 78e0f00ab4..79d4ecf83f 100644 --- a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.mm +++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.mm @@ -41,15 +41,17 @@ #include "qiosimagepickercontroller.h" -@implementation QIOSImagePickerController +@implementation QIOSImagePickerController { + QIOSFileDialog *m_fileDialog; +} -- (id)initWithQIOSFileDialog:(QIOSFileDialog *)fileDialog +- (instancetype)initWithQIOSFileDialog:(QIOSFileDialog *)fileDialog { self = [super init]; if (self) { m_fileDialog = fileDialog; - [self setSourceType:UIImagePickerControllerSourceTypePhotoLibrary]; - [self setDelegate:self]; + self.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; + self.delegate = self; } return self; } @@ -57,8 +59,8 @@ - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { Q_UNUSED(picker); - NSURL *url = [info objectForKey:UIImagePickerControllerReferenceURL]; - QUrl fileUrl = QUrl::fromLocalFile(QString::fromNSString([url description])); + NSURL *url = info[UIImagePickerControllerReferenceURL]; + QUrl fileUrl = QUrl::fromLocalFile(QString::fromNSString(url.description)); m_fileDialog->selectedFilesChanged(QList<QUrl>() << fileUrl); emit m_fileDialog->accept(); } diff --git a/src/plugins/platforms/ios/qiosclipboard.mm b/src/plugins/platforms/ios/qiosclipboard.mm index 9a975eadc9..6bdbf94d3f 100644 --- a/src/plugins/platforms/ios/qiosclipboard.mm +++ b/src/plugins/platforms/ios/qiosclipboard.mm @@ -46,11 +46,11 @@ #include <QtGui/QGuiApplication> @interface UIPasteboard (QUIPasteboard) - + (UIPasteboard *)pasteboardWithQClipboardMode:(QClipboard::Mode)mode; ++ (instancetype)pasteboardWithQClipboardMode:(QClipboard::Mode)mode; @end @implementation UIPasteboard (QUIPasteboard) -+ (UIPasteboard *)pasteboardWithQClipboardMode:(QClipboard::Mode)mode ++ (instancetype)pasteboardWithQClipboardMode:(QClipboard::Mode)mode { NSString *name = (mode == QClipboard::Clipboard) ? UIPasteboardNameGeneral : UIPasteboardNameFind; return [UIPasteboard pasteboardWithName:name create:NO]; @@ -60,17 +60,15 @@ // -------------------------------------------------------------------- @interface QUIClipboard : NSObject -{ -@public +@end + +@implementation QUIClipboard { QIOSClipboard *m_qiosClipboard; NSInteger m_changeCountClipboard; NSInteger m_changeCountFindBuffer; } -@end - -@implementation QUIClipboard -- (id)initWithQIOSClipboard:(QIOSClipboard *)qiosClipboard +- (instancetype)initWithQIOSClipboard:(QIOSClipboard *)qiosClipboard { self = [super init]; if (self) { @@ -149,7 +147,7 @@ QStringList QIOSMimeData::formats() const { QStringList foundMimeTypes; UIPasteboard *pb = [UIPasteboard pasteboardWithQClipboardMode:m_mode]; - NSArray *pasteboardTypes = [pb pasteboardTypes]; + NSArray<NSString *> *pasteboardTypes = [pb pasteboardTypes]; for (NSUInteger i = 0; i < [pasteboardTypes count]; ++i) { QString uti = QString::fromNSString([pasteboardTypes objectAtIndex:i]); @@ -164,7 +162,7 @@ QStringList QIOSMimeData::formats() const QVariant QIOSMimeData::retrieveData(const QString &mimeType, QVariant::Type) const { UIPasteboard *pb = [UIPasteboard pasteboardWithQClipboardMode:m_mode]; - NSArray *pasteboardTypes = [pb pasteboardTypes]; + NSArray<NSString *> *pasteboardTypes = [pb pasteboardTypes]; foreach (QMacInternalPasteboardMime *converter, QMacInternalPasteboardMime::all(QMacInternalPasteboardMime::MIME_ALL)) { @@ -213,12 +211,12 @@ void QIOSClipboard::setMimeData(QMimeData *mimeData, QClipboard::Mode mode) UIPasteboard *pb = [UIPasteboard pasteboardWithQClipboardMode:mode]; if (!mimeData) { - pb.items = [NSArray array]; + pb.items = [NSArray<NSDictionary<NSString *, id> *> array]; return; } mimeData->deleteLater(); - NSMutableDictionary *pbItem = [NSMutableDictionary dictionaryWithCapacity:mimeData->formats().size()]; + NSMutableDictionary<NSString *, id> *pbItem = [NSMutableDictionary<NSString *, id> dictionaryWithCapacity:mimeData->formats().size()]; foreach (const QString &mimeType, mimeData->formats()) { foreach (QMacInternalPasteboardMime *converter, @@ -246,7 +244,7 @@ void QIOSClipboard::setMimeData(QMimeData *mimeData, QClipboard::Mode mode) } } - pb.items = [NSArray arrayWithObject:pbItem]; + pb.items = @[pbItem]; } bool QIOSClipboard::supportsMode(QClipboard::Mode mode) const diff --git a/src/plugins/platforms/ios/qioseventdispatcher.mm b/src/plugins/platforms/ios/qioseventdispatcher.mm index a6f6a7aac9..d5f74881ab 100644 --- a/src/plugins/platforms/ios/qioseventdispatcher.mm +++ b/src/plugins/platforms/ios/qioseventdispatcher.mm @@ -204,6 +204,11 @@ namespace jmp_buf applicationWillTerminateJumpPoint; bool debugStackUsage = false; + + struct { + QAppleLogActivity UIApplicationMain; + QAppleLogActivity applicationDidFinishLaunching; + } logActivity; } extern "C" int qt_main_wrapper(int argc, char *argv[]) @@ -228,6 +233,9 @@ extern "C" int qt_main_wrapper(int argc, char *argv[]) } } + logActivity.UIApplicationMain = QT_APPLE_LOG_ACTIVITY( + lcEventDispatcher().isDebugEnabled(), "UIApplicationMain").enter(); + qCDebug(lcEventDispatcher) << "Running UIApplicationMain"; return UIApplicationMain(argc, argv, nil, NSStringFromClass([QIOSApplicationDelegate class])); } @@ -245,7 +253,7 @@ extern "C" int main(int argc, char *argv[]); static void __attribute__((noinline, noreturn)) user_main_trampoline() { - NSArray *arguments = [[NSProcessInfo processInfo] arguments]; + NSArray<NSString *> *arguments = [[NSProcessInfo processInfo] arguments]; int argc = arguments.count; char **argv = new char*[argc]; @@ -263,11 +271,14 @@ static void __attribute__((noinline, noreturn)) user_main_trampoline() int exitCode = main(argc, argv); delete[] argv; + logActivity.applicationDidFinishLaunching.enter(); qCDebug(lcEventDispatcher) << "Returned from main with exit code " << exitCode; if (Q_UNLIKELY(debugStackUsage)) userMainStack.printUsage(); + logActivity.applicationDidFinishLaunching.leave(); + if (applicationAboutToTerminate) longjmp(applicationWillTerminateJumpPoint, kJumpedFromUserMainTrampoline); @@ -322,6 +333,9 @@ static bool rootLevelRunLoopIntegration() + (void)applicationDidFinishLaunching:(NSNotification *)notification { + logActivity.applicationDidFinishLaunching = QT_APPLE_LOG_ACTIVITY_WITH_PARENT( + lcEventDispatcher().isDebugEnabled(), "applicationDidFinishLaunching", logActivity.UIApplicationMain).enter(); + qCDebug(lcEventDispatcher) << "Application launched with options" << notification.userInfo; if (!isQtApplication()) @@ -339,10 +353,11 @@ static bool rootLevelRunLoopIntegration() return; } - switch (setjmp(processEventEnterJumpPoint)) { - case kJumpPointSetSuccessfully: + case kJumpPointSetSuccessfully: { qCDebug(lcEventDispatcher) << "Running main() on separate stack"; + QT_APPLE_SCOPED_LOG_ACTIVITY(lcEventDispatcher().isDebugEnabled(), "main()"); + // Redirect the stack pointer to the start of the reserved stack. This ensures // that when we longjmp out of the event dispatcher and continue execution, the // 'Qt main' call-stack will not be smashed, as it lives in a part of the stack @@ -357,9 +372,11 @@ static bool rootLevelRunLoopIntegration() Q_UNREACHABLE(); break; + } case kJumpedFromEventDispatcherProcessEvents: // We've returned from the longjmp in the event dispatcher, // and the stack has been restored to its old self. + logActivity.UIApplicationMain.enter(); qCDebug(lcEventDispatcher) << "↳ Jumped from processEvents due to exec"; if (Q_UNLIKELY(debugStackUsage)) @@ -378,6 +395,10 @@ static const char kApplicationWillTerminateExitCode = char(SIGTERM | 0x80); + (void)applicationWillTerminate { + QAppleLogActivity applicationWillTerminateActivity = QT_APPLE_LOG_ACTIVITY_WITH_PARENT( + lcEventDispatcher().isDebugEnabled(), "applicationWillTerminate", logActivity.UIApplicationMain).enter(); + qCDebug(lcEventDispatcher) << "Application about to be terminated by iOS"; + if (!isQtApplication()) return; @@ -403,11 +424,14 @@ static const char kApplicationWillTerminateExitCode = char(SIGTERM | 0x80); // so we'll never see the exit activity and have a chance to return from // QEventLoop::exec(). We initiate the return manually as a workaround. qCDebug(lcEventDispatcher) << "Manually triggering return from event loop exec"; + applicationWillTerminateActivity.leave(); static_cast<QIOSEventDispatcher *>(qApp->eventDispatcher())->interruptEventLoopExec(); break; case kJumpedFromUserMainTrampoline: + applicationWillTerminateActivity.enter(); // The user's main has returned, so we're ready to let iOS terminate the application qCDebug(lcEventDispatcher) << "kJumpedFromUserMainTrampoline, allowing iOS to terminate"; + applicationWillTerminateActivity.leave(); break; default: qFatal("Unexpected jump result in event loop integration"); @@ -441,6 +465,7 @@ bool __attribute__((returns_twice)) QIOSEventDispatcher::processEvents(QEventLoo } if (!m_processEventLevel && (flags & QEventLoop::EventLoopExec)) { + QT_APPLE_SCOPED_LOG_ACTIVITY(lcEventDispatcher().isDebugEnabled(), "processEvents"); qCDebug(lcEventDispatcher) << "Processing events with flags" << flags; ++m_processEventLevel; @@ -487,6 +512,7 @@ bool QIOSEventDispatcher::processPostedEvents() if (!QEventDispatcherCoreFoundation::processPostedEvents()) return false; + QT_APPLE_SCOPED_LOG_ACTIVITY(lcEventDispatcher().isDebugEnabled(), "sendWindowSystemEvents"); qCDebug(lcEventDispatcher) << "Sending window system events for" << m_processEvents.flags; QWindowSystemInterface::sendWindowSystemEvents(m_processEvents.flags); @@ -516,10 +542,12 @@ void QIOSEventDispatcher::interruptEventLoopExec() switch (setjmp(processEventEnterJumpPoint)) { case kJumpPointSetSuccessfully: qCDebug(lcEventDispatcher) << "Jumping into processEvents due to system runloop exit ⇢"; + logActivity.UIApplicationMain.leave(); longjmp(processEventExitJumpPoint, kJumpedFromEventLoopExecInterrupt); break; case kJumpedFromEventDispatcherProcessEvents: // QEventLoop was re-executed + logActivity.UIApplicationMain.enter(); qCDebug(lcEventDispatcher) << "↳ Jumped from processEvents due to re-exec"; break; default: diff --git a/src/plugins/platforms/ios/qiosinputcontext.mm b/src/plugins/platforms/ios/qiosinputcontext.mm index 050c592aca..3e22634071 100644 --- a/src/plugins/platforms/ios/qiosinputcontext.mm +++ b/src/plugins/platforms/ios/qiosinputcontext.mm @@ -67,7 +67,7 @@ static QUIView *focusView() @implementation QIOSLocaleListener -- (id)init +- (instancetype)init { if (self = [super init]) { NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; @@ -95,16 +95,15 @@ static QUIView *focusView() // ------------------------------------------------------------------------- -@interface QIOSKeyboardListener : UIGestureRecognizer <UIGestureRecognizerDelegate> { - @private - QT_PREPEND_NAMESPACE(QIOSInputContext) *m_context; -} +@interface QIOSKeyboardListener : UIGestureRecognizer <UIGestureRecognizerDelegate> @property BOOL hasDeferredScrollToCursor; @end -@implementation QIOSKeyboardListener +@implementation QIOSKeyboardListener { + QT_PREPEND_NAMESPACE(QIOSInputContext) *m_context; +} -- (id)initWithQIOSInputContext:(QT_PREPEND_NAMESPACE(QIOSInputContext) *)context +- (instancetype)initWithQIOSInputContext:(QT_PREPEND_NAMESPACE(QIOSInputContext) *)context { if (self = [super initWithTarget:self action:@selector(gestureStateChanged:)]) { @@ -574,7 +573,7 @@ void QIOSInputContext::scroll(int y) // Raise all known windows to above the status-bar if we're scrolling the screen, // while keeping the relative window level between the windows the same. - NSArray *applicationWindows = [[UIApplication sharedApplication] windows]; + NSArray<UIWindow *> *applicationWindows = [[UIApplication sharedApplication] windows]; static QHash<UIWindow *, UIWindowLevel> originalWindowLevels; for (UIWindow *window in applicationWindows) { if (keyboardScrollIsActive && !originalWindowLevels.contains(window)) diff --git a/src/plugins/platforms/ios/qiosintegration.mm b/src/plugins/platforms/ios/qiosintegration.mm index 92c1e39d72..5f9f7ad96d 100644 --- a/src/plugins/platforms/ios/qiosintegration.mm +++ b/src/plugins/platforms/ios/qiosintegration.mm @@ -97,7 +97,7 @@ QIOSIntegration::QIOSIntegration() QDir::setCurrent(QString::fromUtf8([[[NSBundle mainBundle] bundlePath] UTF8String])); UIScreen *mainScreen = [UIScreen mainScreen]; - NSMutableArray *screens = [[[UIScreen screens] mutableCopy] autorelease]; + NSMutableArray<UIScreen *> *screens = [[[UIScreen screens] mutableCopy] autorelease]; if (![screens containsObject:mainScreen]) { // Fallback for iOS 7.1 (QTBUG-42345) [screens insertObject:mainScreen atIndex:0]; diff --git a/src/plugins/platforms/ios/qiosmenu.mm b/src/plugins/platforms/ios/qiosmenu.mm index 6c70676a31..74a77de757 100644 --- a/src/plugins/platforms/ios/qiosmenu.mm +++ b/src/plugins/platforms/ios/qiosmenu.mm @@ -60,14 +60,14 @@ QIOSMenu *QIOSMenu::m_currentMenu = 0; static NSString *const kSelectorPrefix = @"_qtMenuItem_"; -@interface QUIMenuController : UIResponder { - QIOSMenuItemList m_visibleMenuItems; -} +@interface QUIMenuController : UIResponder @end -@implementation QUIMenuController +@implementation QUIMenuController { + QIOSMenuItemList m_visibleMenuItems; +} -- (id)initWithVisibleMenuItems:(const QIOSMenuItemList &)visibleMenuItems +- (instancetype)initWithVisibleMenuItems:(const QIOSMenuItemList &)visibleMenuItems { if (self = [super init]) { [self setVisibleMenuItems:visibleMenuItems]; @@ -80,7 +80,7 @@ static NSString *const kSelectorPrefix = @"_qtMenuItem_"; return self; } --(void)dealloc +- (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self @@ -91,7 +91,7 @@ static NSString *const kSelectorPrefix = @"_qtMenuItem_"; - (void)setVisibleMenuItems:(const QIOSMenuItemList &)visibleMenuItems { m_visibleMenuItems = visibleMenuItems; - NSMutableArray *menuItemArray = [NSMutableArray arrayWithCapacity:m_visibleMenuItems.size()]; + NSMutableArray<UIMenuItem *> *menuItemArray = [NSMutableArray<UIMenuItem *> arrayWithCapacity:m_visibleMenuItems.size()]; // Create an array of UIMenuItems, one for each visible QIOSMenuItem. Each // UIMenuItem needs a callback assigned, so we assign one of the placeholder methods // added to UIWindow (QIOSMenuActionTargets) below. Each method knows its own index, which @@ -107,7 +107,7 @@ static NSString *const kSelectorPrefix = @"_qtMenuItem_"; [[UIMenuController sharedMenuController] setMenuVisible:YES animated:NO]; } --(void)menuClosed +- (void)menuClosed { QIOSMenu::currentMenu()->dismiss(); } @@ -141,19 +141,19 @@ static NSString *const kSelectorPrefix = @"_qtMenuItem_"; // ------------------------------------------------------------------------- -@interface QUIPickerView : UIPickerView <UIPickerViewDelegate, UIPickerViewDataSource> { - QIOSMenuItemList m_visibleMenuItems; - QPointer<QObject> m_focusObjectWithPickerView; - NSInteger m_selectedRow; -} +@interface QUIPickerView : UIPickerView <UIPickerViewDelegate, UIPickerViewDataSource> @property(retain) UIToolbar *toolbar; @end -@implementation QUIPickerView +@implementation QUIPickerView { + QIOSMenuItemList m_visibleMenuItems; + QPointer<QObject> m_focusObjectWithPickerView; + NSInteger m_selectedRow; +} -- (id)initWithVisibleMenuItems:(const QIOSMenuItemList &)visibleMenuItems selectItem:(const QIOSMenuItem *)selectItem +- (instancetype)initWithVisibleMenuItems:(const QIOSMenuItemList &)visibleMenuItems selectItem:(const QIOSMenuItem *)selectItem { if (self = [super init]) { [self setVisibleMenuItems:visibleMenuItems selectItem:selectItem]; @@ -172,7 +172,7 @@ static NSString *const kSelectorPrefix = @"_qtMenuItem_"; UIBarButtonItem *doneButton = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(closeMenu)] autorelease]; - [self.toolbar setItems:[NSArray arrayWithObjects:cancelButton, spaceButton, doneButton, nil]]; + [self.toolbar setItems:@[cancelButton, spaceButton, doneButton]]; [self setDelegate:self]; [self setDataSource:self]; diff --git a/src/plugins/platforms/ios/qiosoptionalplugininterface.h b/src/plugins/platforms/ios/qiosoptionalplugininterface.h index 660c74e856..bae9e5a0d8 100644 --- a/src/plugins/platforms/ios/qiosoptionalplugininterface.h +++ b/src/plugins/platforms/ios/qiosoptionalplugininterface.h @@ -44,10 +44,10 @@ #include "qiosfiledialog.h" -QT_BEGIN_NAMESPACE - Q_FORWARD_DECLARE_OBJC_CLASS(UIViewController); +QT_BEGIN_NAMESPACE + #define QIosOptionalPluginInterface_iid "org.qt-project.Qt.QPA.ios.optional" class QIosOptionalPluginInterface diff --git a/src/plugins/platforms/ios/qiosscreen.mm b/src/plugins/platforms/ios/qiosscreen.mm index c394592d76..91d26c88c2 100644 --- a/src/plugins/platforms/ios/qiosscreen.mm +++ b/src/plugins/platforms/ios/qiosscreen.mm @@ -130,16 +130,14 @@ static QIOSScreen* qtPlatformScreenFor(UIScreen *uiScreen) // ------------------------------------------------------------------------- -@interface QIOSOrientationListener : NSObject { - @public - QIOSScreen *m_screen; -} -- (id)initWithQIOSScreen:(QIOSScreen *)screen; +@interface QIOSOrientationListener : NSObject @end -@implementation QIOSOrientationListener +@implementation QIOSOrientationListener { + QIOSScreen *m_screen; +} -- (id)initWithQIOSScreen:(QIOSScreen *)screen +- (instancetype)initWithQIOSScreen:(QIOSScreen *)screen { self = [super init]; if (self) { @@ -193,7 +191,7 @@ static QIOSScreen* qtPlatformScreenFor(UIScreen *uiScreen) @implementation QUIWindow -- (id)initWithFrame:(CGRect)frame +- (instancetype)initWithFrame:(CGRect)frame { if ((self = [super initWithFrame:frame])) self->_sendingEvent = NO; diff --git a/src/plugins/platforms/ios/qiostextinputoverlay.mm b/src/plugins/platforms/ios/qiostextinputoverlay.mm index fe3c29d037..ff696f5b7f 100644 --- a/src/plugins/platforms/ios/qiostextinputoverlay.mm +++ b/src/plugins/platforms/ios/qiostextinputoverlay.mm @@ -96,7 +96,7 @@ static void executeBlockWithoutAnimation(Block block) @implementation QIOSEditMenu -- (id)init +- (instancetype)init { if (self = [super init]) { NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; @@ -160,7 +160,13 @@ static void executeBlockWithoutAnimation(Block block) // ------------------------------------------------------------------------- -@interface QIOSLoupeLayer : CALayer { +@interface QIOSLoupeLayer : CALayer +@property (nonatomic, retain) UIView *targetView; +@property (nonatomic, assign) CGPoint focalPoint; +@property (nonatomic, assign) BOOL visible; +@end + +@implementation QIOSLoupeLayer { UIView *_snapshotView; BOOL _pendingSnapshotUpdate; UIView *_loupeImageView; @@ -168,14 +174,8 @@ static void executeBlockWithoutAnimation(Block block) CGFloat _loupeOffset; QTimer _updateTimer; } -@property (nonatomic, retain) UIView *targetView; -@property (nonatomic, assign) CGPoint focalPoint; -@property (nonatomic, assign) BOOL visible; -@end -@implementation QIOSLoupeLayer - -- (id)initWithSize:(CGSize)size cornerRadius:(CGFloat)cornerRadius bottomOffset:(CGFloat)bottomOffset +- (instancetype)initWithSize:(CGSize)size cornerRadius:(CGFloat)cornerRadius bottomOffset:(CGFloat)bottomOffset { if (self = [super init]) { _loupeOffset = bottomOffset + (size.height / 2); @@ -301,26 +301,22 @@ static void executeBlockWithoutAnimation(Block block) // ------------------------------------------------------------------------- -#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_10_0) -@interface QIOSHandleLayer : CALayer <CAAnimationDelegate> { -#else -@interface QIOSHandleLayer : CALayer { -#endif - CALayer *_handleCursorLayer; - CALayer *_handleKnobLayer; - Qt::Edge _selectionEdge; -} +@interface QIOSHandleLayer : CALayer <CAAnimationDelegate> @property (nonatomic, assign) CGRect cursorRectangle; @property (nonatomic, assign) CGFloat handleScale; @property (nonatomic, assign) BOOL visible; @property (nonatomic, copy) Block onAnimationDidStop; @end -@implementation QIOSHandleLayer +@implementation QIOSHandleLayer { + CALayer *_handleCursorLayer; + CALayer *_handleKnobLayer; + Qt::Edge _selectionEdge; +} @dynamic handleScale; -- (id)initWithKnobAtEdge:(Qt::Edge)selectionEdge +- (instancetype)initWithKnobAtEdge:(Qt::Edge)selectionEdge { if (self = [super init]) { CGColorRef bgColor = [UIColor colorWithRed:0.1 green:0.4 blue:0.9 alpha:1].CGColor; @@ -355,16 +351,8 @@ static void executeBlockWithoutAnimation(Block block) // The handle should "bounce" in when becoming visible CAKeyframeAnimation * animation = [CAKeyframeAnimation animationWithKeyPath:key]; [animation setDuration:0.5]; - animation.values = [NSArray arrayWithObjects: - [NSNumber numberWithFloat:0], - [NSNumber numberWithFloat:1.3], - [NSNumber numberWithFloat:1.3], - [NSNumber numberWithFloat:1], nil]; - animation.keyTimes = [NSArray arrayWithObjects: - [NSNumber numberWithFloat:0], - [NSNumber numberWithFloat:0.3], - [NSNumber numberWithFloat:0.9], - [NSNumber numberWithFloat:1], nil]; + animation.values = @[@(0.0f), @(1.3f), @(1.3f), @(1.0f)]; + animation.keyTimes = @[@(0.0f), @(0.3f), @(0.9f), @(1.0f)]; return animation; } else { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:key]; @@ -436,8 +424,13 @@ static void executeBlockWithoutAnimation(Block block) below will inherit. It takes care of creating and showing a magnifier glass depending on the current gesture state. */ -@interface QIOSLoupeRecognizer : UIGestureRecognizer <UIGestureRecognizerDelegate> { -@public +@interface QIOSLoupeRecognizer : UIGestureRecognizer <UIGestureRecognizerDelegate> +@property (nonatomic, assign) QPointF focalPoint; +@property (nonatomic, assign) BOOL dragTriggersGesture; +@property (nonatomic, readonly) UIView *focusView; +@end + +@implementation QIOSLoupeRecognizer { QIOSLoupeLayer *_loupeLayer; UIView *_desktopView; CGPoint _firstTouchPoint; @@ -445,14 +438,8 @@ static void executeBlockWithoutAnimation(Block block) QTimer _triggerStateBeganTimer; int _originalCursorFlashTime; } -@property (nonatomic, assign) QPointF focalPoint; -@property (nonatomic, assign) BOOL dragTriggersGesture; -@property (nonatomic, readonly) UIView *focusView; -@end -@implementation QIOSLoupeRecognizer - -- (id)init +- (instancetype)init { if (self = [super initWithTarget:self action:@selector(gestureStateChanged)]) { self.enabled = NO; @@ -657,7 +644,10 @@ static void executeBlockWithoutAnimation(Block block) on the sides. If the user starts dragging on a handle (or do a press and hold), it will show a magnifier glass that follows the handle as it moves. */ -@interface QIOSSelectionRecognizer : QIOSLoupeRecognizer { +@interface QIOSSelectionRecognizer : QIOSLoupeRecognizer +@end + +@implementation QIOSSelectionRecognizer { CALayer *_clipRectLayer; QIOSHandleLayer *_cursorLayer; QIOSHandleLayer *_anchorLayer; @@ -669,11 +659,8 @@ static void executeBlockWithoutAnimation(Block block) QMetaObject::Connection _anchorConnection; QMetaObject::Connection _clipRectConnection; } -@end - -@implementation QIOSSelectionRecognizer -- (id)init +- (instancetype)init { if (self = [super init]) { self.delaysTouchesBegan = YES; @@ -889,15 +876,15 @@ static void executeBlockWithoutAnimation(Block block) visibility of the edit menu will be toggled. Otherwise, if there's a selection, a first tap will close the edit menu (if any), and a second tap will remove the selection. */ -@interface QIOSTapRecognizer : UITapGestureRecognizer { +@interface QIOSTapRecognizer : UITapGestureRecognizer +@end + +@implementation QIOSTapRecognizer { int _cursorPosOnPress; UIView *_focusView; } -@end - -@implementation QIOSTapRecognizer -- (id)init +- (instancetype)init { if (self = [super initWithTarget:self action:@selector(gestureStateChanged)]) { self.enabled = NO; diff --git a/src/plugins/platforms/ios/qiostextresponder.h b/src/plugins/platforms/ios/qiostextresponder.h index 77be2cf2fe..074598c1c3 100644 --- a/src/plugins/platforms/ios/qiostextresponder.h +++ b/src/plugins/platforms/ios/qiostextresponder.h @@ -49,16 +49,8 @@ class QIOSInputContext; QT_END_NAMESPACE @interface QIOSTextInputResponder : UIResponder <UITextInputTraits, UIKeyInput, UITextInput> -{ - @private - QT_PREPEND_NAMESPACE(QIOSInputContext) *m_inputContext; - QT_PREPEND_NAMESPACE(QInputMethodQueryEvent) *m_configuredImeState; - QString m_markedText; - BOOL m_inSendEventToFocusObject; - BOOL m_inSelectionChange; -} -- (id)initWithInputContext:(QT_PREPEND_NAMESPACE(QIOSInputContext) *)context; +- (instancetype)initWithInputContext:(QT_PREPEND_NAMESPACE(QIOSInputContext) *)context; - (BOOL)needsKeyboardReconfigure:(Qt::InputMethodQueries)updatedProperties; - (void)notifyInputDelegate:(Qt::InputMethodQueries)updatedProperties; diff --git a/src/plugins/platforms/ios/qiostextresponder.mm b/src/plugins/platforms/ios/qiostextresponder.mm index b029c49a67..91a088ede1 100644 --- a/src/plugins/platforms/ios/qiostextresponder.mm +++ b/src/plugins/platforms/ios/qiostextresponder.mm @@ -55,13 +55,13 @@ @interface QUITextPosition : UITextPosition @property (nonatomic) NSUInteger index; -+ (QUITextPosition *)positionWithIndex:(NSUInteger)index; ++ (instancetype)positionWithIndex:(NSUInteger)index; @end @implementation QUITextPosition -+ (QUITextPosition *)positionWithIndex:(NSUInteger)index ++ (instancetype)positionWithIndex:(NSUInteger)index { QUITextPosition *pos = [[QUITextPosition alloc] init]; pos.index = index; @@ -75,15 +75,15 @@ @interface QUITextRange : UITextRange @property (nonatomic) NSRange range; -+ (QUITextRange *)rangeWithNSRange:(NSRange)range; ++ (instancetype)rangeWithNSRange:(NSRange)range; @end @implementation QUITextRange -+ (QUITextRange *)rangeWithNSRange:(NSRange)nsrange ++ (instancetype)rangeWithNSRange:(NSRange)nsrange { - QUITextRange *range = [[QUITextRange alloc] init]; + QUITextRange *range = [[self alloc] init]; range.range = nsrange; return [range autorelease]; } @@ -117,7 +117,7 @@ @implementation WrapperView -- (id)initWithView:(UIView *)view +- (instancetype)initWithView:(UIView *)view { if (self = [self init]) { [self addSubview:view]; @@ -132,7 +132,7 @@ - (void)layoutSubviews { - UIView* view = [self.subviews firstObject]; + UIView *view = [self.subviews firstObject]; view.frame = self.bounds; // FIXME: During orientation changes the size and position @@ -161,9 +161,15 @@ // ------------------------------------------------------------------------- -@implementation QIOSTextInputResponder +@implementation QIOSTextInputResponder { + QT_PREPEND_NAMESPACE(QIOSInputContext) *m_inputContext; + QT_PREPEND_NAMESPACE(QInputMethodQueryEvent) *m_configuredImeState; + QString m_markedText; + BOOL m_inSendEventToFocusObject; + BOOL m_inSelectionChange; +} -- (id)initWithInputContext:(QT_PREPEND_NAMESPACE(QIOSInputContext) *)inputContext +- (instancetype)initWithInputContext:(QT_PREPEND_NAMESPACE(QIOSInputContext) *)inputContext { if (!(self = [self init])) return self; @@ -548,7 +554,7 @@ [self sendKeyPressRelease:key modifiers:modifiers]; } -- (void)addKeyCommandsToArray:(NSMutableArray *)array key:(NSString *)key +- (void)addKeyCommandsToArray:(NSMutableArray<UIKeyCommand *> *)array key:(NSString *)key { SEL s = @selector(keyCommandTriggered:); [array addObject:[UIKeyCommand keyCommandWithInput:key modifierFlags:0 action:s]]; @@ -559,19 +565,19 @@ [array addObject:[UIKeyCommand keyCommandWithInput:key modifierFlags:UIKeyModifierCommand|UIKeyModifierShift action:s]]; } -- (NSArray *)keyCommands +- (NSArray<UIKeyCommand *> *)keyCommands { // Since keyCommands is called for every key // press/release, we cache the result static dispatch_once_t once; - static NSMutableArray *array; + static NSMutableArray<UIKeyCommand *> *array; dispatch_once(&once, ^{ // We let Qt move the cursor around when the arrow keys are being used. This // is normally implemented through UITextInput, but since IM in Qt have poor // support for moving the cursor vertically, and even less support for selecting // text across multiple paragraphs, we do this through key events. - array = [NSMutableArray new]; + array = [NSMutableArray<UIKeyCommand *> new]; [self addKeyCommandsToArray:array key:UIKeyInputUpArrow]; [self addKeyCommandsToArray:array key:UIKeyInputDownArrow]; [self addKeyCommandsToArray:array key:UIKeyInputLeftArrow]; @@ -825,13 +831,13 @@ return startRect.united(endRect).toCGRect(); } -- (NSArray *)selectionRectsForRange:(UITextRange *)range +- (NSArray<UITextSelectionRect *> *)selectionRectsForRange:(UITextRange *)range { Q_UNUSED(range); // This method is supposed to return a rectangle for each line with selection. Since we don't // expose an API in Qt/IM for getting this information, and since we never seems to be getting // a call from UIKit for this, we return an empty array until a need arise. - return [[NSArray new] autorelease]; + return [[NSArray<UITextSelectionRect *> new] autorelease]; } - (CGRect)caretRectForPosition:(UITextPosition *)position @@ -916,7 +922,7 @@ QObject *focusObject = QGuiApplication::focusObject(); if (!focusObject) - return [NSDictionary dictionary]; + return @{}; // Assume position is the same as the cursor for now. QInputMethodQueryEvent with Qt::ImFont // needs to be extended to take an extra position argument before this can be fully correct. @@ -925,8 +931,8 @@ QFont qfont = qvariant_cast<QFont>(e.value(Qt::ImFont)); UIFont *uifont = [UIFont fontWithName:qfont.family().toNSString() size:qfont.pointSize()]; if (!uifont) - return [NSDictionary dictionary]; - return [NSDictionary dictionaryWithObject:uifont forKey:NSFontAttributeName]; + return @{}; + return @{NSFontAttributeName: uifont}; } #endif diff --git a/src/plugins/platforms/ios/qiosviewcontroller.h b/src/plugins/platforms/ios/qiosviewcontroller.h index 07d5535e1a..7af4c83b48 100644 --- a/src/plugins/platforms/ios/qiosviewcontroller.h +++ b/src/plugins/platforms/ios/qiosviewcontroller.h @@ -48,7 +48,7 @@ QT_END_NAMESPACE @interface QIOSViewController : UIViewController -- (id)initWithQIOSScreen:(QT_PREPEND_NAMESPACE(QIOSScreen) *)screen; +- (instancetype)initWithQIOSScreen:(QT_PREPEND_NAMESPACE(QIOSScreen) *)screen; - (void)updateProperties; #ifndef Q_OS_TVOS diff --git a/src/plugins/platforms/ios/qiosviewcontroller.mm b/src/plugins/platforms/ios/qiosviewcontroller.mm index a7663b9e94..787c6b8409 100644 --- a/src/plugins/platforms/ios/qiosviewcontroller.mm +++ b/src/plugins/platforms/ios/qiosviewcontroller.mm @@ -56,12 +56,8 @@ // ------------------------------------------------------------------------- -@interface QIOSViewController () { - @public - QPointer<QT_PREPEND_NAMESPACE(QIOSScreen)> m_screen; - BOOL m_updatingProperties; - QMetaObject::Connection m_focusWindowChangeConnection; -} +@interface QIOSViewController () +@property (nonatomic, assign) QPointer<QT_PREPEND_NAMESPACE(QIOSScreen)> platformScreen; @property (nonatomic, assign) BOOL changingOrientation; @end @@ -72,7 +68,7 @@ @implementation QIOSDesktopManagerView -- (id)init +- (instancetype)init { if (!(self = [super init])) return nil; @@ -125,7 +121,7 @@ { Q_UNUSED(subview); - QT_PREPEND_NAMESPACE(QIOSScreen) *screen = self.qtViewController->m_screen; + QT_PREPEND_NAMESPACE(QIOSScreen) *screen = self.qtViewController.platformScreen; // The 'window' property of our view is not valid until the window // has been shown, so we have to access it through the QIOSScreen. @@ -170,7 +166,7 @@ // here. iOS will still use the latest rendered frame to create the // application switcher thumbnail, but it will be based on the last // active orientation of the application. - QIOSScreen *screen = self.qtViewController->m_screen; + QIOSScreen *screen = self.qtViewController.platformScreen; qCDebug(lcQpaWindow) << "ignoring layout of subviews while suspended," << "likely system snapshot of" << screen->screen()->primaryOrientation(); return; @@ -246,7 +242,10 @@ // ------------------------------------------------------------------------- -@implementation QIOSViewController +@implementation QIOSViewController { + BOOL m_updatingProperties; + QMetaObject::Connection m_focusWindowChangeConnection; +} #ifndef Q_OS_TVOS @synthesize prefersStatusBarHidden; @@ -254,11 +253,10 @@ @synthesize preferredStatusBarStyle; #endif -- (id)initWithQIOSScreen:(QT_PREPEND_NAMESPACE(QIOSScreen) *)screen +- (instancetype)initWithQIOSScreen:(QT_PREPEND_NAMESPACE(QIOSScreen) *)screen { if (self = [self init]) { - m_screen = screen; - + self.platformScreen = screen; self.changingOrientation = NO; #ifndef Q_OS_TVOS @@ -316,7 +314,7 @@ - (BOOL)shouldAutorotate { #ifndef Q_OS_TVOS - return m_screen && m_screen->uiScreen() == [UIScreen mainScreen] && !self.lockedOrientation; + return self.platformScreen && self.platformScreen->uiScreen() == [UIScreen mainScreen] && !self.lockedOrientation; #else return NO; #endif @@ -396,8 +394,8 @@ if (!QCoreApplication::instance()) return; - if (m_screen) - m_screen->updateProperties(); + if (self.platformScreen) + self.platformScreen->updateProperties(); } // ------------------------------------------------------------------------- @@ -407,12 +405,12 @@ if (!isQtApplication()) return; - if (!m_screen || !m_screen->screen()) + if (!self.platformScreen || !self.platformScreen->screen()) return; // For now we only care about the main screen, as both the statusbar // visibility and orientation is only appropriate for the main screen. - if (m_screen->uiScreen() != [UIScreen mainScreen]) + if (self.platformScreen->uiScreen() != [UIScreen mainScreen]) return; // Prevent recursion caused by updating the status bar appearance (position @@ -434,7 +432,7 @@ return; // We only care about changes to focusWindow that involves our screen - if (!focusWindow->screen() || focusWindow->screen()->handle() != m_screen) + if (!focusWindow->screen() || focusWindow->screen()->handle() != self.platformScreen) return; // All decisions are based on the the top level window diff --git a/src/plugins/platforms/ios/qioswindow.mm b/src/plugins/platforms/ios/qioswindow.mm index 6ee258e363..cdec57de71 100644 --- a/src/plugins/platforms/ios/qioswindow.mm +++ b/src/plugins/platforms/ios/qioswindow.mm @@ -96,7 +96,7 @@ QIOSWindow::~QIOSWindow() [m_view touchesCancelled:[NSSet set] withEvent:0]; clearAccessibleCache(); - m_view->m_qioswindow = 0; + m_view.platformWindow = 0; [m_view removeFromSuperview]; [m_view release]; } @@ -139,7 +139,7 @@ void QIOSWindow::setVisible(bool visible) } else if (!visible && [m_view isActiveWindow]) { // Our window was active/focus window but now hidden, so relinquish // focus to the next possible window in the stack. - NSArray *subviews = m_view.viewController.view.subviews; + NSArray<UIView *> *subviews = m_view.viewController.view.subviews; for (int i = int(subviews.count) - 1; i >= 0; --i) { UIView *view = [subviews objectAtIndex:i]; if (view.hidden) @@ -301,7 +301,7 @@ void QIOSWindow::raiseOrLower(bool raise) if (!isQtApplication()) return; - NSArray *subviews = m_view.superview.subviews; + NSArray<UIView *> *subviews = m_view.superview.subviews; if (subviews.count == 1) return; diff --git a/src/plugins/platforms/ios/quiaccessibilityelement.h b/src/plugins/platforms/ios/quiaccessibilityelement.h index 03abf5407e..6b8efdcede 100644 --- a/src/plugins/platforms/ios/quiaccessibilityelement.h +++ b/src/plugins/platforms/ios/quiaccessibilityelement.h @@ -45,13 +45,12 @@ #ifndef QT_NO_ACCESSIBILITY -@interface QMacAccessibilityElement : UIAccessibilityElement -{} +@interface QT_MANGLE_NAMESPACE(QMacAccessibilityElement) : UIAccessibilityElement @property (readonly) QAccessible::Id axid; -- (id)initWithId:(QAccessible::Id)anId withAccessibilityContainer:(id)view; -+ (QMacAccessibilityElement *)elementWithId:(QAccessible::Id)anId withAccessibilityContainer:(id)view; +- (instancetype)initWithId:(QAccessible::Id)anId withAccessibilityContainer:(id)view; ++ (instancetype)elementWithId:(QAccessible::Id)anId withAccessibilityContainer:(id)view; @end diff --git a/src/plugins/platforms/ios/quiaccessibilityelement.mm b/src/plugins/platforms/ios/quiaccessibilityelement.mm index a26ba61b3c..3154536aad 100644 --- a/src/plugins/platforms/ios/quiaccessibilityelement.mm +++ b/src/plugins/platforms/ios/quiaccessibilityelement.mm @@ -42,20 +42,23 @@ #ifndef QT_NO_ACCESSIBILITY #include "private/qaccessiblecache_p.h" +#include "private/qcore_mac_p.h" + +QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); @implementation QMacAccessibilityElement -- (id)initWithId:(QAccessible::Id)anId withAccessibilityContainer:(id)view +- (instancetype)initWithId:(QAccessible::Id)anId withAccessibilityContainer:(id)view { Q_ASSERT((int)anId < 0); - self = [super initWithAccessibilityContainer: view]; + self = [super initWithAccessibilityContainer:view]; if (self) _axid = anId; return self; } -+ (id)elementWithId:(QAccessible::Id)anId withAccessibilityContainer:(id)view ++ (instancetype)elementWithId:(QAccessible::Id)anId withAccessibilityContainer:(id)view { Q_ASSERT(anId); if (!anId) @@ -63,10 +66,10 @@ QAccessibleCache *cache = QAccessibleCache::instance(); - QT_MANGLE_NAMESPACE(QMacAccessibilityElement) *element = cache->elementForId(anId); + QMacAccessibilityElement *element = cache->elementForId(anId); if (!element) { Q_ASSERT(QAccessible::accessibleInterface(anId)); - element = [[self alloc] initWithId:anId withAccessibilityContainer: view]; + element = [[self alloc] initWithId:anId withAccessibilityContainer:view]; cache->insertElement(anId, element); } return element; diff --git a/src/plugins/platforms/ios/quiview.h b/src/plugins/platforms/ios/quiview.h index 3e3c579075..e1d5d5af0c 100644 --- a/src/plugins/platforms/ios/quiview.h +++ b/src/plugins/platforms/ios/quiview.h @@ -53,21 +53,10 @@ QT_END_NAMESPACE @class QIOSViewController; @interface QUIView : UIView -{ - @public - QT_PREPEND_NAMESPACE(QIOSWindow) *m_qioswindow; - @private - QHash<UITouch *, QWindowSystemInterface::TouchPoint> m_activeTouches; - UITouch *m_activePencilTouch; - int m_nextTouchId; - - @private - NSMutableArray *m_accessibleElements; -}; - -- (id)initWithQIOSWindow:(QT_PREPEND_NAMESPACE(QIOSWindow) *)window; +- (instancetype)initWithQIOSWindow:(QT_PREPEND_NAMESPACE(QIOSWindow) *)window; - (void)sendUpdatedExposeEvent; - (BOOL)isActiveWindow; +@property (nonatomic, assign) QT_PREPEND_NAMESPACE(QIOSWindow) *platformWindow; @end @interface QUIView (Accessibility) diff --git a/src/plugins/platforms/ios/quiview.mm b/src/plugins/platforms/ios/quiview.mm index 584dfe9b41..53a4485609 100644 --- a/src/plugins/platforms/ios/quiview.mm +++ b/src/plugins/platforms/ios/quiview.mm @@ -55,7 +55,12 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") -@implementation QUIView +@implementation QUIView { + QHash<UITouch *, QWindowSystemInterface::TouchPoint> m_activeTouches; + UITouch *m_activePencilTouch; + int m_nextTouchId; + NSMutableArray<UIAccessibilityElement *> *m_accessibleElements; +} + (void)load { @@ -82,25 +87,26 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") return [CAEAGLLayer class]; } -- (id)initWithQIOSWindow:(QT_PREPEND_NAMESPACE(QIOSWindow) *)window +- (instancetype)initWithQIOSWindow:(QT_PREPEND_NAMESPACE(QIOSWindow) *)window { if (self = [self initWithFrame:window->geometry().toCGRect()]) { - m_qioswindow = window; - m_accessibleElements = [[NSMutableArray alloc] init]; + self.platformWindow = window; + m_accessibleElements = [[NSMutableArray<UIAccessibilityElement *> alloc] init]; } return self; } -- (id)initWithFrame:(CGRect)frame +- (instancetype)initWithFrame:(CGRect)frame { if ((self = [super initWithFrame:frame])) { // Set up EAGL layer CAEAGLLayer *eaglLayer = static_cast<CAEAGLLayer *>(self.layer); eaglLayer.opaque = TRUE; - eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithBool:YES], kEAGLDrawablePropertyRetainedBacking, - kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil]; + eaglLayer.drawableProperties = @{ + kEAGLDrawablePropertyRetainedBacking: @(YES), + kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8 + }; if (isQtApplication()) self.hidden = YES; @@ -156,7 +162,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") #ifndef QT_NO_DEBUG_STREAM QString platformWindowDescription; QDebug debug(&platformWindowDescription); - debug.nospace() << "; " << m_qioswindow << ">"; + debug.nospace() << "; " << self.platformWindow << ">"; NSRange lastCharacter = [description rangeOfComposedCharacterSequenceAtIndex:description.length - 1]; [description replaceCharactersInRange:lastCharacter withString:platformWindowDescription.toNSString()]; #endif @@ -210,10 +216,10 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") if (!CGAffineTransformIsIdentity(self.transform)) qWarning() << self << "has a transform set. This is not supported."; - QWindow *window = m_qioswindow->window(); + QWindow *window = self.platformWindow->window(); QRect lastReportedGeometry = qt_window_private(window)->geometry; QRect currentGeometry = QRectF::fromCGRect(self.frame).toRect(); - qCDebug(lcQpaWindow) << m_qioswindow << "new geometry is" << currentGeometry; + qCDebug(lcQpaWindow) << self.platformWindow << "new geometry is" << currentGeometry; QWindowSystemInterface::handleGeometryChange(window, currentGeometry); if (currentGeometry.size() != lastReportedGeometry.size()) { @@ -237,29 +243,29 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") { QRegion region; - if (m_qioswindow->isExposed()) { + if (self.platformWindow->isExposed()) { QSize bounds = QRectF::fromCGRect(self.layer.bounds).toRect().size(); - Q_ASSERT(m_qioswindow->geometry().size() == bounds); - Q_ASSERT(self.hidden == !m_qioswindow->window()->isVisible()); + Q_ASSERT(self.platformWindow->geometry().size() == bounds); + Q_ASSERT(self.hidden == !self.platformWindow->window()->isVisible()); region = QRect(QPoint(), bounds); } - qCDebug(lcQpaWindow) << m_qioswindow << region << "isExposed" << m_qioswindow->isExposed(); - QWindowSystemInterface::handleExposeEvent(m_qioswindow->window(), region); + qCDebug(lcQpaWindow) << self.platformWindow << region << "isExposed" << self.platformWindow->isExposed(); + QWindowSystemInterface::handleExposeEvent(self.platformWindow->window(), region); } - (void)safeAreaInsetsDidChange { - QWindowSystemInterface::handleSafeAreaMarginsChanged(m_qioswindow->window()); + QWindowSystemInterface::handleSafeAreaMarginsChanged(self.platformWindow->window()); } // ------------------------------------------------------------------------- - (BOOL)canBecomeFirstResponder { - return !(m_qioswindow->window()->flags() & Qt::WindowDoesNotAcceptFocus); + return !(self.platformWindow->window()->flags() & Qt::WindowDoesNotAcceptFocus); } - (BOOL)becomeFirstResponder @@ -280,10 +286,10 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") qImDebug() << self << "became first responder"; } - if (qGuiApp->focusWindow() != m_qioswindow->window()) - QWindowSystemInterface::handleWindowActivated(m_qioswindow->window()); + if (qGuiApp->focusWindow() != self.platformWindow->window()) + QWindowSystemInterface::handleWindowActivated(self.platformWindow->window()); else - qImDebug() << m_qioswindow->window() << "already active, not sending window activation"; + qImDebug() << self.platformWindow->window() << "already active, not sending window activation"; return YES; } @@ -361,7 +367,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") -(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { - if (m_qioswindow->window()->flags() & Qt::WindowTransparentForInput) + if (self.platformWindow->window()->flags() & Qt::WindowTransparentForInput) return NO; return [super pointInside:point withEvent:event]; } @@ -378,7 +384,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") for (UITouch *cTouch in cTouches) { QPointF localViewPosition = QPointF::fromCGPoint([cTouch preciseLocationInView:self]); QPoint localViewPositionI = localViewPosition.toPoint(); - QPointF globalScreenPosition = m_qioswindow->mapToGlobal(localViewPositionI) + + QPointF globalScreenPosition = self.platformWindow->mapToGlobal(localViewPositionI) + (localViewPosition - localViewPositionI); qreal pressure = cTouch.force / cTouch.maximumPossibleForce; // azimuth unit vector: +x to the right, +y going downwards @@ -391,7 +397,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") qCDebug(lcQpaTablet) << i << ":" << timeStamp << localViewPosition << pressure << state << "azimuth" << azimuth.dx << azimuth.dy << "angle" << azimuthAngle << "altitude" << cTouch.altitudeAngle << "xTilt" << qBound(-60.0, altitudeAngle * azimuth.dx, 60.0) << "yTilt" << qBound(-60.0, altitudeAngle * azimuth.dy, 60.0); - QWindowSystemInterface::handleTabletEvent(m_qioswindow->window(), timeStamp, localViewPosition, globalScreenPosition, + QWindowSystemInterface::handleTabletEvent(self.platformWindow->window(), timeStamp, localViewPosition, globalScreenPosition, // device, pointerType, buttons QTabletEvent::RotationStylus, QTabletEvent::Pen, state == Qt::TouchPointReleased ? Qt::NoButton : Qt::LeftButton, // pressure, xTilt, yTilt @@ -415,12 +421,12 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") // just map from the local view position to global coordinates. // tvOS: all touches start at the center of the screen and move from there. QPoint localViewPosition = QPointF::fromCGPoint([uiTouch locationInView:self]).toPoint(); - QPoint globalScreenPosition = m_qioswindow->mapToGlobal(localViewPosition); + QPoint globalScreenPosition = self.platformWindow->mapToGlobal(localViewPosition); touchPoint.area = QRectF(globalScreenPosition, QSize(0, 0)); // FIXME: Do we really need to support QTouchDevice::NormalizedPosition? - QSize screenSize = m_qioswindow->screen()->geometry().size(); + QSize screenSize = self.platformWindow->screen()->geometry().size(); touchPoint.normalPosition = QPointF(globalScreenPosition.x() / screenSize.width(), globalScreenPosition.y() / screenSize.height()); @@ -439,7 +445,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") } if (m_activeTouches.isEmpty()) return; - QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::SynchronousDelivery>(m_qioswindow->window(), timeStamp, iosIntegration->touchDevice(), m_activeTouches.values()); + QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::SynchronousDelivery>(self.platformWindow->window(), timeStamp, iosIntegration->touchDevice(), m_activeTouches.values()); if (!static_cast<QUIWindow *>(self.window).sendingEvent) { // The event is likely delivered as part of delayed touch delivery, via // _UIGestureEnvironmentSortAndSendDelayedTouches, due to one of the two @@ -450,10 +456,10 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") // alert dialog, will fail to recognize. To be on the safe side, we deliver // the event asynchronously. QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::AsynchronousDelivery>( - m_qioswindow->window(), timeStamp, iosIntegration->touchDevice(), m_activeTouches.values()); + self.platformWindow->window(), timeStamp, iosIntegration->touchDevice(), m_activeTouches.values()); } else { QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::SynchronousDelivery>( - m_qioswindow->window(), timeStamp, iosIntegration->touchDevice(), m_activeTouches.values()); + self.platformWindow->window(), timeStamp, iosIntegration->touchDevice(), m_activeTouches.values()); } } @@ -481,8 +487,8 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") #endif } - if (m_qioswindow->shouldAutoActivateWindow() && m_activeTouches.size() == 1) { - QPlatformWindow *topLevel = m_qioswindow; + if (self.platformWindow->shouldAutoActivateWindow() && m_activeTouches.size() == 1) { + QPlatformWindow *topLevel = self.platformWindow; while (QPlatformWindow *p = topLevel->parent()) topLevel = p; if (topLevel->window() != QGuiApplication::focusWindow()) @@ -552,7 +558,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") NSTimeInterval timestamp = event ? event.timestamp : [[NSProcessInfo processInfo] systemUptime]; QIOSIntegration *iosIntegration = static_cast<QIOSIntegration *>(QGuiApplicationPrivate::platformIntegration()); - QWindowSystemInterface::handleTouchCancelEvent(m_qioswindow->window(), ulong(timestamp * 1000), iosIntegration->touchDevice()); + QWindowSystemInterface::handleTouchCancelEvent(self.platformWindow->window(), ulong(timestamp * 1000), iosIntegration->touchDevice()); } - (int)mapPressTypeToKey:(UIPress*)press @@ -580,7 +586,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") int key = [self mapPressTypeToKey:press]; if (key == Qt::Key_unknown) continue; - if (QWindowSystemInterface::handleKeyEvent(m_qioswindow->window(), type, key, Qt::NoModifier)) + if (QWindowSystemInterface::handleKeyEvent(self.platformWindow->window(), type, key, Qt::NoModifier)) handled = true; } @@ -634,7 +640,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") - (QWindow *)qwindow { if ([self isKindOfClass:[QUIView class]]) { - if (QT_PREPEND_NAMESPACE(QIOSWindow) *w = static_cast<QUIView *>(self)->m_qioswindow) + if (QT_PREPEND_NAMESPACE(QIOSWindow) *w = static_cast<QUIView *>(self).platformWindow) return w->window(); } return nil; diff --git a/src/plugins/platforms/ios/quiview_accessibility.mm b/src/plugins/platforms/ios/quiview_accessibility.mm index 69a4d375bd..a3f4156a59 100644 --- a/src/plugins/platforms/ios/quiview_accessibility.mm +++ b/src/plugins/platforms/ios/quiview_accessibility.mm @@ -49,8 +49,9 @@ if (!iface || iface->state().invisible || (iface->text(QAccessible::Name).isEmpty() && iface->text(QAccessible::Value).isEmpty() && iface->text(QAccessible::Description).isEmpty())) return; QAccessible::Id accessibleId = QAccessible::uniqueId(iface); - UIAccessibilityElement *elem = [[QMacAccessibilityElement alloc] initWithId: accessibleId withAccessibilityContainer: self]; - [m_accessibleElements addObject:[elem autorelease]]; + UIAccessibilityElement *elem = [[QT_MANGLE_NAMESPACE(QMacAccessibilityElement) alloc] initWithId:accessibleId withAccessibilityContainer:self]; + [m_accessibleElements addObject:elem]; + [elem release]; } - (void)createAccessibleContainer:(QAccessibleInterface *)iface @@ -73,7 +74,7 @@ if ([m_accessibleElements count]) return; - QWindow *win = m_qioswindow->window(); + QWindow *win = self.platformWindow->window(); QAccessibleInterface *iface = win->accessibleRoot(); if (iface) [self createAccessibleContainer: iface]; diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.cpp b/src/plugins/platforms/windows/qwindowsmousehandler.cpp index 814291c54a..0dd1f40b71 100644 --- a/src/plugins/platforms/windows/qwindowsmousehandler.cpp +++ b/src/plugins/platforms/windows/qwindowsmousehandler.cpp @@ -178,6 +178,85 @@ Qt::MouseButtons QWindowsMouseHandler::queryMouseButtons() return result; } +static QPoint lastMouseMovePos; + +namespace { +struct MouseEvent { + QEvent::Type type; + Qt::MouseButton button; +}; + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug d, const MouseEvent &e) +{ + QDebugStateSaver saver(d); + d.nospace(); + d << "MouseEvent(" << e.type << ", " << e.button << ')'; + return d; +} +#endif // QT_NO_DEBUG_STREAM +} // namespace + +static inline Qt::MouseButton extraButton(WPARAM wParam) // for WM_XBUTTON... +{ + return GET_XBUTTON_WPARAM(wParam) == XBUTTON1 ? Qt::BackButton : Qt::ForwardButton; +} + +static inline MouseEvent eventFromMsg(const MSG &msg) +{ + switch (msg.message) { + case WM_MOUSEMOVE: + return {QEvent::MouseMove, Qt::NoButton}; + case WM_LBUTTONDOWN: + return {QEvent::MouseButtonPress, Qt::LeftButton}; + case WM_LBUTTONUP: + return {QEvent::MouseButtonRelease, Qt::LeftButton}; + case WM_LBUTTONDBLCLK: + return {QEvent::MouseButtonDblClick, Qt::LeftButton}; + case WM_MBUTTONDOWN: + return {QEvent::MouseButtonPress, Qt::MidButton}; + case WM_MBUTTONUP: + return {QEvent::MouseButtonRelease, Qt::MidButton}; + case WM_MBUTTONDBLCLK: + return {QEvent::MouseButtonDblClick, Qt::MidButton}; + case WM_RBUTTONDOWN: + return {QEvent::MouseButtonPress, Qt::RightButton}; + case WM_RBUTTONUP: + return {QEvent::MouseButtonRelease, Qt::RightButton}; + case WM_RBUTTONDBLCLK: + return {QEvent::MouseButtonDblClick, Qt::RightButton}; + case WM_XBUTTONDOWN: + return {QEvent::MouseButtonPress, extraButton(msg.wParam)}; + case WM_XBUTTONUP: + return {QEvent::MouseButtonRelease, extraButton(msg.wParam)}; + case WM_XBUTTONDBLCLK: + return {QEvent::MouseButtonDblClick, extraButton(msg.wParam)}; + case WM_NCMOUSEMOVE: + return {QEvent::NonClientAreaMouseMove, Qt::NoButton}; + case WM_NCLBUTTONDOWN: + return {QEvent::NonClientAreaMouseButtonPress, Qt::LeftButton}; + case WM_NCLBUTTONUP: + return {QEvent::NonClientAreaMouseButtonRelease, Qt::LeftButton}; + case WM_NCLBUTTONDBLCLK: + return {QEvent::NonClientAreaMouseButtonDblClick, Qt::LeftButton}; + case WM_NCMBUTTONDOWN: + return {QEvent::NonClientAreaMouseButtonPress, Qt::MidButton}; + case WM_NCMBUTTONUP: + return {QEvent::NonClientAreaMouseButtonRelease, Qt::MidButton}; + case WM_NCMBUTTONDBLCLK: + return {QEvent::NonClientAreaMouseButtonDblClick, Qt::MidButton}; + case WM_NCRBUTTONDOWN: + return {QEvent::NonClientAreaMouseButtonPress, Qt::RightButton}; + case WM_NCRBUTTONUP: + return {QEvent::NonClientAreaMouseButtonRelease, Qt::RightButton}; + case WM_NCRBUTTONDBLCLK: + return {QEvent::NonClientAreaMouseButtonDblClick, Qt::RightButton}; + default: // WM_MOUSELEAVE + break; + } + return {QEvent::None, Qt::NoButton}; +} + bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, LRESULT *result) @@ -192,8 +271,33 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, if (et == QtWindows::MouseWheelEvent) return translateMouseWheelEvent(window, hwnd, msg, result); + const QPoint winEventPosition(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam)); + QPoint clientPosition; + QPoint globalPosition; + if (et & QtWindows::NonClientEventFlag) { + globalPosition = winEventPosition; + clientPosition = QWindowsGeometryHint::mapFromGlobal(hwnd, globalPosition); + } else { + clientPosition = winEventPosition; + globalPosition = QWindowsGeometryHint::mapToGlobal(hwnd, winEventPosition); + } + + // Windows sends a mouse move with no buttons pressed to signal "Enter" + // when a window is shown over the cursor. Discard the event and only use + // it for generating QEvent::Enter to be consistent with other platforms - + // X11 and macOS. + bool discardEvent = false; + if (msg.message == WM_MOUSEMOVE) { + const bool samePosition = globalPosition == lastMouseMovePos; + lastMouseMovePos = globalPosition; + if (msg.wParam == 0 && (m_windowUnderMouse.isNull() || samePosition)) + discardEvent = true; + } + Qt::MouseEventSource source = Qt::MouseEventNotSynthesized; + const MouseEvent mouseEvent = eventFromMsg(msg); + // Check for events synthesized from touch. Lower byte is touch index, 0 means pen. static const bool passSynthesizedMouseEvents = !(QWindowsIntegration::instance()->options() & QWindowsIntegration::DontPassOsMouseEventsSynthesizedFromTouch); @@ -210,13 +314,11 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, } } - const QPoint winEventPosition(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam)); - if (et & QtWindows::NonClientEventFlag) { - const QPoint globalPosition = winEventPosition; - const QPoint clientPosition = QWindowsGeometryHint::mapFromGlobal(hwnd, globalPosition); + if (mouseEvent.type >= QEvent::NonClientAreaMouseMove && mouseEvent.type <= QEvent::NonClientAreaMouseButtonDblClick) { const Qt::MouseButtons buttons = QWindowsMouseHandler::queryMouseButtons(); QWindowSystemInterface::handleFrameStrutMouseEvent(window, clientPosition, globalPosition, buttons, + mouseEvent.button, mouseEvent.type, QWindowsKeyMapper::queryKeyboardModifiers(), source); return false; // Allow further event processing (dragging of windows). @@ -224,7 +326,8 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, *result = 0; if (msg.message == WM_MOUSELEAVE) { - qCDebug(lcQpaEvents) << "WM_MOUSELEAVE for " << window << " previous window under mouse = " << m_windowUnderMouse << " tracked window =" << m_trackedWindow; + qCDebug(lcQpaEvents) << mouseEvent << "for" << window << "previous window under mouse=" + << m_windowUnderMouse << "tracked window=" << m_trackedWindow; // When moving out of a window, WM_MOUSEMOVE within the moved-to window is received first, // so if m_trackedWindow is not the window here, it means the cursor has left the @@ -269,7 +372,6 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, } } - const QPoint globalPosition = QWindowsGeometryHint::mapToGlobal(hwnd, winEventPosition); // In this context, neither an invisible nor a transparent window (transparent regarding mouse // events, "click-through") can be considered as the window under mouse. QWindow *currentWindowUnderMouse = platformWindow->hasMouseCapture() ? @@ -290,10 +392,7 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, // Qt expects the platform plugin to capture the mouse on // any button press until release. if (!platformWindow->hasMouseCapture() - && (msg.message == WM_LBUTTONDOWN || msg.message == WM_MBUTTONDOWN - || msg.message == WM_RBUTTONDOWN || msg.message == WM_XBUTTONDOWN - || msg.message == WM_LBUTTONDBLCLK || msg.message == WM_MBUTTONDBLCLK - || msg.message == WM_RBUTTONDBLCLK || msg.message == WM_XBUTTONDBLCLK)) { + && (mouseEvent.type == QEvent::MouseButtonPress || mouseEvent.type == QEvent::MouseButtonDblClick)) { platformWindow->setMouseGrabEnabled(true); platformWindow->setFlag(QWindowsWindow::AutoMouseCapture); qCDebug(lcQpaEvents) << "Automatic mouse capture " << window; @@ -302,8 +401,7 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, window->requestActivate(); } else if (platformWindow->hasMouseCapture() && platformWindow->testFlag(QWindowsWindow::AutoMouseCapture) - && (msg.message == WM_LBUTTONUP || msg.message == WM_MBUTTONUP - || msg.message == WM_RBUTTONUP || msg.message == WM_XBUTTONUP) + && mouseEvent.type == QEvent::MouseButtonRelease && !buttons) { platformWindow->setMouseGrabEnabled(false); qCDebug(lcQpaEvents) << "Releasing automatic mouse capture " << window; @@ -369,9 +467,12 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, m_windowUnderMouse = currentWindowUnderMouse; } - QWindowSystemInterface::handleMouseEvent(window, winEventPosition, globalPosition, buttons, - QWindowsKeyMapper::queryKeyboardModifiers(), - source); + if (!discardEvent && mouseEvent.type != QEvent::None) { + QWindowSystemInterface::handleMouseEvent(window, winEventPosition, globalPosition, buttons, + mouseEvent.button, mouseEvent.type, + QWindowsKeyMapper::queryKeyboardModifiers(), + source); + } m_previousCaptureWindow = hasCapture ? window : 0; // QTBUG-48117, force synchronous handling for the extra buttons so that WM_APPCOMMAND // is sent for unhandled WM_XBUTTONDOWN. @@ -397,9 +498,10 @@ static void redirectWheelEvent(QWindow *window, const QPoint &globalPos, int del { // If a window is blocked by modality, it can't get the event. if (isValidWheelReceiver(window)) { + const QPoint point = (orientation == Qt::Vertical) ? QPoint(0, delta) : QPoint(delta, 0); QWindowSystemInterface::handleWheelEvent(window, QWindowsGeometryHint::mapFromGlobal(window, globalPos), - globalPos, delta, orientation, mods); + globalPos, QPoint(), point, mods); } } diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 2431f99ab8..7d213320a4 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -2032,10 +2032,7 @@ void QXcbConnection::initializeAllAtoms() { ++ptr; } - Q_ASSERT(i == QXcbAtom::NPredefinedAtoms); - - const QByteArray settings_atom_name = "_QT_SETTINGS_TIMESTAMP_" + m_displayName; - names[i++] = settings_atom_name; + Q_ASSERT(i == QXcbAtom::NAtoms); xcb_intern_atom_cookie_t cookies[QXcbAtom::NAtoms]; diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index ced5208c81..4a9958f334 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -303,9 +303,6 @@ namespace QXcbAtom { _COMPIZ_TOOLKIT_ACTION, _GTK_LOAD_ICONTHEMES, - NPredefinedAtoms, - - _QT_SETTINGS_TIMESTAMP = NPredefinedAtoms, NAtoms }; } diff --git a/src/plugins/platforms/xcb/xcb-static/xcb-static.pro b/src/plugins/platforms/xcb/xcb-static/xcb-static.pro index 20d8b83e7c..078b275381 100644 --- a/src/plugins/platforms/xcb/xcb-static/xcb-static.pro +++ b/src/plugins/platforms/xcb/xcb-static/xcb-static.pro @@ -2,7 +2,7 @@ # Statically compile in code for # libxcb-fixes, libxcb-randr, libxcb-shm, libxcb-sync, libxcb-image, # libxcb-keysyms, libxcb-icccm, libxcb-renderutil, libxcb-xkb, -# libxcb-xinerama +# libxcb-xinerama, libxcb-xinput # CONFIG += static @@ -29,7 +29,8 @@ SOURCES += \ $$LIBXCB_DIR/render.c \ $$LIBXCB_DIR/shape.c \ $$LIBXCB_DIR/xkb.c \ - $$LIBXCB_DIR/xinerama.c + $$LIBXCB_DIR/xinerama.c \ + $$LIBXCB_DIR/xinput.c # # xcb-util diff --git a/src/plugins/printsupport/cups/qcupsprintengine.cpp b/src/plugins/printsupport/cups/qcupsprintengine.cpp index bd0d641e79..725ee0d58b 100644 --- a/src/plugins/printsupport/cups/qcupsprintengine.cpp +++ b/src/plugins/printsupport/cups/qcupsprintengine.cpp @@ -132,6 +132,9 @@ QVariant QCupsPrintEngine::property(PrintEnginePropertyKey key) const case PPK_CupsOptions: ret = d->cupsOptions; break; + case PPK_Duplex: + ret = d->duplex; + break; default: ret = QPdfPrintEngine::property(key); break; @@ -140,7 +143,9 @@ QVariant QCupsPrintEngine::property(PrintEnginePropertyKey key) const } -QCupsPrintEnginePrivate::QCupsPrintEnginePrivate(QPrinter::PrinterMode m) : QPdfPrintEnginePrivate(m) +QCupsPrintEnginePrivate::QCupsPrintEnginePrivate(QPrinter::PrinterMode m) + : QPdfPrintEnginePrivate(m) + , duplex(QPrint::DuplexNone) { } diff --git a/src/plugins/printsupport/cups/qcupsprintengine_p.h b/src/plugins/printsupport/cups/qcupsprintengine_p.h index d5363bb8cc..2a1a83b9d7 100644 --- a/src/plugins/printsupport/cups/qcupsprintengine_p.h +++ b/src/plugins/printsupport/cups/qcupsprintengine_p.h @@ -99,6 +99,7 @@ private: QPrintDevice m_printDevice; QStringList cupsOptions; QString cupsTempFile; + QPrint::DuplexMode duplex; }; QT_END_NAMESPACE diff --git a/src/plugins/sqldrivers/configure.json b/src/plugins/sqldrivers/configure.json index 234f880579..4802d3b04d 100644 --- a/src/plugins/sqldrivers/configure.json +++ b/src/plugins/sqldrivers/configure.json @@ -229,7 +229,7 @@ Oracle driver, as the current build will most likely fail." "summary": [ { - "section": "Qt Sql", + "section": "Qt Sql Drivers", "entries": [ "sql-db2", "sql-ibase", "sql-mysql", "sql-oci", "sql-odbc", "sql-psql", "sql-sqlite2", "sql-sqlite", "system-sqlite", "sql-tds" diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm index 9e8af78a8e..cdddf227ac 100644 --- a/src/plugins/styles/mac/qmacstyle_mac.mm +++ b/src/plugins/styles/mac/qmacstyle_mac.mm @@ -145,22 +145,12 @@ static QWindow *qt_getWindow(const QWidget *widget) return widget ? widget->window()->windowHandle() : 0; } -@interface QT_MANGLE_NAMESPACE(NotificationReceiver) : NSObject { -QMacStylePrivate *mPrivate; -} -- (id)initWithPrivate:(QMacStylePrivate *)priv; -- (void)scrollBarStyleDidChange:(NSNotification *)notification; +@interface QT_MANGLE_NAMESPACE(NotificationReceiver) : NSObject @end QT_NAMESPACE_ALIAS_OBJC_CLASS(NotificationReceiver); @implementation NotificationReceiver -- (id)initWithPrivate:(QMacStylePrivate *)priv -{ - self = [super init]; - mPrivate = priv; - return self; -} - (void)scrollBarStyleDidChange:(NSNotification *)notification { @@ -2285,7 +2275,7 @@ QMacStyle::QMacStyle() Q_D(QMacStyle); QMacAutoReleasePool pool; - d->receiver = [[NotificationReceiver alloc] initWithPrivate:d]; + d->receiver = [[NotificationReceiver alloc] init]; [[NSNotificationCenter defaultCenter] addObserver:d->receiver selector:@selector(scrollBarStyleDidChange:) name:NSPreferredScrollerStyleDidChangeNotification @@ -4513,7 +4503,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter const auto cw = QMacStylePrivate::CocoaControl(ct, QStyleHelper::SizeLarge); auto *sv = static_cast<NSSplitView *>(d->cocoaControl(cw)); sv.frame = opt->rect.toCGRect(); - d->drawNSViewInRect(cw, sv, opt->rect, p, w != nullptr, ^(CGContextRef ctx, const CGRect &rect) { + d->drawNSViewInRect(cw, sv, opt->rect, p, w != nullptr, ^(CGContextRef __unused ctx, const CGRect &rect) { [sv drawDividerInRect:rect]; }); } else { @@ -6209,7 +6199,7 @@ QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, switch (ct) { #if QT_CONFIG(spinbox) case CT_SpinBox: - if (const QStyleOptionSpinBox *vopt = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) { + if (qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) { const int buttonWidth = 20; // FIXME Use subControlRect() sz += QSize(buttonWidth, -3); } |