diff options
Diffstat (limited to 'src/plugins/platforms/cocoa/qnsview.mm')
-rw-r--r-- | src/plugins/platforms/cocoa/qnsview.mm | 129 |
1 files changed, 86 insertions, 43 deletions
diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index 1ec33df744..0d80333e65 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -39,9 +39,9 @@ #include "qnsview.h" #include "qcocoawindow.h" #include "qcocoahelpers.h" -#include "qcocoaautoreleasepool.h" #include "qmultitouch_mac_p.h" #include "qcocoadrag.h" +#include "qcocoainputcontext.h" #include <qpa/qplatformintegration.h> #include <qpa/qwindowsysteminterface.h> @@ -153,6 +153,8 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; m_mouseMoveHelper = [[QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper) alloc] initWithView:self]; m_resendKeyEvent = false; m_scrolling = false; + m_updatingDrag = false; + m_currentlyInterpretedKeyEvent = 0; if (!touchDevice) { touchDevice = new QTouchDevice; @@ -160,6 +162,8 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::NormalizedPosition | QTouchDevice::MouseEmulation); QWindowSystemInterface::registerTouchDevice(touchDevice); } + + m_isMenuView = false; } return self; } @@ -213,6 +217,11 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; name:NSViewFrameDidChangeNotification object:self]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(textInputContextKeyboardSelectionDidChangeNotification:) + name:NSTextInputContextKeyboardSelectionDidChangeNotification + object:nil]; + return self; } @@ -269,11 +278,11 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; - (void)viewDidMoveToWindow { + m_isMenuView = [self.window.className isEqualToString:@"NSCarbonMenuWindow"]; if (self.window) { // This is the case of QWidgetAction's generated QWidget inserted in an NSMenu. // 10.9 and newer get the NSWindowDidChangeOcclusionStateNotification - if ((!_q_NSWindowDidChangeOcclusionStateNotification - && [self.window.className isEqualToString:@"NSCarbonMenuWindow"])) { + if (!_q_NSWindowDidChangeOcclusionStateNotification && m_isMenuView) { m_exposedOnMoveToWindow = true; m_platformWindow->exposeWindow(); } @@ -347,6 +356,12 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; if (m_platformWindow->m_nsWindow && geometry == m_platformWindow->geometry()) return; + // It can happen that self.window is nil (if we are changing + // styleMask from/to borderless and content view is being re-parented) + // - this results in an invalid coordinates. + if (m_platformWindow->m_inSetStyleMask && !self.window) + return; + #ifdef QT_COCOA_ENABLE_WINDOW_DEBUG qDebug() << "QNSView::udpateGeometry" << m_platformWindow << geometry; #endif @@ -396,7 +411,7 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; NSString *notificationName = [windowNotification name]; if (notificationName == NSWindowDidBecomeKeyNotification) { - if (!m_platformWindow->windowIsPopupType()) + if (!m_platformWindow->windowIsPopupType() && !m_isMenuView) QWindowSystemInterface::handleWindowActivated(m_window); } else if (notificationName == NSWindowDidResignKeyNotification) { // key window will be non-nil if another window became key... do not @@ -405,7 +420,7 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; NSWindow *keyWindow = [NSApp keyWindow]; if (!keyWindow) { // no new key window, go ahead and set the active window to zero - if (!m_platformWindow->windowIsPopupType()) + if (!m_platformWindow->windowIsPopupType() && !m_isMenuView) QWindowSystemInterface::handleWindowActivated(0); } } else if (notificationName == NSWindowDidMiniaturizeNotification @@ -425,16 +440,17 @@ QT_WARNING_PUSH QT_WARNING_DISABLE_CLANG("-Wobjc-method-access") enum { NSWindowOcclusionStateVisible = 1UL << 1 }; #endif - if ((NSUInteger)[self.window occlusionState] & NSWindowOcclusionStateVisible) { - m_platformWindow->exposeWindow(); - } else { - // Send Obscure events on window occlusion to stop animations. Several - // unit tests expect paint and/or expose events for windows that are - // sometimes (unpredictably) occlouded: Don't send Obscure events when - // running under QTestLib. - static bool onTestLib = qt_mac_resolveOption(false, "QT_QTESTLIB_RUNNING"); - if (!onTestLib) + // Several unit tests expect paint and/or expose events for windows that are + // sometimes (unpredictably) occluded and some unit tests depend on QWindow::isExposed - + // don't send Expose/Obscure events when running under QTestLib. + static const bool onTestLib = qt_mac_resolveOption(false, "QT_QTESTLIB_RUNNING"); + if (!onTestLib) { + if ((NSUInteger)[self.window occlusionState] & NSWindowOcclusionStateVisible) { + m_platformWindow->exposeWindow(); + } else { + // Send Obscure events on window occlusion to stop animations. m_platformWindow->obscureWindow(); + } } #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9 QT_WARNING_POP @@ -456,6 +472,15 @@ QT_WARNING_POP } } +- (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)notifyWindowWillZoom:(BOOL)willZoom { Qt::WindowState newState = willZoom ? Qt::WindowMaximized : Qt::WindowNoState; @@ -474,6 +499,12 @@ QT_WARNING_POP m_platformWindow->exposeWindow(); } +- (void)removeFromSuperview +{ + QMacAutoReleasePool pool; + [super removeFromSuperview]; +} + - (void) flushBackingStore:(QCocoaBackingStore *)backingStore region:(const QRegion &)region offset:(QPoint)offset { m_backingStore = backingStore; @@ -615,13 +646,15 @@ QT_WARNING_POP { if (m_window && (m_window->flags() & Qt::WindowTransparentForInput) ) return NO; - if (!m_platformWindow->windowIsPopupType()) + if (!m_platformWindow->windowIsPopupType() && !m_isMenuView) QWindowSystemInterface::handleWindowActivated([self topLevelWindow]); return YES; } - (BOOL)acceptsFirstResponder { + if (m_isMenuView) + return NO; if (m_platformWindow->shouldRefuseKeyWindowAndFirstResponder()) return NO; if (m_window && (m_window->flags() & Qt::WindowTransparentForInput) ) @@ -852,7 +885,7 @@ QT_WARNING_POP { [super updateTrackingAreas]; - QCocoaAutoReleasePool pool; + 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]) @@ -1435,41 +1468,42 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) if (!(modifiers & (Qt::ControlModifier | Qt::MetaModifier)) && (ch.unicode() < 0xf700 || ch.unicode() > 0xf8ff)) text = QCFString::toQString(characters); - QWindow *focusWindow = [self topLevelWindow]; + 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()) { - QKeyEvent override(QEvent::ShortcutOverride, keyCode, modifiers, - nativeScanCode, nativeVirtualKey, nativeModifiers, text, [nsevent isARepeat], 1); - override.setTimestamp(timestamp); - m_sendKeyEvent = !QWindowSystemInterface::tryHandleShortcutOverrideEvent(focusWindow, &override); + m_sendKeyEvent = !QWindowSystemInterface::handleShortcutEvent(window, timestamp, keyCode, + modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers, text, [nsevent isARepeat], 1); } QObject *fo = QGuiApplication::focusObject(); if (m_sendKeyEvent && fo) { - // if escape is pressed we don't want interpretKeyEvents to close a dialog. This will be done via QWindowSystemInterface - if (keyCode == Qt::Key_Escape) - m_platformWindow->m_ignoreWindowShouldClose = true; - 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; } } - - m_platformWindow->m_ignoreWindowShouldClose = false;; } if (m_resendKeyEvent) m_sendKeyEvent = true; } if (m_sendKeyEvent && m_composingText.isEmpty()) - QWindowSystemInterface::handleExtendedKeyEvent(focusWindow, timestamp, QEvent::Type(eventType), keyCode, modifiers, + QWindowSystemInterface::handleExtendedKeyEvent(window, timestamp, QEvent::Type(eventType), keyCode, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers, text, [nsevent isARepeat], 1, false); m_sendKeyEvent = false; @@ -1490,21 +1524,23 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) [self handleKeyEvent:nsevent eventType:int(QEvent::KeyRelease)]; } -- (BOOL)performKeyEquivalent:(NSEvent *)nsevent +- (void)cancelOperation:(id)sender { - NSString *chars = [nsevent charactersIgnoringModifiers]; + Q_UNUSED(sender); - if ([nsevent type] == NSKeyDown && [chars length] > 0) { - QChar ch = [chars characterAtIndex:0]; - Qt::Key qtKey = qt_mac_cocoaKey2QtKey(ch); - // check for Command + Key_Period - if ([nsevent modifierFlags] & NSCommandKeyMask - && qtKey == Qt::Key_Period) { - [self handleKeyEvent:nsevent eventType:int(QEvent::KeyPress)]; - return YES; - } - } - return [super performKeyEquivalent:nsevent]; + 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 @@ -1793,7 +1829,7 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) -(void)registerDragTypes { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; QStringList customTypes = qt_mac_enabledDraggedTypes(); if (currentCustomDragTypes == 0 || *currentCustomDragTypes != customTypes) { if (currentCustomDragTypes == 0) @@ -1891,6 +1927,9 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin // 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, @@ -1908,7 +1947,11 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender { - return [self handleDrag : sender]; + m_updatingDrag = true; + const NSDragOperation ret([self handleDrag : sender]); + m_updatingDrag = false; + + return ret; } // Sends drag update to Qt, return the action |