From d1114669e301e35cc4e9b2e4c8c4b9476180fb56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Wed, 9 Oct 2013 08:43:34 +0200 Subject: Cocoa: Improve cursor setting. Implement cursor setting in terms of [NSCursor set] and [NSView cursorUpdate] using the window tracking area. Refactor cursor conversion into QCocoaCursor:: convertCursor. Rename QCoocaWindow::m_underMouseWindow to m_enterLeaveTargetWindow since it's set according to spesific enter/leave logic. Add m_windowUnderMouse which tracks mouseEntered/mouseExited state. Task-number: QTBUG-33961 Change-Id: Id5e12594f5db365e09c9926a4c08d748a9afb935 Reviewed-by: Gabriel de Dietrich --- src/plugins/platforms/cocoa/qcocoacursor.h | 5 +- src/plugins/platforms/cocoa/qcocoacursor.mm | 75 ++++++++++++++++------------- src/plugins/platforms/cocoa/qcocoawindow.h | 6 ++- src/plugins/platforms/cocoa/qcocoawindow.mm | 19 ++++++++ src/plugins/platforms/cocoa/qnsview.mm | 27 ++++++++--- 5 files changed, 88 insertions(+), 44 deletions(-) diff --git a/src/plugins/platforms/cocoa/qcocoacursor.h b/src/plugins/platforms/cocoa/qcocoacursor.h index dfa1fcff81..f332240724 100644 --- a/src/plugins/platforms/cocoa/qcocoacursor.h +++ b/src/plugins/platforms/cocoa/qcocoacursor.h @@ -55,12 +55,13 @@ public: QCocoaCursor(); ~QCocoaCursor(); - virtual void changeCursor(QCursor * widgetCursor, QWindow * widget); + virtual void changeCursor(QCursor *cursor, QWindow *window); virtual QPoint pos() const; virtual void setPos(const QPoint &position); private: QHash m_cursors; - NSCursor *createCursorData(QCursor *); + NSCursor *convertCursor(QCursor *cursor); + NSCursor *createCursorData(QCursor * cursor); NSCursor *createCursorFromBitmap(const QBitmap *bitmap, const QBitmap *mask, const QPoint hotspot = QPoint()); NSCursor *createCursorFromPixmap(const QPixmap pixmap, const QPoint hotspot = QPoint()); }; diff --git a/src/plugins/platforms/cocoa/qcocoacursor.mm b/src/plugins/platforms/cocoa/qcocoacursor.mm index d734c36d6f..13f6423701 100644 --- a/src/plugins/platforms/cocoa/qcocoacursor.mm +++ b/src/plugins/platforms/cocoa/qcocoacursor.mm @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qcocoacursor.h" +#include "qcocoawindow.h" #include "qcocoahelpers.h" #include "qcocoaautoreleasepool.h" @@ -63,82 +64,90 @@ QCocoaCursor::~QCocoaCursor() void QCocoaCursor::changeCursor(QCursor *cursor, QWindow *window) { - Q_UNUSED(window); + NSCursor * cocoaCursor = convertCursor(cursor); + if (QPlatformWindow * platformWindow = window->handle()) + static_cast(platformWindow)->setWindowCursor(cocoaCursor); +} + +QPoint QCocoaCursor::pos() const +{ + return qt_mac_flipPoint([NSEvent mouseLocation]).toPoint(); +} + +void QCocoaCursor::setPos(const QPoint &position) +{ + CGPoint pos; + pos.x = position.x(); + pos.y = position.y(); + + CGEventRef e = CGEventCreateMouseEvent(0, kCGEventMouseMoved, pos, 0); + CGEventPost(kCGHIDEventTap, e); + CFRelease(e); +} + +NSCursor *QCocoaCursor::convertCursor(QCursor * cursor) +{ const Qt::CursorShape newShape = cursor ? cursor->shape() : Qt::ArrowCursor; + NSCursor *cocoaCursor; + // Check for a suitable built-in NSCursor first: switch (newShape) { case Qt::ArrowCursor: - [[NSCursor arrowCursor] set]; + cocoaCursor= [NSCursor arrowCursor]; break; case Qt::CrossCursor: - [[NSCursor crosshairCursor] set]; + cocoaCursor = [NSCursor crosshairCursor]; break; case Qt::IBeamCursor: - [[NSCursor IBeamCursor] set]; + cocoaCursor = [NSCursor IBeamCursor]; break; case Qt::WhatsThisCursor: //for now just use the pointing hand case Qt::PointingHandCursor: - [[NSCursor pointingHandCursor] set]; + cocoaCursor = [NSCursor pointingHandCursor]; break; case Qt::SplitVCursor: - [[NSCursor resizeUpDownCursor] set]; + cocoaCursor = [NSCursor resizeUpDownCursor]; break; case Qt::SplitHCursor: - [[NSCursor resizeLeftRightCursor] set]; + cocoaCursor = [NSCursor resizeLeftRightCursor]; break; case Qt::OpenHandCursor: - [[NSCursor openHandCursor] set]; + cocoaCursor = [NSCursor openHandCursor]; break; case Qt::ClosedHandCursor: - [[NSCursor closedHandCursor] set]; + cocoaCursor = [NSCursor closedHandCursor]; break; case Qt::DragMoveCursor: - [[NSCursor crosshairCursor] set]; + cocoaCursor = [NSCursor crosshairCursor]; break; case Qt::DragCopyCursor: - [[NSCursor crosshairCursor] set]; + cocoaCursor = [NSCursor crosshairCursor]; break; case Qt::DragLinkCursor: - [[NSCursor dragLinkCursor] set]; + cocoaCursor = [NSCursor dragLinkCursor]; break; default : { // No suitable OS cursor exist, use cursors provided // by Qt for the rest. Check for a cached cursor: - NSCursor *cocoaCursor = m_cursors.value(newShape); + cocoaCursor = m_cursors.value(newShape); if (cocoaCursor && cursor->shape() == Qt::BitmapCursor) { [cocoaCursor release]; cocoaCursor = 0; } if (cocoaCursor == 0) { cocoaCursor = createCursorData(cursor); - if (cocoaCursor == 0) { - [[NSCursor arrowCursor] set]; - return; - } + if (cocoaCursor == 0) + return [NSCursor arrowCursor]; + m_cursors.insert(newShape, cocoaCursor); } - [cocoaCursor set]; break; } } + return cocoaCursor; } -QPoint QCocoaCursor::pos() const -{ - return qt_mac_flipPoint([NSEvent mouseLocation]).toPoint(); -} - -void QCocoaCursor::setPos(const QPoint &position) -{ - CGPoint pos; - pos.x = position.x(); - pos.y = position.y(); - - CGEventRef e = CGEventCreateMouseEvent(0, kCGEventMouseMoved, pos, 0); - CGEventPost(kCGHIDEventTap, e); - CFRelease(e); -} // Creates an NSCursor for the given QCursor. NSCursor *QCocoaCursor::createCursorData(QCursor *cursor) diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h index 7f0f07e912..4f5a208f43 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.h +++ b/src/plugins/platforms/cocoa/qcocoawindow.h @@ -154,6 +154,8 @@ public: void setMenubar(QCocoaMenuBar *mb); QCocoaMenuBar *menubar() const; + void setWindowCursor(NSCursor *cursor); + void registerTouch(bool enable); qreal devicePixelRatio() const; @@ -190,11 +192,13 @@ public: // for QNSView Qt::WindowState m_synchedWindowState; Qt::WindowModality m_windowModality; QPointer m_activePopupWindow; - QPointer m_underMouseWindow; + QPointer m_enterLeaveTargetWindow; + bool m_windowUnderMouse; bool m_inConstructor; QCocoaGLContext *m_glContext; QCocoaMenuBar *m_menubar; + NSCursor *m_windowCursor; bool m_hasModalSession; bool m_frameStrutEventsEnabled; diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 845cc1202f..565594a98a 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -206,9 +206,11 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw) , m_nsWindowDelegate(0) , m_synchedWindowState(Qt::WindowActive) , m_windowModality(Qt::NonModal) + , m_windowUnderMouse(false) , m_inConstructor(true) , m_glContext(0) , m_menubar(0) + , m_windowCursor(0) , m_hasModalSession(false) , m_frameStrutEventsEnabled(false) , m_isExposed(false) @@ -1030,6 +1032,23 @@ QCocoaMenuBar *QCocoaWindow::menubar() const return m_menubar; } +void QCocoaWindow::setWindowCursor(NSCursor *cursor) +{ + // This function is called (via QCocoaCursor) by Qt to set + // the cursor for this window. It can be called for a window + // that is not currenly under the mouse pointer (for example + // for a popup window.) Qt expects the set cursor to "stick": + // it should be accociated with the window until a different + // cursor is set. + + // Cocoa has different abstractions. We can set the cursor *now*: + if (m_windowUnderMouse) + [cursor set]; + // or we can set the cursor on mouse enter/leave using tracking + // areas. This is done in QNSView, save the cursor: + m_windowCursor = cursor; +} + void QCocoaWindow::registerTouch(bool enable) { m_registerTouchCount += enable ? 1 : -1; diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index 8f839384df..71c4de3b69 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -671,7 +671,7 @@ static QTouchDevice *touchDevice = 0; // 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; + | NSTrackingInVisibleRect | NSTrackingMouseMoved | NSTrackingCursorUpdate; NSTrackingArea *ta = [[[NSTrackingArea alloc] initWithRect:[self frame] options:trackingOptions owner:self @@ -680,6 +680,13 @@ static QTouchDevice *touchDevice = 0; [self addTrackingArea:ta]; } +-(void)cursorUpdate:(NSEvent *)theEvent +{ + Q_UNUSED(theEvent) + if (m_platformWindow->m_windowCursor) + [m_platformWindow->m_windowCursor set]; +} + - (void)mouseMoved:(NSEvent *)theEvent { if (m_window->flags() & Qt::WindowTransparentForInput) @@ -696,9 +703,9 @@ static QTouchDevice *touchDevice = 0; // handling mouseEnter and mouseLeave envents, since they are sent // individually to different views. if (m_platformWindow->m_nsWindow && childWindow) { - if (childWindow != m_platformWindow->m_underMouseWindow) { - QWindowSystemInterface::handleEnterLeaveEvent(childWindow, m_platformWindow->m_underMouseWindow, windowPoint, screenPoint); - m_platformWindow->m_underMouseWindow = childWindow; + if (childWindow != m_platformWindow->m_enterLeaveTargetWindow) { + QWindowSystemInterface::handleEnterLeaveEvent(childWindow, m_platformWindow->m_enterLeaveTargetWindow, windowPoint, screenPoint); + m_platformWindow->m_enterLeaveTargetWindow = childWindow; } } @@ -712,6 +719,8 @@ static QTouchDevice *touchDevice = 0; - (void)mouseEntered:(NSEvent *)theEvent { + m_platformWindow->m_windowUnderMouse = true; + if (m_window->flags() & Qt::WindowTransparentForInput) return [super mouseEntered:theEvent]; @@ -722,12 +731,14 @@ static QTouchDevice *touchDevice = 0; QPointF windowPoint; QPointF screenPoint; [self convertFromScreen:[NSEvent mouseLocation] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; - m_platformWindow->m_underMouseWindow = m_platformWindow->childWindowAt(windowPoint.toPoint()); - QWindowSystemInterface::handleEnterEvent(m_platformWindow->m_underMouseWindow, windowPoint, screenPoint); + m_platformWindow->m_enterLeaveTargetWindow = m_platformWindow->childWindowAt(windowPoint.toPoint()); + QWindowSystemInterface::handleEnterEvent(m_platformWindow->m_enterLeaveTargetWindow, windowPoint, screenPoint); } - (void)mouseExited:(NSEvent *)theEvent { + m_platformWindow->m_windowUnderMouse = false; + if (m_window->flags() & Qt::WindowTransparentForInput) return [super mouseExited:theEvent]; Q_UNUSED(theEvent); @@ -736,8 +747,8 @@ static QTouchDevice *touchDevice = 0; if (!m_platformWindow->m_nsWindow) return; - QWindowSystemInterface::handleLeaveEvent(m_platformWindow->m_underMouseWindow); - m_platformWindow->m_underMouseWindow = 0; + QWindowSystemInterface::handleLeaveEvent(m_platformWindow->m_enterLeaveTargetWindow); + m_platformWindow->m_enterLeaveTargetWindow = 0; } - (void)rightMouseDown:(NSEvent *)theEvent -- cgit v1.2.3