diff options
Diffstat (limited to 'src/plugins/platforms/cocoa/qnsview.mm')
-rw-r--r-- | src/plugins/platforms/cocoa/qnsview.mm | 209 |
1 files changed, 84 insertions, 125 deletions
diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index 1f58a0457f..0ce5b3b332 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -143,7 +143,7 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; { self = [super initWithFrame : NSMakeRect(0,0, 300,300)]; if (self) { - m_backingStore = 0; + m_pixelRatio = 1.; m_maskImage = 0; m_shouldInvalidateWindowShadow = false; m_window = 0; @@ -361,12 +361,11 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; // Send a geometry change event to Qt, if it's ready to handle events if (!m_platformWindow->m_inConstructor) { QWindowSystemInterface::handleGeometryChange(m_window, geometry); - // Do not send incorrect exposes in case the window is not even visible yet. - // We might get here as a result of a resize() from QWidget's show(), for instance. - if (m_platformWindow->window()->isVisible()) { - m_platformWindow->updateExposedGeometry(); + m_platformWindow->updateExposedGeometry(); + // Guard against processing window system events during QWindow::setGeometry + // calles, which Qt and Qt applications do not excpect. + if (!m_platformWindow->m_inSetGeometry) QWindowSystemInterface::flushWindowSystemEvents(); - } } } @@ -408,10 +407,6 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; Qt::WindowState newState = notificationName == NSWindowDidMiniaturizeNotification ? Qt::WindowMinimized : Qt::WindowNoState; [self notifyWindowStateChanged:newState]; - // NSWindowDidOrderOnScreenAndFinishAnimatingNotification is private API, and not - // emitted in 10.6, so we bring back the old behavior for that case alone. - if (newState == Qt::WindowNoState && QSysInfo::QSysInfo::MacintoshVersion == QSysInfo::MV_10_6) - m_platformWindow->exposeWindow(); } else if ([notificationName isEqualToString: @"NSWindowDidOrderOffScreenNotification"]) { m_platformWindow->obscureWindow(); } else if ([notificationName isEqualToString: @"NSWindowDidOrderOnScreenAndFinishAnimatingNotification"]) { @@ -424,12 +419,17 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; #pragma clang diagnostic ignored "-Wobjc-method-access" enum { NSWindowOcclusionStateVisible = 1UL << 1 }; #endif - // Older versions managed in -[QNSView viewDidMoveToWindow]. - // Support QWidgetAction in NSMenu. Mavericks only sends this notification. - // Ideally we should support this in Qt as well, in order to disable animations - // when the window is occluded. - if ((NSUInteger)[self.window occlusionState] & NSWindowOcclusionStateVisible) + 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) + m_platformWindow->obscureWindow(); + } #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9 #pragma clang diagnostic pop #endif @@ -437,24 +437,16 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; if (m_window) { NSUInteger screenIndex = [[NSScreen screens] indexOfObject:self.window.screen]; if (screenIndex != NSNotFound) { - m_platformWindow->updateExposedGeometry(); QCocoaScreen *cocoaScreen = QCocoaIntegration::instance()->screenAtIndex(screenIndex); QWindowSystemInterface::handleWindowScreenChanged(m_window, cocoaScreen->screen()); + m_platformWindow->updateExposedGeometry(); } } - } else { - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 - if (QSysInfo::QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) { - if (notificationName == NSWindowDidEnterFullScreenNotification - || notificationName == NSWindowDidExitFullScreenNotification) { - Qt::WindowState newState = notificationName == NSWindowDidEnterFullScreenNotification ? - Qt::WindowFullScreen : Qt::WindowNoState; - [self notifyWindowStateChanged:newState]; - } - } -#endif - + } else if (notificationName == NSWindowDidEnterFullScreenNotification + || notificationName == NSWindowDidExitFullScreenNotification) { + Qt::WindowState newState = notificationName == NSWindowDidEnterFullScreenNotification ? + Qt::WindowFullScreen : Qt::WindowNoState; + [self notifyWindowStateChanged:newState]; } } @@ -478,8 +470,9 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; - (void) flushBackingStore:(QCocoaBackingStore *)backingStore region:(const QRegion &)region offset:(QPoint)offset { - m_backingStore = backingStore; - m_backingStoreOffset = offset * m_backingStore->getBackingStoreDevicePixelRatio(); + m_backingStore = backingStore->toImage(); + m_pixelRatio = backingStore->getBackingStoreDevicePixelRatio(); + m_backingStoreOffset = offset * m_pixelRatio; foreach (QRect rect, region.rects()) { [self setNeedsDisplayInRect:NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height())]; } @@ -543,7 +536,7 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; if (m_platformWindow->m_drawContentBorderGradient) NSDrawWindowBackground(dirtyRect); - if (!m_backingStore) + if (m_backingStore.isNull()) return; // Calculate source and target rects. The target rect is the dirtyRect: @@ -551,11 +544,10 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; // The backing store source rect will be larger on retina displays. // Scale dirtyRect by the device pixel ratio: - const qreal devicePixelRatio = m_backingStore->getBackingStoreDevicePixelRatio(); - CGRect dirtyBackingRect = CGRectMake(dirtyRect.origin.x * devicePixelRatio, - dirtyRect.origin.y * devicePixelRatio, - dirtyRect.size.width * devicePixelRatio, - dirtyRect.size.height * devicePixelRatio); + CGRect dirtyBackingRect = CGRectMake(dirtyRect.origin.x * m_pixelRatio, + dirtyRect.origin.y * m_pixelRatio, + dirtyRect.size.width * m_pixelRatio, + dirtyRect.size.height * m_pixelRatio); NSGraphicsContext *nsGraphicsContext = [NSGraphicsContext currentContext]; CGContextRef cgContext = (CGContextRef) [nsGraphicsContext graphicsPort]; @@ -581,7 +573,7 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; dirtyBackingRect.size.width, dirtyBackingRect.size.height ); - CGImageRef bsCGImage = m_backingStore->getBackingStoreCGImage(); + CGImageRef bsCGImage = qt_mac_toCGImage(m_backingStore); CGImageRef cleanImg = CGImageCreateWithImageInRect(bsCGImage, backingStoreRect); // Optimization: Copy frame buffer content instead of blending for @@ -596,10 +588,9 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; CGContextRestoreGState(cgContext); CGImageRelease(cleanImg); CGImageRelease(subMask); + CGImageRelease(bsCGImage); [self invalidateWindowShadowIfNeeded]; - - m_backingStore = 0; } - (BOOL) isFlipped @@ -611,7 +602,8 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; { if (m_window->flags() & Qt::WindowTransparentForInput) return NO; - QWindowSystemInterface::handleWindowActivated([self topLevelWindow]); + if (!m_platformWindow->windowIsPopupType()) + QWindowSystemInterface::handleWindowActivated([self topLevelWindow]); return YES; } @@ -656,16 +648,8 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; NSWindow *window = [self window]; NSPoint nsWindowPoint; - // Use convertRectToScreen if available (added in 10.7). -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 - if ([window respondsToSelector:@selector(convertRectFromScreen:)]) { - NSRect windowRect = [window convertRectFromScreen:NSMakeRect(mouseLocation.x, mouseLocation.y, 1, 1)]; - nsWindowPoint = windowRect.origin; // NSWindow coordinates - } else -#endif - { - nsWindowPoint = [window convertScreenToBase:mouseLocation]; // NSWindow coordinates - } + NSRect windowRect = [window convertRectFromScreen:NSMakeRect(mouseLocation.x, mouseLocation.y, 1, 1)]; + nsWindowPoint = windowRect.origin; // NSWindow coordinates NSPoint nsViewPoint = [self convertPoint: nsWindowPoint fromView: nil]; // NSView/QWindow coordinates *qtWindowPoint = QPointF(nsViewPoint.x, nsViewPoint.y); // NSView/QWindow coordinates @@ -678,6 +662,19 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; 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 { [self handleTabletEvent: theEvent]; @@ -692,23 +689,7 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; m_platformWindow->m_forwardWindow = 0; } - NSPoint globalPos = [NSEvent mouseLocation]; - - if ([self.window parentWindow] - && (theEvent.type == NSLeftMouseDragged || theEvent.type == NSLeftMouseUp)) { - // QToolBar can be implemented as a child window on top of its main window - // (with a borderless NSWindow). If an option "unified toolbar" set on the main window, - // it's possible to drag such a window using this toolbar. - // While handling mouse drag events, QToolBar moves the window (QWidget::move). - // In such a combination [NSEvent mouseLocation] is very different from the - // real event location and as a result a window will move chaotically. - NSPoint winPoint = [theEvent locationInWindow]; - NSRect tmpRect = NSMakeRect(winPoint.x, winPoint.y, 1., 1.); - tmpRect = [[theEvent window] convertRectToScreen:tmpRect]; - globalPos = tmpRect.origin; - } - - [targetView convertFromScreen:globalPos toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint]; + [targetView convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint]; ulong timestamp = [theEvent timestamp] * 1000; QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); @@ -881,7 +862,7 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; QPointF windowPoint; QPointF screenPoint; - [self convertFromScreen:[NSEvent mouseLocation] toWindowPoint:&windowPoint andScreenPoint:&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. @@ -1296,18 +1277,8 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) // 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 - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 - if ([theEvent respondsToSelector:@selector(scrollingDeltaX)]) { - angleDelta.setX([theEvent scrollingDeltaX] * pixelsToDegrees); - angleDelta.setY([theEvent scrollingDeltaY] * pixelsToDegrees); - } else -#endif - { - angleDelta.setX([theEvent deviceDeltaX] * pixelsToDegrees); - angleDelta.setY([theEvent deviceDeltaY] * pixelsToDegrees); - } - + angleDelta.setX([theEvent scrollingDeltaX] * pixelsToDegrees); + angleDelta.setY([theEvent scrollingDeltaY] * pixelsToDegrees); } else { // carbonEventKind == kEventMouseWheelMoved // Remove acceleration, and use either -120 or 120 as delta: @@ -1316,20 +1287,16 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) } QPoint pixelDelta; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 - if ([theEvent respondsToSelector:@selector(scrollingDeltaX)]) { - 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); - } + 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); } -#endif QPointF qt_windowPoint; QPointF qt_screenPoint; @@ -1337,44 +1304,36 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) NSTimeInterval timestamp = [theEvent timestamp]; ulong qt_timestamp = timestamp * 1000; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 - if ([theEvent respondsToSelector:@selector(scrollingDeltaX)]) { - // 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]]; - } + // 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; + NSEventPhase phase = [theEvent phase]; + Qt::ScrollPhase ph = Qt::ScrollUpdate; #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 - if (QSysInfo::QSysInfo::MacintoshVersion >= QSysInfo::MV_10_8) { - // On 10.8 and above, MayBegin is likely to happen. We treat it the same as an actual begin. - if (phase == NSEventPhaseMayBegin) - ph = Qt::ScrollBegin; - } else + if (QSysInfo::QSysInfo::MacintoshVersion >= QSysInfo::MV_10_8) { + // On 10.8 and above, MayBegin is likely to happen. We treat it the same as an actual begin. + if (phase == NSEventPhaseMayBegin) + ph = Qt::ScrollBegin; + } else #endif if (phase == NSEventPhaseBegan) { // On 10.7, MayBegin will not happen, so Began is the actual beginning. ph = Qt::ScrollBegin; } - if (phase == NSEventPhaseEnded || phase == NSEventPhaseCancelled) { - ph = Qt::ScrollEnd; - } - - QWindowSystemInterface::handleWheelEvent(m_window, qt_timestamp, qt_windowPoint, qt_screenPoint, pixelDelta, angleDelta, currentWheelModifiers, ph); - } else -#endif - { - QWindowSystemInterface::handleWheelEvent(m_window, qt_timestamp, qt_windowPoint, qt_screenPoint, pixelDelta, angleDelta, - [QNSView convertKeyModifiers:[theEvent modifierFlags]]); + if (phase == NSEventPhaseEnded || phase == NSEventPhaseCancelled) { + ph = Qt::ScrollEnd; } + + QWindowSystemInterface::handleWheelEvent(m_window, qt_timestamp, qt_windowPoint, qt_screenPoint, pixelDelta, angleDelta, currentWheelModifiers, ph); } #endif //QT_NO_WHEELEVENT @@ -1432,7 +1391,7 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) 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 (ch.unicode() < 0xf700 || ch.unicode() > 0xf8ff) + if (!(modifiers & (Qt::ControlModifier | Qt::MetaModifier)) && (ch.unicode() < 0xf700 || ch.unicode() > 0xf8ff)) text = QCFString::toQString(characters); QWindow *focusWindow = [self topLevelWindow]; |