From 75995d8e58d4df4b1fbfb7bfd9d54b768c9ba8a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Tue, 11 Apr 2023 19:35:52 +0200 Subject: [PATCH] macOS: Synthesize cursor updates on QCocoaWindow::setWindowCursor The original issue for doing this was that invalidateCursorRectsForView would not result in an updateCursor callback in certain scenarios on macOS 11 and below. In macOS 13, improvements to how tracking areas work now result in also missing cursorUpdate calls when the mouse is pressed, which makes sense for tracking areas in general (you don't want a drag over a text field to reset the cursor to the I-bream), but not for our specific case of setting a cursor synchronously. To ensure the cursor is updated immediately, even if the mouse is pressed, we synthesize a updateCursor event, just like we did for the invalidateCursorRectsForView workaround. It's up to clients of QWindow to manage their setCursor calls to not happen during a drag operation, which we already manage in Qt Widgets. Pick-to: 6.5 6.2 Change-Id: I67d6e0f8e270b40da9879828455f4de943da7839 Reviewed-by: Timur Pocheptsov --- src/plugins/platforms/cocoa/qcocoawindow.mm | 38 +++++++++++---------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index f29927751a..bb86040481 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -1788,26 +1788,28 @@ void QCocoaWindow::setWindowCursor(NSCursor *cursor) view.cursor = cursor; + // We're not using the the legacy cursor rects API to manage our + // cursor, but calling this function also invalidates AppKit's + // view of whether or not we need a cursorUpdate callback for + // our tracking area. [m_view.window invalidateCursorRectsForView:m_view]; - if (QOperatingSystemVersion::current() <= QOperatingSystemVersion::MacOSMonterey) { - // There's a bug in AppKit where calling invalidateCursorRectsForView when - // there's an override cursor active (for example when hovering over the - // window frame), will not result in a cursorUpdate: callback. To work around - // this we synthesize a cursor update event and call the callback ourselves. - // We only do this is if the window would normally receive cursor updates. - auto locationInWindow = m_view.window.mouseLocationOutsideOfEventStream; - auto locationInSuperview = [m_view.superview convertPoint:locationInWindow fromView:nil]; - bool mouseIsOverView = [m_view hitTest:locationInSuperview] == m_view; - auto utilityMask = NSWindowStyleMaskUtilityWindow | NSWindowStyleMaskTitled; - bool isUtilityWindow = (m_view.window.styleMask & utilityMask) == utilityMask; - if (mouseIsOverView && (m_view.window.keyWindow || isUtilityWindow)) { - qCDebug(lcQpaMouse) << "Synthesizing cursor update"; - [m_view cursorUpdate:[NSEvent enterExitEventWithType:NSEventTypeCursorUpdate - location:locationInWindow modifierFlags:0 timestamp:0 - windowNumber:m_view.window.windowNumber context:nil - eventNumber:0 trackingNumber:0 userData:0]]; - } + // We've informed AppKit that we need a cursorUpdate, but cursor + // updates for tracking areas are deferred in some cases, such as + // when the mouse is down, whereas we want a synchronous update. + // To ensure an updated cursor we synthesize a cursor update event + // now if the window is otherwise allowed to change the cursor. + auto locationInWindow = m_view.window.mouseLocationOutsideOfEventStream; + auto locationInSuperview = [m_view.superview convertPoint:locationInWindow fromView:nil]; + bool mouseIsOverView = [m_view hitTest:locationInSuperview] == m_view; + auto utilityMask = NSWindowStyleMaskUtilityWindow | NSWindowStyleMaskTitled; + bool isUtilityWindow = (m_view.window.styleMask & utilityMask) == utilityMask; + if (mouseIsOverView && (m_view.window.keyWindow || isUtilityWindow)) { + qCDebug(lcQpaMouse) << "Synthesizing cursor update"; + [m_view cursorUpdate:[NSEvent enterExitEventWithType:NSEventTypeCursorUpdate + location:locationInWindow modifierFlags:0 timestamp:0 + windowNumber:m_view.window.windowNumber context:nil + eventNumber:0 trackingNumber:0 userData:0]]; } }