From b2c73c73cd0a68aae0586cf447c2612c13aeb52c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Wed, 27 Feb 2013 10:27:20 +0100 Subject: Cocoa: Correct enter/leave event handling. Make top-level windows generate enter/leave events for sub-windows. Keep track of the current "under mouse" window in mouseMoved and send EnterLeave events when it changes. mouseEntered/mouseExited handles enters and leaves from the top-level window. Add tests/manual/cocoa/nativewidgets. Task-number: QTBUG-27550 Task-number: QTBUG-29751 Change-Id: If4b9f9e0f39d9fb05fdab45a100ffdcf107965ad Reviewed-by: Gabriel de Dietrich --- src/plugins/platforms/cocoa/qcocoawindow.h | 2 ++ src/plugins/platforms/cocoa/qcocoawindow.mm | 15 ++++++++++ src/plugins/platforms/cocoa/qnsview.mm | 44 ++++++++++++++++++++++++++--- 3 files changed, 57 insertions(+), 4 deletions(-) (limited to 'src/plugins/platforms') diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h index c19017166b..5029321247 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.h +++ b/src/plugins/platforms/cocoa/qcocoawindow.h @@ -150,6 +150,7 @@ public: qreal devicePixelRatio() const; void exposeWindow(); void obscureWindow(); + QWindow *childWindowAt(QPoint windowPoint); protected: // NSWindow handling. The QCocoaWindow/QNSView can either be displayed // in an existing NSWindow or in one created by Qt. @@ -177,6 +178,7 @@ public: // for QNSView Qt::WindowState m_synchedWindowState; Qt::WindowModality m_windowModality; QPointer m_activePopupWindow; + QPointer m_underMouseWindow; bool m_inConstructor; QCocoaGLContext *m_glContext; diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index a7e75480ea..9988cea597 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -927,6 +927,21 @@ void QCocoaWindow::obscureWindow() } } +QWindow *QCocoaWindow::childWindowAt(QPoint windowPoint) +{ + QWindow *targetWindow = window(); + foreach (QObject *child, targetWindow->children()) { + if (QWindow *childWindow = qobject_cast(child)) { + if (childWindow->geometry().contains(windowPoint)) { + QCocoaWindow* platformWindow = static_cast(childWindow->handle()); + targetWindow = platformWindow->childWindowAt(windowPoint - childWindow->position()); + } + } + } + + return targetWindow; +} + QMargins QCocoaWindow::frameMargins() const { NSRect frameW = [m_nsWindow frame]; diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index 12a6bb9e69..3046b898df 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -599,6 +599,7 @@ static QTouchDevice *touchDevice = 0; if (NSIsEmptyRect([self visibleRect])) return; + // Remove current trakcing areas: QCocoaAutoReleasePool pool; if (NSArray *trackingArray = [self trackingAreas]) { NSUInteger size = [trackingArray count]; @@ -611,7 +612,7 @@ static QTouchDevice *touchDevice = 0; // 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 QCocoaView gets to pay the cost of + // 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 @@ -628,23 +629,58 @@ static QTouchDevice *touchDevice = 0; { if (m_window->flags() & Qt::WindowTransparentForInput) return [super mouseMoved:theEvent]; - [self handleMouseEvent:theEvent]; + + QPoint windowPoint, screenPoint; + [self convertFromEvent:theEvent toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; + QWindow *childWindow = m_platformWindow->childWindowAt(windowPoint); + + // 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->m_nsWindow && childWindow) { + if (childWindow != m_platformWindow->m_underMouseWindow) { + QWindowSystemInterface::handleEnterLeaveEvent(childWindow, m_platformWindow->m_underMouseWindow, windowPoint, screenPoint); + m_platformWindow->m_underMouseWindow = 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_window) + return; + + [self handleMouseEvent: theEvent]; } - (void)mouseEntered:(NSEvent *)theEvent { if (m_window->flags() & Qt::WindowTransparentForInput) return [super mouseEntered:theEvent]; + + // Top-level windows generate enter events for sub-windows. + if (!m_platformWindow->m_nsWindow) + return; + QPoint windowPoint, screenPoint; [self convertFromEvent:theEvent toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; - QWindowSystemInterface::handleEnterEvent(m_window, windowPoint, screenPoint); + m_platformWindow->m_underMouseWindow = m_platformWindow->childWindowAt(windowPoint); + QWindowSystemInterface::handleEnterEvent(m_platformWindow->m_underMouseWindow, windowPoint, screenPoint); } - (void)mouseExited:(NSEvent *)theEvent { if (m_window->flags() & Qt::WindowTransparentForInput) return [super mouseExited:theEvent]; - QWindowSystemInterface::handleLeaveEvent(m_window); + Q_UNUSED(theEvent); + + // Top-level windows generate leave events for sub-windows. + if (!m_platformWindow->m_nsWindow) + return; + + QWindowSystemInterface::handleLeaveEvent(m_platformWindow->m_underMouseWindow); + m_platformWindow->m_underMouseWindow = 0; } - (void)rightMouseDown:(NSEvent *)theEvent -- cgit v1.2.3