diff --git a/src/qcocoa-qpa/README.ALT b/src/qcocoa-qpa/README.ALT index be344f0..0cb890e 100644 --- a/src/qcocoa-qpa/README.ALT +++ b/src/qcocoa-qpa/README.ALT @@ -1,387 +1,395 @@ +20180110 : upstream head (5.9): 1df2906c75f379a62bce487d3d2db3ade3095b16 + src/plugins/platforms/cocoa/qnsview.mm | 7 +- +patch: 30-sync-upstream-180110.diff +Changes: git log b0938cb6c1fa29ec2d2a4fb9273e515cd0d6c08e..1df2906c75f379a62bce487d3d2db3ade3095b16 src/plugins/platforms/cocoa +commit 4f76c2dbadb09a27cecaba5a36512f68fac3d319 + Cocoa: make "Send correct mouse buttons for tablets" optional + + 20171231 Improved control over selecting the FreeType fontengine and equivalent options to select the use of a FontConfig fontdatabase with FreeType fontengine. 20171219 : upstream head (5.9): b0938cb6c1fa29ec2d2a4fb9273e515cd0d6c08e src/plugins/platforms/cocoa/qcocoamenuloader.mm | 8 +- patch: patches/26-sync-upstream-171219.diff Changes: git log 6a9d076e87f0c8aa4fb49bbcc2f56eefd85af2e3..b0938cb6c1fa29ec2d2a4fb9273e515cd0d6c08e src/plugins/platforms/cocoa commit 29104c85db53e7c0c0aaf3fe78f84b737fce4886 Cocoa: Disable “Hide” menu item on open popups 20171126 : upstream head (5.9): 6a9d076e87f0c8aa4fb49bbcc2f56eefd85af2e3 / v5.9.3-43-g6a9d076 src/plugins/platforms/cocoa/qcocoamenu.h | 2 + src/plugins/platforms/cocoa/qcocoamenu.mm | 16 ++- src/plugins/platforms/cocoa/qcocoamenubar.h | 2 + src/plugins/platforms/cocoa/qcocoamenubar.mm | 11 +- src/plugins/platforms/cocoa/qcocoamenuitem.mm | 4 - src/plugins/platforms/cocoa/qcocoawindow.mm | 15 +++ src/plugins/platforms/cocoa/qnsview.mm | 1 + patch: patches/25-sync-upstream-171126.diff Changes: git log 01c7b474f5ad2c9fcf4b90c71048624070811618..6a9d076e87f0c8aa4fb49bbcc2f56eefd85af2e3 src/plugins/platforms/cocoa commit d0736e9d17d0b3ec2f7aa3f323a1edf70aa83e27 Cocoa: Make High DPI drag cursor work commit 3f519ffa150ce5a2d9e3ad3f147745b312d29afb QCocoaMenuItem: Don't clear NSMenuItem.action when setting submenu commit 5194817941985c766bbc7f80039a58e0cf504b55 Cocoa: optimize backingstore flush on 10-bit displays commit 385589ef458715fcaa533bbd01ca421dc1040eba QCocoaMenu: Attach menu items when updating the menubar 20171030 : upstream head (5.9): 01c7b474f5ad2c9fcf4b90c71048624070811618 src/plugins/platforms/cocoa/cocoa.pro | 11 +- src/plugins/platforms/cocoa/messages.cpp | 2 +- src/plugins/platforms/cocoa/messages.h | 11 + src/plugins/platforms/cocoa/qcocoamenuitem.mm | 10 +- src/plugins/platforms/cocoa/qcocoamenuloader.mm | 20 +- src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm | 31 --- src/plugins/platforms/cocoa/qcocoawindow.mm | 2 + patches: patches/24-sync-upstream-171030.diff patches/24b-patch-build-on-OSX109.diff Changes: git log e03b64c5b1eeebfbbb94d67eb9a9c1d35eaba0bb..01c7b474f5ad2c9fcf4b90c71048624070811618 commit 37a1478787d64b34a0716421c8a47f3246e41bfd Cocoa QPA: Code clean up, make some bits more readable commit c99d8532c892f72f897c9e686be75d1ebba67618 QCocoaSystemTrayIcon: Remove unused classes Both QNSMenu and QSystemTrayIconQMenu aren't referenced anywhere else, including within qcocoasystemtrayicon.mm, since the QPA backend was added. commit f13e75345d035ec906846aaa3540454787edbd3f Cocoa QPA: Remove usage of OBJECTIVE_SOURCES commit 8ac9addd946637401e4685c6e91d1a3cd5b2d768 QCocoaWindow: Toggle titlebar transparency to support unified toolbar This is need from macOS 10.13 onwards. See NSWindow related notes on https://developer.apple.com/library/content/releasenotes/AppKit/RN-AppKit/ NB: that NSWindow change required runtime checking for 10.9 support. 20171011 : upstream head (5.9): e03b64c5b1eeebfbbb94d67eb9a9c1d35eaba0bb src/plugins/platforms/cocoa/qnsview.mm | 4 + patch: patches/23-sync-upstream-171011.diff Changes: git log e0c2d328b11ab893538393fad66ad61d22d823c6..e03b64c5b1eeebfbbb94d67eb9a9c1d35eaba0bb src/plugins/platforms/cocoa/qnsview.mm | cat commit cbbf843e96de3067e4cb7c0a7b4e59a6c27b10f7 macOS: Bail out early when handling shortcut event results in closing window NB: this protection has been added to the contextmenu handling block just above, too. 20170920 : upstream head (5.9): e0c2d328b11ab893538393fad66ad61d22d823c6 src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm | 12 +- src/plugins/platforms/cocoa/qcocoainputcontext.mm | 17 +- src/plugins/platforms/cocoa/qcocoamenubar.mm | 10 +- src/plugins/platforms/cocoa/qnsview.h | 2 + src/plugins/platforms/cocoa/qnsview.mm | 41 ++- patch: patches/22-sync-upstream-170920.diff Changes: git log e81f430e30635f975dd4635ffb64d66fc1bce355..HEAD src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm src/plugins/platforms/cocoa/qcocoainputcontext.mm src/plugins/platforms/cocoa/qcocoamenubar.mm src/plugins/platforms/cocoa/qnsview.h src/plugins/platforms/cocoa/qnsview.mm commit 689606de91faecf91f1f92e8d355789d9be62d2f Cocoa: Update the known menu items when the QCocoaMenuBar is deleted Task-number: QTBUG-62193 commit 202d3ba3e6c9982608f41f5e7d836825c8664c93 Cocoa: Check if charactersIgnoringModifiers is not empty too Task-number: QTBUG-57933 commit 0adc14d8dbdd9e28ccb72c49d865009dd8df1b1c macOS: Don‚Äôt color convert the backing store Task-number: QTBUG-61384 commit 52bda430af2749da1a0467b71d9cca5208f22402 macOS: Reset composition when focus object changes inside window Task-number: QTBUG-59222 commit 0270651dda8e247164a8dccd71fb65712c7d1480 Cocoa integration: do not use released session Task-number: QTBUG-62589 20170829 : upstream head (5.9): e81f430e30635f975dd4635ffb64d66fc1bce355 touched: src/plugins/platforms/cocoa/qcocoamenu.h | 4 + src/plugins/platforms/cocoa/qcocoamenu.mm | 26 +- src/plugins/platforms/cocoa/qcocoamenubar.mm | 2 +- src/plugins/platforms/cocoa/qcocoaprintersupport.h | 2 +- src/plugins/platforms/cocoa/qcocoaprintersupport.mm | 4 +- src/plugins/platforms/cocoa/qcocoawindow.mm | 14 +- src/plugins/platforms/cocoa/qnsview.h | 2 + src/plugins/platforms/cocoa/qnsview.mm | 4 + src/plugins/platforms/cocoa/qprintengine_mac.mm | 8 +- src/plugins/platforms/cocoa/qprintengine_mac_p.h | 2 +- patch: patches/21-sync-upstream-170828.diff Changes: git log 0a5f71c6062d575602ff041fb1b88ec2d8ad92bc..HEAD src/plugins/platforms/cocoa commit 48784486a36f60dea882baabc6923f4b59d2bfe6 QCocoaMenu: Stop update timer commit dbaa4de28e5cdfa1787af77d236586833786ee61 Cocoa: Fix compile when using QT_NO_TABLETEVENT commit 8ebfe00f4ab79516a8276929a682c24f4c675b5f Initialize the print engine with the given printer name QAltCocoa: only on Qt >= 5.9.0 Task-number: QTBUG-62221 commit 306071e50eac8290d234caab90985ddf705a5fc6 QCocoaMenu: Sync menubar menu when adding items Task-number: QTBUG-62260 commit f27d1ccbb24ec2fd4098f2976503478831006cc8 QCocoaMenu: De-pessimize the number of calls to validateMenuItem: Task-number: QTBUG-62396 >>> change enabled for Qt >= 5.9.2 commit c35fc435950437d3d046b17d06593873d7b82011 macOS: Make alpha-based click-trough work again Restore 5.6 behavior by not modifying ignoresMouseEvent if we can. Toggling WindowTransparentForInput on and off again is still broken. Task-number: QTBUG-54830 20180801: upstream head (5.9): 0a5f71c6062d575602ff041fb1b88ec2d8ad92bc touched: src/plugins/platforms/cocoa/cocoa.pro | 7 +- src/plugins/platforms/cocoa/qcocoafontdialoghelper.h | 3 + src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm | 4 - src/plugins/platforms/cocoa/qcocoahelpers.h | 7 +- src/plugins/platforms/cocoa/qcocoahelpers.mm | 5 ++ src/plugins/platforms/cocoa/qcocoatheme.mm | 13 +-- src/plugins/platforms/cocoa/qnswindowdelegate.mm | 7 +- > patch: patches/20-sync-upstream-170801.diff Changes: Fix 32-bit build on macOS macOS: Don't assume the proposed fullscreen size matches the screen size Sometimes AppKit will pass in a proposed size that's smaller than the geometry of the screen. We don't know why, but shouldn't assert. Add missing #include for -no-widgets macOS: Fix unused variable in window:willUseFullScreenContentSize: Convert features.fontdialog to QT_[REQUIRE_]CONFIG Fix macOS build for -no-widgets, take 2 20170707: upstream head (5.9): 9d3cd2268ce3beafcf6fa886bb70d8463260d602 touched: src/plugins/platforms/cocoa/qcocoatheme.mm src/plugins/platforms/cocoa/qnswindowdelegate.mm > patch: patches/19.2-sync-upstream-170707.diff Changes: from git log 03b4838cb51513bd5d2edf76dccc4bc4a1181681..HEAD src/plugins/platforms/cocoa/{qcocoatheme.mm,qnswindowdelegate.mm} commit 9d3cd2268ce3beafcf6fa886bb70d8463260d602 Author: Tor Arne Vestb#c3#b8 Date: Thu Jul 6 17:03:41 2017 +0200 macOS: Account for fullscreen geometry bug in AppKit on OS X 10.10 commit 6a1046e17691c6e35c7384590ba241edb4082707 Author: Stephan Binner qq Date: Tue Jul 4 19:08:55 2017 +0200 Fix macOS build for -no-widgets 20170705: upstream head (5.9 branch): 03b4838cb51513bd5d2edf76dccc4bc4a1181681 touched: src/plugins/platforms/cocoa/cocoa.pro | 16 +++-- src/plugins/platforms/cocoa/qcocoacolordialoghelper.h | 4 ++ src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm | 4 -- src/plugins/platforms/cocoa/qcocoafiledialoghelper.h | 3 + src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm | 4 -- src/plugins/platforms/cocoa/qcocoaintegration.mm | 8 ++- src/plugins/platforms/cocoa/qcocoamenubar.mm | 18 ++++- src/plugins/platforms/cocoa/qcocoatheme.mm | 14 ++-- src/plugins/platforms/cocoa/qnsview.mm | 33 +++++---- > patch: patches/19-sync-upstream-170705.diff Main changes from `cd src/plugins/platforms/cocoa && git log b2cb83ecbb1eec29268852d1b230f37e4c8592e9..HEAD cocoa.pro qcocoacolordialoghelper.h qcocoacolordialoghelper.mm qcocoafiledialoghelper.h qcocoafiledialoghelper.mm qcocoaintegration.mm qcocoamenubar.mm qcocoatheme.mm qnsview.mm ` commit 3e8ebea95b634c7ded9ee0b884768155e9e7f686 Author: Andy Shaw Date: Fri Jun 23 09:08:19 2017 +0200 Cocoa: Reset the target + action for a menuitem after showing a dialog To make it more reliable and efficient we now do the reverse of what we are doing when redirecting the items. This will ensure that the actions are correctly reset to the original target and action. The original approach of updateMenubarImmediately() was not always doing this and it also does other unnecessary things as a result when all we need is to just reset the things we changed. commit 198b67d14bf3ca76d6bdf8901348da0838cf3b8d Author: Tor Arne Vestb#c3#b8 Date: Mon Jun 12 17:58:41 2017 +0200 macOS: Send text input and key events to focus object of window, not app The key events and input method callbacks coming from Cocoa are targeted at our specific NSView, so we should deliver them to the focus object of the corresponding QWindow, not the global application focus object. This means that we'll deliver key events to windows also when they are not key (active), but this is intentional, as we would otherwise fail to deliver input method events coming from e.g. the emoji/symbol picker, which steals the key window when active. Task-number: QTBUG-61359 commit 3851a8ff20c6aed0807bfdc4588ae108a2b108ec Author: Tor Arne Vestb#c3#b8 Date: Fri Jun 23 13:32:25 2017 +0200 macOS: Work around buildup of NSDisplayCycle objects during rapid painting Task-number: QTBUG-60012 commit e6ccc8e56fdaf402602130176064ffb1a0ce29bf Author: R.J.V. Bertin Date: Wed Jun 21 18:16:04 2017 +0200 sync with upstream Cocoa QPA changes in the 5.9 branch (qtbase v5.9.0-208-gb2cb83e) commit 12e9ff4bed9513fe8ed390a1f421d8f73c45960a Author: R.J.V. Bertin Date: Mon Jun 5 17:40:51 2017 +0200 sync with upstream Cocoa QPA changes in the 5.9 branch (qtbase v5.9.0-119-g934235e) commit 0ecceaf6d7871b fc42141909af210f6bb408ac61 Author: R.J.V. Bertin Date: Thu Apr 20 20:50:58 2017 +0200 15-patch-restore-legacy-fullscreen-mode.diff alternative fullscreen mode: simplified and restore window icons commit d4ce302e7c08dcf38a0f0f40b7e9183cab2fc2b1 Author: R.J.V. Bertin Date: Wed Apr 19 23:58:24 2017 +0200 AltCocoa QPA: add a legacy fullscreen mode When windows don't have the fullscreen titlebar button or the env. var. QT_LEGACY_FULLSCREEN is set, toggleFullScreen() adopts a legacy mode. Windows are then made fullscreen by converting them to frame-less and resizing them to the current screen size. This is faster (instantaneous) and preserves display content on other screens in multi-head set-ups. commit 85bb84969c6922667ff83e1483a7ef71628c046e Author: R.J.V. Bertin Date: Tue Apr 11 20:16:12 2017 +0200 qmacstyle : really build against Qt 5.8.0 commit 934afde2c352b401cfffed83cb0877462dc27787 Author: R.J.V. Bertin Date: Mon Apr 10 23:56:13 2017 +0200 modified (Alt) 5.9.0 commit c8dd86fd23c6787f949c2849ccb20eaf22e118ae Author: R.J.V. Bertin Date: Mon Apr 10 23:45:31 2017 +0200 13-themehint-add-aqua.diff commit cbf229875c49c2a57d7b62b641d866134e569a1a Author: R.J.V. Bertin Date: Thu Apr 6 21:57:00 2017 +0200 12-deactivate-menurole-heuristics.patch this patch deactivates the worst of the TextHeuristic menu role guessmatics that can lead to putting the wrong QAction under the Mac's About or Preferences menu items in the Application menu. Many KDE applications have more than 1 About and Configure menu actions and are thus especially at risk. KDE applications that use KStandardActions to create the standard menu items will see the appropriate actions assigned to the About and Preferences menu items because their roles are set explicitly. commit af7c4544d1883e9978609742b48dbb152e880c34 Author: R.J.V. Bertin Date: Thu Apr 6 21:50:46 2017 +0200 11-patch-keyboard-support-menukey.diff this patch introduces support for a Menu key. Macs don't normally have one but the SDKs do provide a special keysym (NSMenuFunctionKey) which might be used (generated) by 3rd party keyboard drivers or even a native event filter. The patch completes the pre-existing support and generates a ContextMenu event at an appropriate location when the keysym is received. commit c45430fc3c1827b44dd9002932b7b582da19729e Author: R.J.V. Bertin Date: Thu Apr 6 19:22:14 2017 +0200 10-patch-keyboard-mapping.diff From 3ee093b47109e2b24ab77ccdecde84437c5aced9 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Wed, 2 Nov 2016 14:19:26 +0100 Subject: [PATCH] WIP - Cocoa integration: fix incorrect keyboard mapping When switching between different input sources, we have to update layouts. Task-number: QTBUG-50865 Change-Id: I19ee45feabf014e61dfef7454b5468e596ce2786 Committed from host : Portia.local commit 65de5666d5574a08a2fe49b82f29fd0b8a6b3cbf Author: R.J.V. Bertin Date: Thu Apr 6 19:21:36 2017 +0200 09-patch-silence-setscreen-warning.diff Prevent an unnecessary warning commit 07527d713953a417b93393558cfb44475bc42d23 Author: R.J.V. Bertin Date: Thu Apr 6 19:20:56 2017 +0200 08-patch-freetype-gamma-cocoa.diff Adapt the font smoothing gamma when using the FreeType rendering engine. commit a7b0481d07c592c1a1810160e317d6792eeb7a5d Author: R.J.V. Bertin Date: Thu Apr 6 19:19:32 2017 +0200 06-patch-respect-DontSwapCtrlMeta.diff From ce953f011356257c1e1862d0353f785453559028 Mon Sep 17 00:00:00 2001 From: Dyami Caliri Date: Tue, 16 Sep 2014 17:32:22 -0700 Subject: [PATCH] OS X: Handle Qt::AA_MacDontSwapCtrlAndMeta for regular key events The flag Qt::AA_MacDontSwapCtrlAndMeta was only being used with shortcut detection. Now trying to incorporate flag in normal key events. Task-number: QTBUG-22775 Change-Id: I9edf97d0de93aa225435c9802372e33641cdac73 Committed from host : Portia.local commit 5ffc2fff1bd729ec9e54120e424fdfd51197f438 Author: R.J.V. Bertin Date: Thu Apr 6 19:18:46 2017 +0200 05-patch-improve-fontweight-support9.diff part of a more comprehensive patch improving support for less-common font weights. I'm not sure how much effect it has on its own but it should not have adverse effects when used without the companion modifications. commit 658f1d0e2fe5a9a678bdae712cc5a47878bc59de Author: R.J.V. Bertin Date: Thu Apr 6 19:16:29 2017 +0200 04-patch-qmenuAddSection.diff add support for named menu sections ("texted separators") in menus that are part of the native menubar. This requires the companion patch to the "native" Macintosh theme otherwise named sections won't appear when they are the 1st item in a menu. commit 42f58e14282bf4beae408f5440397dd5a6b30471 Author: R.J.V. Bertin Date: Thu Apr 6 19:14:00 2017 +0200 03-patch-better-menuitem-insert-warning.diff Provide a more helpful warning when adding an item to an additional menu or removing it from the wrong menu. commit f5bbcfc20e52dfcfa27eedb4a1240e2fccf5fb93 Author: R.J.V. Bertin Date: Thu Apr 6 19:12:01 2017 +0200 02-patch-enable-kde-theme.diff advertise the existence of support for the KDE platform theme plugin when KDE_SESSION_VERSION is set to at least 4 in the environment. commit f3707f1516f1393235c178d5829cf3f88d5844e1 Author: R.J.V. Bertin Date: Thu Apr 6 19:03:23 2017 +0200 01-patch-missing-autoreleasepools.diff Add autorelease pools to functions that (used to) need them. This is safe and comes at almost no cost. diff --git a/src/qcocoa-qpa/patches/30-sync-upstream-180110.diff b/src/qcocoa-qpa/patches/30-sync-upstream-180110.diff new file mode 100644 index 0000000..00d3b7a --- /dev/null +++ b/src/qcocoa-qpa/patches/30-sync-upstream-180110.diff @@ -0,0 +1,31 @@ +diff --git a/cocoa/qnsview.mm b/cocoa/qnsview.mm +index 4c53127..795c3ea 100644 +--- a/cocoa/qnsview.mm ++++ b/cocoa/qnsview.mm +@@ -1068,6 +1068,8 @@ Q_GLOBAL_STATIC(QCocoaTabletDeviceDataHash, tabletDeviceDataHash) + + - (bool)handleTabletEvent: (NSEvent *)theEvent + { ++ static bool ignoreButtonMapping = qEnvironmentVariableIsSet("QT_MAC_TABLET_IGNORE_BUTTON_MAPPING"); ++ + if (!m_platformWindow) + return false; + +@@ -1116,14 +1118,15 @@ Q_GLOBAL_STATIC(QCocoaTabletDeviceDataHash, tabletDeviceDataHash) + rotation -= 360.0; + + Qt::KeyboardModifiers keyboardModifiers = [QNSView convertKeyModifiers:[theEvent modifierFlags]]; ++ Qt::MouseButtons buttons = ignoreButtonMapping ? static_cast(static_cast([theEvent buttonMask])) : m_buttons; + + qCDebug(lcQpaTablet, "event on tablet %d with tool %d type %d unique ID %lld pos %6.1f, %6.1f root pos %6.1f, %6.1f buttons 0x%x pressure %4.2lf tilt %d, %d rotation %6.2lf", + deviceId, deviceData.device, deviceData.pointerType, deviceData.uid, + windowPoint.x(), windowPoint.y(), screenPoint.x(), screenPoint.y(), +- static_cast(m_buttons), pressure, xTilt, yTilt, rotation); ++ static_cast(buttons), pressure, xTilt, yTilt, rotation); + + QWindowSystemInterface::handleTabletEvent(m_platformWindow->window(), timestamp, windowPoint, screenPoint, +- deviceData.device, deviceData.pointerType, m_buttons, pressure, xTilt, yTilt, ++ deviceData.device, deviceData.pointerType, buttons, pressure, xTilt, yTilt, + tangentialPressure, rotation, z, deviceData.uid, + keyboardModifiers); + return true; diff --git a/src/qcocoa-qpa/qnsview.mm b/src/qcocoa-qpa/qnsview.mm index 4c53127..795c3ea 100644 --- a/src/qcocoa-qpa/qnsview.mm +++ b/src/qcocoa-qpa/qnsview.mm @@ -1,2252 +1,2255 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include "qnsview.h" #include "qcocoawindow.h" #include "qcocoahelpers.h" #include "qmultitouch_mac_p.h" #include "qcocoadrag.h" #include "qcocoainputcontext.h" #include #include #include #include #include #include #include #include "qcocoabackingstore.h" #ifndef QT_NO_OPENGL #include "qcocoaglcontext.h" #endif #include "qcocoaintegration.h" #ifdef QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR #include #endif #if QT_VERSION < QT_VERSION_CHECK(5, 9, 0) #include #endif Q_LOGGING_CATEGORY(lcQpaTouch, "qt.qpa.input.touch") #ifndef QT_NO_GESTURES Q_LOGGING_CATEGORY(lcQpaGestures, "qt.qpa.input.gestures") #endif Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") static QTouchDevice *touchDevice = 0; static bool _q_dontOverrideCtrlLMB = false; @interface NSEvent (Qt_Compile_Leopard_DeviceDelta) - (CGFloat)deviceDeltaX; - (CGFloat)deviceDeltaY; - (CGFloat)deviceDeltaZ; @end @interface QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper) : NSObject { QNSView *view; } - (id)initWithView:(QNSView *)theView; - (void)mouseMoved:(NSEvent *)theEvent; - (void)mouseEntered:(NSEvent *)theEvent; - (void)mouseExited:(NSEvent *)theEvent; - (void)cursorUpdate:(NSEvent *)theEvent; @end @implementation QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper) - (id)initWithView:(QNSView *)theView { self = [super init]; if (self) { view = theView; } return self; } - (void)mouseMoved:(NSEvent *)theEvent { [view mouseMovedImpl:theEvent]; } - (void)mouseEntered:(NSEvent *)theEvent { [view mouseEnteredImpl:theEvent]; } - (void)mouseExited:(NSEvent *)theEvent { [view mouseExitedImpl:theEvent]; } - (void)cursorUpdate:(NSEvent *)theEvent { [self cursorUpdate:theEvent]; } @end // Private interface @interface QT_MANGLE_NAMESPACE(QNSView) () - (BOOL)isTransparentForUserInput; @end @implementation QT_MANGLE_NAMESPACE(QNSView) + (void)initialize { _q_dontOverrideCtrlLMB = qt_mac_resolveOption(false, "QT_MAC_DONT_OVERRIDE_CTRL_LMB"); } - (id) init { if (self = [super initWithFrame:NSZeroRect]) { m_backingStore = 0; m_maskImage = 0; m_shouldInvalidateWindowShadow = false; m_buttons = Qt::NoButton; m_acceptedMouseDowns = Qt::NoButton; m_frameStrutButtons = Qt::NoButton; m_sendKeyEvent = false; #ifndef QT_NO_OPENGL m_glContext = 0; m_shouldSetGLContextinDrawRect = false; #endif currentCustomDragTypes = 0; m_sendUpAsRightButton = false; m_inputSource = 0; 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; touchDevice->setType(QTouchDevice::TouchPad); touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::NormalizedPosition | QTouchDevice::MouseEmulation); QWindowSystemInterface::registerTouchDevice(touchDevice); } m_isMenuView = false; self.focusRingType = NSFocusRingTypeNone; } return self; } - (void)dealloc { CGImageRelease(m_maskImage); if (m_trackingArea) { [self removeTrackingArea:m_trackingArea]; [m_trackingArea release]; } m_maskImage = 0; [m_inputSource release]; [[NSNotificationCenter defaultCenter] removeObserver:self]; [m_mouseMoveHelper release]; delete currentCustomDragTypes; [super dealloc]; } - (id)initWithCocoaWindow:(QCocoaWindow *)platformWindow { self = [self init]; if (!self) return 0; m_platformWindow = platformWindow; m_sendKeyEvent = false; m_trackingArea = nil; #ifdef QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR // prevent rift in space-time continuum, disable // accessibility for the accessibility inspector's windows. static bool skipAccessibilityForInspectorWindows = false; if (!skipAccessibilityForInspectorWindows) { // m_accessibleRoot = window->accessibleRoot(); AccessibilityInspector *inspector = new AccessibilityInspector(window); skipAccessibilityForInspectorWindows = true; inspector->inspectWindow(window); skipAccessibilityForInspectorWindows = false; } #endif [self registerDragTypes]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textInputContextKeyboardSelectionDidChangeNotification:) name:NSTextInputContextKeyboardSelectionDidChangeNotification object:nil]; return self; } #ifndef QT_NO_OPENGL - (void) setQCocoaGLContext:(QCocoaGLContext *)context { m_glContext = context; [m_glContext->nsOpenGLContext() setView:self]; if (![m_glContext->nsOpenGLContext() view]) { //was unable to set view m_shouldSetGLContextinDrawRect = true; } } #endif - (void)viewDidMoveToSuperview { if (!m_platformWindow) return; if (!(m_platformWindow->m_viewIsToBeEmbedded)) return; if ([self superview]) { m_platformWindow->m_viewIsEmbedded = true; QWindowSystemInterface::handleGeometryChange(m_platformWindow->window(), m_platformWindow->geometry()); m_platformWindow->updateExposedGeometry(); QWindowSystemInterface::flushWindowSystemEvents(); } else { m_platformWindow->m_viewIsEmbedded = false; } } - (void)viewDidMoveToWindow { m_backingStore = Q_NULLPTR; } - (QWindow *)topLevelWindow { if (!m_platformWindow) return nullptr; QWindow *focusWindow = m_platformWindow->window(); // For widgets we need to do a bit of trickery as the window // to activate is the window of the top-level widget. if (qstrcmp(focusWindow->metaObject()->className(), "QWidgetWindow") == 0) { while (focusWindow->parent()) { focusWindow = focusWindow->parent(); } } return focusWindow; } - (void)updateGeometry { if (!m_platformWindow) return; QRect geometry; if (self.window.parentWindow) { return; #if 0 //geometry = QRectF::fromCGRect([self frame]).toRect(); qDebug() << "nsview updateGeometry" << m_platformWindow->window(); QRect screenRect = QRectF::fromCGRect([m_platformWindow->m_nsWindow convertRectToScreen:[self frame]]).toRect(); qDebug() << "screenRect" << screenRect; screenRect.moveTop(qt_mac_flipYCoordinate(screenRect.y() + screenRect.height())); geometry = QRect(m_platformWindow->window()->parent()->mapFromGlobal(screenRect.topLeft()), screenRect.size()); qDebug() << "geometry" << geometry; #endif //geometry = QRect(screenRect.origin.x, qt_mac_flipYCoordinate(screenRect.origin.y + screenRect.size.height), screenRect.size.width, screenRect.size.height); } else if (m_platformWindow->m_nsWindow) { // top level window, get window rect and flip y. NSRect rect = [self frame]; NSRect windowRect = [[self window] frame]; geometry = QRect(windowRect.origin.x, qt_mac_flipYCoordinate(windowRect.origin.y + rect.size.height), rect.size.width, rect.size.height); } else if (m_platformWindow->m_viewIsToBeEmbedded) { // embedded child window, use the frame rect ### merge with case below geometry = QRectF::fromCGRect(NSRectToCGRect([self bounds])).toRect(); } else { // child window, use the frame rect geometry = QRectF::fromCGRect(NSRectToCGRect([self frame])).toRect(); } if (m_platformWindow->m_nsWindow && geometry == m_platformWindow->geometry()) return; const bool isResize = geometry.size() != m_platformWindow->geometry().size(); // 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; qCDebug(lcQpaCocoaWindow) << "[QNSView udpateGeometry:]" << m_platformWindow->window() << "current" << m_platformWindow->geometry() << "new" << geometry; // Call setGeometry on QPlatformWindow. (not on QCocoaWindow, // doing that will initiate a geometry change it and possibly create // an infinite loop when this notification is triggered again.) m_platformWindow->QPlatformWindow::setGeometry(geometry); // Don't send the geometry change if the QWindow is designated to be // embedded in a foreign view hiearchy but has not actually been // embedded yet - it's too early. if (m_platformWindow->m_viewIsToBeEmbedded && !m_platformWindow->m_viewIsEmbedded) return; // Send a geometry change event to Qt, if it's ready to handle events if (!m_platformWindow->m_inConstructor) { QWindowSystemInterface::handleGeometryChange(m_platformWindow->window(), geometry); 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(); else if (isResize) m_backingStore = 0; } } - (void)textInputContextKeyboardSelectionDidChangeNotification : (NSNotification *) textInputContextKeyboardSelectionDidChangeNotification { Q_UNUSED(textInputContextKeyboardSelectionDidChangeNotification) if (([NSApp keyWindow] == [self window]) && [[self window] firstResponder] == self) { QCocoaInputContext *ic = qobject_cast(QCocoaIntegration::instance()->inputContext()); ic->updateLocale(); } } - (void)viewDidHide { m_platformWindow->obscureWindow(); } - (void)viewDidUnhide { m_platformWindow->exposeWindow(); } - (void)removeFromSuperview { QMacAutoReleasePool pool; [super removeFromSuperview]; } - (void) flushBackingStore:(QCocoaBackingStore *)backingStore region:(const QRegion &)region offset:(QPoint)offset { qCDebug(lcQpaCocoaWindow) << "[QNSView flushBackingStore:]" << m_platformWindow->window() << region.rectCount() << region.boundingRect() << offset; m_backingStore = backingStore; m_backingStoreOffset = offset * m_backingStore->paintDevice()->devicePixelRatio(); // Prevent buildup of NSDisplayCycle objects during setNeedsDisplayInRect, which // would normally be released as part of the root runloop's autorelease pool, but // can be kept alive during repeated painting which starve the root runloop. // FIXME: Move this to the event dispatcher, to cover more cases of starvation. // FIXME: Figure out if there's a way to detect and/or prevent runloop starvation. QMacAutoReleasePool pool; for (const QRect &rect : region) [self setNeedsDisplayInRect:NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height())]; } - (void)clearBackingStore:(QCocoaBackingStore *)backingStore { if (backingStore == m_backingStore) m_backingStore = 0; } - (BOOL) hasMask { return !m_maskRegion.isEmpty(); } - (BOOL) isOpaque { if (!m_platformWindow) return true; return m_platformWindow->isOpaque(); } - (void) setMaskRegion:(const QRegion *)region { m_shouldInvalidateWindowShadow = true; m_maskRegion = *region; if (m_maskImage) CGImageRelease(m_maskImage); if (region->isEmpty()) { m_maskImage = 0; return; } const QRect &rect = region->boundingRect(); QImage tmp(rect.size(), QImage::Format_RGB32); tmp.fill(Qt::white); QPainter p(&tmp); p.setClipRegion(*region); p.fillRect(rect, Qt::black); p.end(); QImage maskImage = QImage(rect.size(), QImage::Format_Indexed8); for (int y=0; ym_nsWindow) { [m_platformWindow->m_nsWindow invalidateShadow]; m_shouldInvalidateWindowShadow = false; } } - (void)drawRect:(NSRect)dirtyRect { if (!m_platformWindow) return; qCDebug(lcQpaCocoaWindow) << "[QNSView drawRect:]" << m_platformWindow->window() << QRectF::fromCGRect(NSRectToCGRect(dirtyRect)); #ifndef QT_NO_OPENGL if (m_glContext && m_shouldSetGLContextinDrawRect) { [m_glContext->nsOpenGLContext() setView:self]; m_shouldSetGLContextinDrawRect = false; } #endif if (m_platformWindow->m_drawContentBorderGradient) NSDrawWindowBackground(dirtyRect); if (m_backingStore) [self drawBackingStoreUsingCoreGraphics:dirtyRect]; [self invalidateWindowShadowIfNeeded]; } // Draws the backing store content to the QNSView using Core Graphics. // This function assumes that the QNSView is in a configuration that // supports Core Graphics, such as "classic" mode or layer mode with // the default layer. - (void)drawBackingStoreUsingCoreGraphics:(NSRect)dirtyRect { if (!m_backingStore) return; // Calculate source and target rects. The target rect is the dirtyRect: CGRect dirtyWindowRect = NSRectToCGRect(dirtyRect); // The backing store source rect will be larger on retina displays. // Scale dirtyRect by the device pixel ratio: const qreal devicePixelRatio = m_backingStore->paintDevice()->devicePixelRatio(); CGRect dirtyBackingRect = CGRectMake(dirtyRect.origin.x * devicePixelRatio, dirtyRect.origin.y * devicePixelRatio, dirtyRect.size.width * devicePixelRatio, dirtyRect.size.height * devicePixelRatio); NSGraphicsContext *nsGraphicsContext = [NSGraphicsContext currentContext]; CGContextRef cgContext = (CGContextRef) [nsGraphicsContext graphicsPort]; // Translate coordiate system from CoreGraphics (bottom-left) to NSView (top-left): CGContextSaveGState(cgContext); int dy = dirtyWindowRect.origin.y + CGRectGetMaxY(dirtyWindowRect); CGContextTranslateCTM(cgContext, 0, dy); CGContextScaleCTM(cgContext, 1, -1); // If a mask is set, modify the sub image accordingly: CGImageRef subMask = 0; if (m_maskImage) { subMask = CGImageCreateWithImageInRect(m_maskImage, dirtyWindowRect); CGContextClipToMask(cgContext, dirtyWindowRect, subMask); } // Clip out and draw the correct sub image from the (shared) backingstore: CGRect backingStoreRect = CGRectMake( dirtyBackingRect.origin.x + m_backingStoreOffset.x(), dirtyBackingRect.origin.y + m_backingStoreOffset.y(), dirtyBackingRect.size.width, dirtyBackingRect.size.height ); CGImageRef bsCGImage = qt_mac_toCGImage(m_backingStore->toImage()); // Prevent potentially costly color conversion by assiging the display // color space to the backingstore image. CGImageRef displayColorSpaceImage = CGImageCreateCopyWithColorSpace(bsCGImage, self.window.screen.colorSpace.CGColorSpace); CGImageRef cleanImg = CGImageCreateWithImageInRect(displayColorSpaceImage, backingStoreRect); // Optimization: Copy frame buffer content instead of blending for // top-level windows where Qt fills the entire window content area. // (But don't overpaint the title-bar gradient) if (m_platformWindow->m_nsWindow && !m_platformWindow->m_drawContentBorderGradient) CGContextSetBlendMode(cgContext, kCGBlendModeCopy); CGContextDrawImage(cgContext, dirtyWindowRect, cleanImg); // Clean-up: CGContextRestoreGState(cgContext); CGImageRelease(cleanImg); CGImageRelease(subMask); CGImageRelease(bsCGImage); CGImageRelease(displayColorSpaceImage); } - (BOOL) isFlipped { return YES; } - (BOOL)isTransparentForUserInput { return m_platformWindow->window() && m_platformWindow->window()->flags() & Qt::WindowTransparentForInput; } - (BOOL)becomeFirstResponder { if (!m_platformWindow) return NO; if ([self isTransparentForUserInput]) return NO; if (!m_platformWindow->windowIsPopupType() && !m_isMenuView) QWindowSystemInterface::handleWindowActivated([self topLevelWindow]); return YES; } - (BOOL)acceptsFirstResponder { if (!m_platformWindow) return NO; if (m_isMenuView) return NO; if (m_platformWindow->shouldRefuseKeyWindowAndFirstResponder()) return NO; if ([self isTransparentForUserInput]) return NO; if ((m_platformWindow->window()->flags() & Qt::ToolTip) == Qt::ToolTip) return NO; return YES; } - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent { Q_UNUSED(theEvent) if (!m_platformWindow) return NO; if ([self isTransparentForUserInput]) return NO; return YES; } - (NSView *)hitTest:(NSPoint)aPoint { NSView *candidate = [super hitTest:aPoint]; if (candidate == self) { if ([self isTransparentForUserInput]) return nil; } return candidate; } - (void)convertFromScreen:(NSPoint)mouseLocation toWindowPoint:(QPointF *)qtWindowPoint andScreenPoint:(QPointF *)qtScreenPoint { // Calculate the mouse position in the QWindow and Qt screen coordinate system, // starting from coordinates in the NSWindow coordinate system. // // This involves translating according to the window location on screen, // as well as inverting the y coordinate due to the origin change. // // Coordinate system overview, outer to innermost: // // Name Origin // // OS X screen bottom-left // Qt screen top-left // NSWindow bottom-left // NSView/QWindow top-left // // NSView and QWindow are equal coordinate systems: the QWindow covers the // entire NSView, and we've set the NSView's isFlipped property to true. NSWindow *window = [self window]; NSPoint nsWindowPoint; 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 *qtScreenPoint = QPointF(mouseLocation.x, qt_mac_flipYCoordinate(mouseLocation.y)); // Qt screen coordinates } - (void)resetMouseButtons { m_buttons = Qt::NoButton; 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 { if (!m_platformWindow) return; #ifndef QT_NO_TABLETEVENT // Tablet events may come in via the mouse event handlers, // check if this is a valid tablet event first. if ([self handleTabletEvent: theEvent]) return; #endif QPointF qtWindowPoint; QPointF qtScreenPoint; QNSView *targetView = self; if (m_platformWindow && m_platformWindow->m_forwardWindow) { if (theEvent.type == NSLeftMouseDragged || theEvent.type == NSLeftMouseUp) targetView = qnsview_cast(m_platformWindow->m_forwardWindow->view()); else m_platformWindow->m_forwardWindow.clear(); } if (!targetView.platformWindow) return; // Popups implicitly grap mouse events; forward to the active popup if there is one if (QCocoaWindow *popup = QCocoaIntegration::instance()->activePopupWindow()) { // Tooltips must be transparent for mouse events // The bug reference is QTBUG-46379 if (!popup->m_windowFlags.testFlag(Qt::ToolTip)) { if (QNSView *popupView = qnsview_cast(popup->view())) targetView = popupView; } } [targetView convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint]; ulong timestamp = [theEvent timestamp] * 1000; QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); nativeDrag->setLastMouseEvent(theEvent, self); Qt::KeyboardModifiers keyboardModifiers = [QNSView convertKeyModifiers:[theEvent modifierFlags]]; QWindowSystemInterface::handleMouseEvent(targetView->m_platformWindow->window(), timestamp, qtWindowPoint, qtScreenPoint, m_buttons, keyboardModifiers, Qt::MouseEventNotSynthesized); } - (void)handleFrameStrutMouseEvent:(NSEvent *)theEvent { if (!m_platformWindow) return; // get m_buttons in sync // Don't send frme strut events if we are in the middle of a mouse drag. if (m_buttons != Qt::NoButton) return; NSEventType ty = [theEvent type]; switch (ty) { case NSLeftMouseDown: m_frameStrutButtons |= Qt::LeftButton; break; case NSLeftMouseUp: m_frameStrutButtons &= ~Qt::LeftButton; break; case NSRightMouseDown: m_frameStrutButtons |= Qt::RightButton; break; case NSLeftMouseDragged: m_frameStrutButtons |= Qt::LeftButton; break; case NSRightMouseDragged: m_frameStrutButtons |= Qt::RightButton; break; case NSRightMouseUp: m_frameStrutButtons &= ~Qt::RightButton; break; case NSOtherMouseDown: m_frameStrutButtons |= cocoaButton2QtButton([theEvent buttonNumber]); break; case NSOtherMouseUp: m_frameStrutButtons &= ~cocoaButton2QtButton([theEvent buttonNumber]); default: break; } NSWindow *window = [self window]; NSPoint windowPoint = [theEvent locationInWindow]; int windowScreenY = [window frame].origin.y + [window frame].size.height; NSPoint windowCoord = [self convertPoint:[self frame].origin toView:nil]; int viewScreenY = [window convertRectToScreen:NSMakeRect(windowCoord.x, windowCoord.y, 0, 0)].origin.y; int titleBarHeight = windowScreenY - viewScreenY; NSPoint nsViewPoint = [self convertPoint: windowPoint fromView: nil]; QPoint qtWindowPoint = QPoint(nsViewPoint.x, titleBarHeight + nsViewPoint.y); NSPoint screenPoint = [window convertRectToScreen:NSMakeRect(windowPoint.x, windowPoint.y, 0, 0)].origin; QPoint qtScreenPoint = QPoint(screenPoint.x, qt_mac_flipYCoordinate(screenPoint.y)); ulong timestamp = [theEvent timestamp] * 1000; QWindowSystemInterface::handleFrameStrutMouseEvent(m_platformWindow->window(), timestamp, qtWindowPoint, qtScreenPoint, m_frameStrutButtons); } - (bool)handleMouseDownEvent:(NSEvent *)theEvent withButton:(int)buttonNumber { if ([self isTransparentForUserInput]) return false; Qt::MouseButton button = cocoaButton2QtButton(buttonNumber); QPointF qtWindowPoint; QPointF qtScreenPoint; [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint]; Q_UNUSED(qtScreenPoint); // Maintain masked state for the button for use by MouseDragged and MouseUp. const bool masked = [self hasMask] && !m_maskRegion.contains(qtWindowPoint.toPoint()); if (masked) m_acceptedMouseDowns &= ~button; else m_acceptedMouseDowns |= button; // Forward masked out events to the next responder if (masked) return false; m_buttons |= button; [self handleMouseEvent:theEvent]; return true; } - (bool)handleMouseDraggedEvent:(NSEvent *)theEvent withButton:(int)buttonNumber { if ([self isTransparentForUserInput]) return false; Qt::MouseButton button = cocoaButton2QtButton(buttonNumber); // Forward the event to the next responder if Qt did not accept the // corresponding mouse down for this button if (!(m_acceptedMouseDowns & button) == button) return false; [self handleMouseEvent:theEvent]; return true; } - (bool)handleMouseUpEvent:(NSEvent *)theEvent withButton:(int)buttonNumber { if ([self isTransparentForUserInput]) return false; Qt::MouseButton button = cocoaButton2QtButton(buttonNumber); // Forward the event to the next responder if Qt did not accept the // corresponding mouse down for this button if (!(m_acceptedMouseDowns & button) == button) return false; if (m_sendUpAsRightButton && button == Qt::LeftButton) button = Qt::RightButton; if (button == Qt::RightButton) m_sendUpAsRightButton = false; m_buttons &= ~button; [self handleMouseEvent:theEvent]; return true; } - (void)mouseDown:(NSEvent *)theEvent { if ([self isTransparentForUserInput]) return [super mouseDown:theEvent]; m_sendUpAsRightButton = false; // Handle any active poup windows; clicking outisde them should close them // all. Don't do anything or clicks inside one of the menus, let Cocoa // handle that case. Note that in practice many windows of the Qt::Popup type // will actually close themselves in this case using logic implemented in // that particular poup type (for example context menus). However, Qt expects // that plain popup QWindows will also be closed, so we implement the logic // here as well. QList *popups = QCocoaIntegration::instance()->popupWindowStack(); if (!popups->isEmpty()) { // Check if the click is outside all popups. bool inside = false; QPointF qtScreenPoint = qt_mac_flipPoint([self screenMousePoint:theEvent]); for (QList::const_iterator it = popups->begin(); it != popups->end(); ++it) { if ((*it)->geometry().contains(qtScreenPoint.toPoint())) { inside = true; break; } } // Close the popups if the click was outside. if (!inside) { Qt::WindowType type = QCocoaIntegration::instance()->activePopupWindow()->window()->type(); while (QCocoaWindow *popup = QCocoaIntegration::instance()->popPopupWindow()) { QWindowSystemInterface::handleCloseEvent(popup->window()); QWindowSystemInterface::flushWindowSystemEvents(); } // Consume the mouse event when closing the popup, except for tool tips // were it's expected that the event is processed normally. if (type != Qt::ToolTip) return; } } QPointF qtWindowPoint; QPointF qtScreenPoint; [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint]; Q_UNUSED(qtScreenPoint); const bool masked = [self hasMask] && !m_maskRegion.contains(qtWindowPoint.toPoint()); // Maintain masked state for the button for use by MouseDragged and Up. if (masked) m_acceptedMouseDowns &= ~Qt::LeftButton; else m_acceptedMouseDowns |= Qt::LeftButton; // Forward masked out events to the next responder if (masked) { [super mouseDown:theEvent]; return; } if ([self hasMarkedText]) { [[NSTextInputContext currentInputContext] handleEvent:theEvent]; } else { if (!_q_dontOverrideCtrlLMB && [QNSView convertKeyModifiers:[theEvent modifierFlags]] & Qt::MetaModifier) { m_buttons |= Qt::RightButton; m_sendUpAsRightButton = true; } else { m_buttons |= Qt::LeftButton; } [self handleMouseEvent:theEvent]; } } - (void)mouseDragged:(NSEvent *)theEvent { const bool accepted = [self handleMouseDraggedEvent:theEvent withButton:[theEvent buttonNumber]]; if (!accepted) [super mouseDragged:theEvent]; } - (void)mouseUp:(NSEvent *)theEvent { const bool accepted = [self handleMouseUpEvent:theEvent withButton:[theEvent buttonNumber]]; if (!accepted) [super mouseUp:theEvent]; } - (void)rightMouseDown:(NSEvent *)theEvent { // Wacom tablet might not return the correct button number for NSEvent buttonNumber // on right clicks. Decide here that the button is the "right" button and forward // the button number to the mouse (and tablet) handler. const bool accepted = [self handleMouseDownEvent:theEvent withButton:1]; if (!accepted) [super rightMouseDown:theEvent]; } - (void)rightMouseDragged:(NSEvent *)theEvent { const bool accepted = [self handleMouseDraggedEvent:theEvent withButton:1]; if (!accepted) [super rightMouseDragged:theEvent]; } - (void)rightMouseUp:(NSEvent *)theEvent { const bool accepted = [self handleMouseUpEvent:theEvent withButton:1]; if (!accepted) [super rightMouseUp:theEvent]; } - (void)otherMouseDown:(NSEvent *)theEvent { const bool accepted = [self handleMouseDownEvent:theEvent withButton:[theEvent buttonNumber]]; if (!accepted) [super otherMouseDown:theEvent]; } - (void)otherMouseDragged:(NSEvent *)theEvent { const bool accepted = [self handleMouseDraggedEvent:theEvent withButton:[theEvent buttonNumber]]; if (!accepted) [super otherMouseDragged:theEvent]; } - (void)otherMouseUp:(NSEvent *)theEvent { const bool accepted = [self handleMouseUpEvent:theEvent withButton:[theEvent buttonNumber]]; if (!accepted) [super otherMouseUp:theEvent]; } - (void)updateTrackingAreas { [super updateTrackingAreas]; 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]) return; // 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 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 | NSTrackingInVisibleRect | NSTrackingMouseMoved | NSTrackingCursorUpdate; [m_trackingArea release]; m_trackingArea = [[NSTrackingArea alloc] initWithRect:[self frame] options:trackingOptions owner:m_mouseMoveHelper userInfo:nil]; [self addTrackingArea:m_trackingArea]; } - (void)cursorUpdate:(NSEvent *)theEvent { Q_UNUSED(theEvent); m_platformWindow->applyEffectiveWindowCursor(); } - (void)mouseMovedImpl:(NSEvent *)theEvent { if (!m_platformWindow) return; if ([self isTransparentForUserInput]) return; QPointF windowPoint; QPointF 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. // 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_enterLeaveTargetWindow) { QWindowSystemInterface::handleEnterLeaveEvent(childWindow, m_platformWindow->m_enterLeaveTargetWindow, windowPoint, screenPoint); m_platformWindow->m_enterLeaveTargetWindow = 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_platformWindow->window()) return; [self handleMouseEvent: theEvent]; } - (void)mouseEnteredImpl:(NSEvent *)theEvent { Q_UNUSED(theEvent) if (!m_platformWindow) return; m_platformWindow->m_windowUnderMouse = true; if ([self isTransparentForUserInput]) return; // Top-level windows generate enter events for sub-windows. if (!m_platformWindow->m_nsWindow) return; QPointF windowPoint; QPointF screenPoint; [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; m_platformWindow->m_enterLeaveTargetWindow = m_platformWindow->childWindowAt(windowPoint.toPoint()); QWindowSystemInterface::handleEnterEvent(m_platformWindow->m_enterLeaveTargetWindow, windowPoint, screenPoint); } - (void)mouseExitedImpl:(NSEvent *)theEvent { Q_UNUSED(theEvent); if (!m_platformWindow) return; m_platformWindow->m_windowUnderMouse = false; if ([self isTransparentForUserInput]) return; // Top-level windows generate leave events for sub-windows. if (!m_platformWindow->m_nsWindow) return; QWindowSystemInterface::handleLeaveEvent(m_platformWindow->m_enterLeaveTargetWindow); m_platformWindow->m_enterLeaveTargetWindow = 0; } #ifndef QT_NO_TABLETEVENT struct QCocoaTabletDeviceData { QTabletEvent::TabletDevice device; QTabletEvent::PointerType pointerType; uint capabilityMask; qint64 uid; }; typedef QHash QCocoaTabletDeviceDataHash; Q_GLOBAL_STATIC(QCocoaTabletDeviceDataHash, tabletDeviceDataHash) - (bool)handleTabletEvent: (NSEvent *)theEvent { + static bool ignoreButtonMapping = qEnvironmentVariableIsSet("QT_MAC_TABLET_IGNORE_BUTTON_MAPPING"); + if (!m_platformWindow) return false; NSEventType eventType = [theEvent type]; if (eventType != NSTabletPoint && [theEvent subtype] != NSTabletPointEventSubtype) return false; // Not a tablet event. ulong timestamp = [theEvent timestamp] * 1000; QPointF windowPoint; QPointF screenPoint; [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint: &windowPoint andScreenPoint: &screenPoint]; uint deviceId = [theEvent deviceID]; if (!tabletDeviceDataHash->contains(deviceId)) { // Error: Unknown tablet device. Qt also gets into this state // when running on a VM. This appears to be harmless; don't // print a warning. return false; } const QCocoaTabletDeviceData &deviceData = tabletDeviceDataHash->value(deviceId); bool down = (eventType != NSMouseMoved); qreal pressure; if (down) { pressure = [theEvent pressure]; } else { pressure = 0.0; } NSPoint tilt = [theEvent tilt]; int xTilt = qRound(tilt.x * 60.0); int yTilt = qRound(tilt.y * -60.0); qreal tangentialPressure = 0; qreal rotation = 0; int z = 0; if (deviceData.capabilityMask & 0x0200) z = [theEvent absoluteZ]; if (deviceData.capabilityMask & 0x0800) tangentialPressure = ([theEvent tangentialPressure] * 2.0) - 1.0; rotation = 360.0 - [theEvent rotation]; if (rotation > 180.0) rotation -= 360.0; Qt::KeyboardModifiers keyboardModifiers = [QNSView convertKeyModifiers:[theEvent modifierFlags]]; + Qt::MouseButtons buttons = ignoreButtonMapping ? static_cast(static_cast([theEvent buttonMask])) : m_buttons; qCDebug(lcQpaTablet, "event on tablet %d with tool %d type %d unique ID %lld pos %6.1f, %6.1f root pos %6.1f, %6.1f buttons 0x%x pressure %4.2lf tilt %d, %d rotation %6.2lf", deviceId, deviceData.device, deviceData.pointerType, deviceData.uid, windowPoint.x(), windowPoint.y(), screenPoint.x(), screenPoint.y(), - static_cast(m_buttons), pressure, xTilt, yTilt, rotation); + static_cast(buttons), pressure, xTilt, yTilt, rotation); QWindowSystemInterface::handleTabletEvent(m_platformWindow->window(), timestamp, windowPoint, screenPoint, - deviceData.device, deviceData.pointerType, m_buttons, pressure, xTilt, yTilt, + deviceData.device, deviceData.pointerType, buttons, pressure, xTilt, yTilt, tangentialPressure, rotation, z, deviceData.uid, keyboardModifiers); return true; } - (void)tabletPoint:(NSEvent *)theEvent { if ([self isTransparentForUserInput]) return [super tabletPoint:theEvent]; [self handleTabletEvent: theEvent]; } static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) { qint64 uid = [theEvent uniqueID]; uint bits = [theEvent vendorPointingDeviceType]; if (bits == 0 && uid != 0) { // Fallback. It seems that the driver doesn't always include all the information. // High-End Wacom devices store their "type" in the uper bits of the Unique ID. // I'm not sure how to handle it for consumer devices, but I'll test that in a bit. bits = uid >> 32; } QTabletEvent::TabletDevice device; // Defined in the "EN0056-NxtGenImpGuideX" // on Wacom's Developer Website (www.wacomeng.com) if (((bits & 0x0006) == 0x0002) && ((bits & 0x0F06) != 0x0902)) { device = QTabletEvent::Stylus; } else { switch (bits & 0x0F06) { case 0x0802: device = QTabletEvent::Stylus; break; case 0x0902: device = QTabletEvent::Airbrush; break; case 0x0004: device = QTabletEvent::FourDMouse; break; case 0x0006: device = QTabletEvent::Puck; break; case 0x0804: device = QTabletEvent::RotationStylus; break; default: device = QTabletEvent::NoDevice; } } return device; } - (void)tabletProximity:(NSEvent *)theEvent { if ([self isTransparentForUserInput]) return [super tabletProximity:theEvent]; ulong timestamp = [theEvent timestamp] * 1000; QCocoaTabletDeviceData deviceData; deviceData.uid = [theEvent uniqueID]; deviceData.capabilityMask = [theEvent capabilityMask]; switch ([theEvent pointingDeviceType]) { case NSUnknownPointingDevice: default: deviceData.pointerType = QTabletEvent::UnknownPointer; break; case NSPenPointingDevice: deviceData.pointerType = QTabletEvent::Pen; break; case NSCursorPointingDevice: deviceData.pointerType = QTabletEvent::Cursor; break; case NSEraserPointingDevice: deviceData.pointerType = QTabletEvent::Eraser; break; } deviceData.device = wacomTabletDevice(theEvent); // The deviceID is "unique" while in the proximity, it's a key that we can use for // linking up QCocoaTabletDeviceData to an event (especially if there are two devices in action). bool entering = [theEvent isEnteringProximity]; uint deviceId = [theEvent deviceID]; if (entering) { tabletDeviceDataHash->insert(deviceId, deviceData); } else { tabletDeviceDataHash->remove(deviceId); } qCDebug(lcQpaTablet, "proximity change on tablet %d: current tool %d type %d unique ID %lld", deviceId, deviceData.device, deviceData.pointerType, deviceData.uid); if (entering) { QWindowSystemInterface::handleTabletEnterProximityEvent(timestamp, deviceData.device, deviceData.pointerType, deviceData.uid); } else { QWindowSystemInterface::handleTabletLeaveProximityEvent(timestamp, deviceData.device, deviceData.pointerType, deviceData.uid); } } #endif - (bool)shouldSendSingleTouch { if (!m_platformWindow) return true; // QtWidgets expects single-point touch events, QtDeclarative does not. // Until there is an API we solve this by looking at the window class type. return m_platformWindow->window()->inherits("QWidgetWindow"); } - (void)touchesBeganWithEvent:(NSEvent *)event { if (!m_platformWindow) return; const NSTimeInterval timestamp = [event timestamp]; const QList points = QCocoaTouch::getCurrentTouchPointList(event, [self shouldSendSingleTouch]); qCDebug(lcQpaTouch) << "touchesBeganWithEvent" << points; QWindowSystemInterface::handleTouchEvent(m_platformWindow->window(), timestamp * 1000, touchDevice, points); } - (void)touchesMovedWithEvent:(NSEvent *)event { if (!m_platformWindow) return; const NSTimeInterval timestamp = [event timestamp]; const QList points = QCocoaTouch::getCurrentTouchPointList(event, [self shouldSendSingleTouch]); qCDebug(lcQpaTouch) << "touchesMovedWithEvent" << points; QWindowSystemInterface::handleTouchEvent(m_platformWindow->window(), timestamp * 1000, touchDevice, points); } - (void)touchesEndedWithEvent:(NSEvent *)event { if (!m_platformWindow) return; const NSTimeInterval timestamp = [event timestamp]; const QList points = QCocoaTouch::getCurrentTouchPointList(event, [self shouldSendSingleTouch]); qCDebug(lcQpaTouch) << "touchesEndedWithEvent" << points; QWindowSystemInterface::handleTouchEvent(m_platformWindow->window(), timestamp * 1000, touchDevice, points); } - (void)touchesCancelledWithEvent:(NSEvent *)event { if (!m_platformWindow) return; const NSTimeInterval timestamp = [event timestamp]; const QList points = QCocoaTouch::getCurrentTouchPointList(event, [self shouldSendSingleTouch]); qCDebug(lcQpaTouch) << "touchesCancelledWithEvent" << points; QWindowSystemInterface::handleTouchEvent(m_platformWindow->window(), timestamp * 1000, touchDevice, points); } #ifndef QT_NO_GESTURES - (bool)handleGestureAsBeginEnd:(NSEvent *)event { if (QOperatingSystemVersion::current() < QOperatingSystemVersion::OSXElCapitan) return false; if ([event phase] == NSEventPhaseBegan) { [self beginGestureWithEvent:event]; return true; } if ([event phase] == NSEventPhaseEnded) { [self endGestureWithEvent:event]; return true; } return false; } - (void)magnifyWithEvent:(NSEvent *)event { if (!m_platformWindow) return; if ([self handleGestureAsBeginEnd:event]) return; qCDebug(lcQpaGestures) << "magnifyWithEvent" << [event magnification]; const NSTimeInterval timestamp = [event timestamp]; QPointF windowPoint; QPointF screenPoint; [self convertFromScreen:[self screenMousePoint:event] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; QWindowSystemInterface::handleGestureEventWithRealValue(m_platformWindow->window(), timestamp, Qt::ZoomNativeGesture, [event magnification], windowPoint, screenPoint); } - (void)smartMagnifyWithEvent:(NSEvent *)event { if (!m_platformWindow) return; static bool zoomIn = true; qCDebug(lcQpaGestures) << "smartMagnifyWithEvent" << zoomIn; const NSTimeInterval timestamp = [event timestamp]; QPointF windowPoint; QPointF screenPoint; [self convertFromScreen:[self screenMousePoint:event] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; QWindowSystemInterface::handleGestureEventWithRealValue(m_platformWindow->window(), timestamp, Qt::SmartZoomNativeGesture, zoomIn ? 1.0f : 0.0f, windowPoint, screenPoint); zoomIn = !zoomIn; } - (void)rotateWithEvent:(NSEvent *)event { if (!m_platformWindow) return; if ([self handleGestureAsBeginEnd:event]) return; const NSTimeInterval timestamp = [event timestamp]; QPointF windowPoint; QPointF screenPoint; [self convertFromScreen:[self screenMousePoint:event] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; QWindowSystemInterface::handleGestureEventWithRealValue(m_platformWindow->window(), timestamp, Qt::RotateNativeGesture, -[event rotation], windowPoint, screenPoint); } - (void)swipeWithEvent:(NSEvent *)event { if (!m_platformWindow) return; qCDebug(lcQpaGestures) << "swipeWithEvent" << [event deltaX] << [event deltaY]; const NSTimeInterval timestamp = [event timestamp]; QPointF windowPoint; QPointF screenPoint; [self convertFromScreen:[self screenMousePoint:event] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; qreal angle = 0.0f; if ([event deltaX] == 1) angle = 180.0f; else if ([event deltaX] == -1) angle = 0.0f; else if ([event deltaY] == 1) angle = 90.0f; else if ([event deltaY] == -1) angle = 270.0f; QWindowSystemInterface::handleGestureEventWithRealValue(m_platformWindow->window(), timestamp, Qt::SwipeNativeGesture, angle, windowPoint, screenPoint); } - (void)beginGestureWithEvent:(NSEvent *)event { if (!m_platformWindow) return; const NSTimeInterval timestamp = [event timestamp]; QPointF windowPoint; QPointF screenPoint; [self convertFromScreen:[self screenMousePoint:event] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; qCDebug(lcQpaGestures) << "beginGestureWithEvent @" << windowPoint; QWindowSystemInterface::handleGestureEvent(m_platformWindow->window(), timestamp, Qt::BeginNativeGesture, windowPoint, screenPoint); } - (void)endGestureWithEvent:(NSEvent *)event { if (!m_platformWindow) return; qCDebug(lcQpaGestures) << "endGestureWithEvent"; const NSTimeInterval timestamp = [event timestamp]; QPointF windowPoint; QPointF screenPoint; [self convertFromScreen:[self screenMousePoint:event] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; QWindowSystemInterface::handleGestureEvent(m_platformWindow->window(), timestamp, Qt::EndNativeGesture, windowPoint, screenPoint); } #endif // QT_NO_GESTURES #if QT_CONFIG(wheelevent) - (void)scrollWheel:(NSEvent *)theEvent { if (!m_platformWindow) return; if ([self isTransparentForUserInput]) return [super scrollWheel:theEvent]; QPoint angleDelta; Qt::MouseEventSource source = Qt::MouseEventNotSynthesized; if ([theEvent hasPreciseScrollingDeltas]) { // The mouse device contains pixel scroll wheel support (Mighty Mouse, Trackpad). // Since deviceDelta is delivered as pixels rather than degrees, we need to // convert from pixels to degrees in a sensible manner. // 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 angleDelta.setX([theEvent scrollingDeltaX] * pixelsToDegrees); angleDelta.setY([theEvent scrollingDeltaY] * pixelsToDegrees); source = Qt::MouseEventSynthesizedBySystem; } else { // Remove acceleration, and use either -120 or 120 as delta: angleDelta.setX(qBound(-120, int([theEvent deltaX] * 10000), 120)); angleDelta.setY(qBound(-120, int([theEvent deltaY] * 10000), 120)); } QPoint pixelDelta; 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); } QPointF qt_windowPoint; QPointF qt_screenPoint; [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qt_windowPoint andScreenPoint:&qt_screenPoint]; NSTimeInterval timestamp = [theEvent timestamp]; ulong qt_timestamp = timestamp * 1000; // 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; // MayBegin is likely to happen. We treat it the same as an actual begin. if (phase == NSEventPhaseMayBegin) { m_scrolling = true; ph = Qt::ScrollBegin; } else if (phase == NSEventPhaseBegan) { // If MayBegin did not happen, Began is the actual beginning. if (!m_scrolling) ph = Qt::ScrollBegin; m_scrolling = true; } else if (phase == NSEventPhaseEnded || phase == NSEventPhaseCancelled || momentumPhase == NSEventPhaseEnded || momentumPhase == NSEventPhaseCancelled) { ph = Qt::ScrollEnd; m_scrolling = false; } else if (phase == NSEventPhaseNone && momentumPhase == NSEventPhaseNone) { ph = Qt::NoScrollPhase; } // "isInverted": natural OS X scrolling, inverted from the Qt/other platform/Jens perspective. bool isInverted = [theEvent isDirectionInvertedFromDevice]; QWindowSystemInterface::handleWheelEvent(m_platformWindow->window(), qt_timestamp, qt_windowPoint, qt_screenPoint, pixelDelta, angleDelta, currentWheelModifiers, ph, source, isInverted); } #endif // QT_CONFIG(wheelevent) - (int) convertKeyCode : (QChar)keyChar { return qt_mac_cocoaKey2QtKey(keyChar); } + (Qt::KeyboardModifiers) convertKeyModifiers : (ulong)modifierFlags { const bool swapCtrlAndMeta = !qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta); Qt::KeyboardModifiers qtMods =Qt::NoModifier; if (modifierFlags & NSShiftKeyMask) qtMods |= Qt::ShiftModifier; if (modifierFlags & NSControlKeyMask) qtMods |= (swapCtrlAndMeta ? Qt::MetaModifier : Qt::ControlModifier); if (modifierFlags & NSAlternateKeyMask) qtMods |= Qt::AltModifier; if (modifierFlags & NSCommandKeyMask) qtMods |= (swapCtrlAndMeta ? Qt::ControlModifier : Qt::MetaModifier); if (modifierFlags & NSNumericPadKeyMask) qtMods |= Qt::KeypadModifier; return qtMods; } - (bool)handleKeyEvent:(NSEvent *)nsevent eventType:(int)eventType { ulong timestamp = [nsevent timestamp] * 1000; ulong nativeModifiers = [nsevent modifierFlags]; Qt::KeyboardModifiers modifiers = [QNSView convertKeyModifiers: nativeModifiers]; NSString *charactersIgnoringModifiers = [nsevent charactersIgnoringModifiers]; NSString *characters = [nsevent characters]; if (m_inputSource != characters) { [m_inputSource release]; m_inputSource = [characters retain]; } // There is no way to get the scan code from carbon/cocoa. But we cannot // use the value 0, since it indicates that the event originates from somewhere // else than the keyboard. quint32 nativeScanCode = 1; quint32 nativeVirtualKey = [nsevent keyCode]; const bool swapCtrlAndMeta = !qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta); QChar ch = QChar::ReplacementCharacter; int keyCode = Qt::Key_unknown; // If a dead key occurs as a result of pressing a key combination then // characters will have 0 length, but charactersIgnoringModifiers will // have a valid character in it. This enables key combinations such as // ALT+E to be used as a shortcut with an English keyboard even though // pressing ALT+E will give a dead key while doing normal text input. if ([characters length] != 0 || [charactersIgnoringModifiers length] != 0) { if (((modifiers & (swapCtrlAndMeta ? Qt::MetaModifier : Qt::ControlModifier)) || (modifiers & Qt::AltModifier)) && ([charactersIgnoringModifiers length] != 0)) ch = QChar([charactersIgnoringModifiers characterAtIndex:0]); else if ([characters length] != 0) ch = QChar([characters characterAtIndex:0]); keyCode = [self convertKeyCode:ch]; } // we will send a key event unless the input method sets m_sendKeyEvent to false m_sendKeyEvent = true; 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 (!(modifiers & (Qt::ControlModifier | Qt::MetaModifier)) && (ch.unicode() < 0xf700 || ch.unicode() > 0xf8ff)) text = QString::fromNSString(characters); 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 (keyCode == Qt::Key_Menu && modifiers == Qt::NoModifier) { QPoint globalPos, pos; if (window) { QNSView *targetView = (QNSView*)(window->winId()); QPointF qtWindowPoint, qtScreenPoint; [targetView convertFromScreen:[nsevent locationInWindow] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint]; globalPos = qtScreenPoint.toPoint(); pos = qtWindowPoint.toPoint(); } QWindowSystemInterface::handleContextMenuEvent(window, false, pos, globalPos, modifiers); // Handling a context menu may result in closing the window if (!m_platformWindow) { return true; } } if (m_composingText.isEmpty()) { m_sendKeyEvent = !QWindowSystemInterface::handleShortcutEvent(window, timestamp, keyCode, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers, text, [nsevent isARepeat], 1); // Handling a shortcut may result in closing the window if (!m_platformWindow) return true; } QObject *fo = m_platformWindow->window()->focusObject(); if (m_sendKeyEvent && fo) { QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImHints); if (QCoreApplication::sendEvent(fo, &queryEvent)) { bool imEnabled = queryEvent.value(Qt::ImEnabled).toBool(); Qt::InputMethodHints hints = static_cast(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; } } } if (m_resendKeyEvent) m_sendKeyEvent = true; } bool accepted = true; if (m_sendKeyEvent && m_composingText.isEmpty()) { QWindowSystemInterface::handleExtendedKeyEvent(window, timestamp, QEvent::Type(eventType), keyCode, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers, text, [nsevent isARepeat], 1, false); accepted = QWindowSystemInterface::flushWindowSystemEvents(); } m_sendKeyEvent = false; m_resendKeyEvent = false; return accepted; } - (void)keyDown:(NSEvent *)nsevent { if ([self isTransparentForUserInput]) return [super keyDown:nsevent]; const bool accepted = [self handleKeyEvent:nsevent eventType:int(QEvent::KeyPress)]; // When Qt is used to implement a plugin for a native application we // want to propagate unhandled events to other native views. However, // Qt does not always set the accepted state correctly (in particular // for return key events), so do this for plugin applications only // to prevent incorrect forwarding in the general case. const bool shouldPropagate = QCoreApplication::testAttribute(Qt::AA_PluginApplication) && !accepted; // Track keyDown acceptance/forward state for later acceptance of the keyUp. if (!shouldPropagate) m_acceptedKeyDowns.insert([nsevent keyCode]); if (shouldPropagate) [super keyDown:nsevent]; } - (void)keyUp:(NSEvent *)nsevent { if ([self isTransparentForUserInput]) return [super keyUp:nsevent]; const bool keyUpAccepted = [self handleKeyEvent:nsevent eventType:int(QEvent::KeyRelease)]; // Propagate the keyUp if neither Qt accepted it nor the corresponding KeyDown was // accepted. Qt text controls wil often not use and ignore keyUp events, but we // want to avoid propagating unmatched keyUps. const bool keyDownAccepted = m_acceptedKeyDowns.remove([nsevent keyCode]); if (!keyUpAccepted && !keyDownAccepted) [super keyUp:nsevent]; } - (void)cancelOperation:(id)sender { Q_UNUSED(sender); 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 { ulong timestamp = [nsevent timestamp] * 1000; ulong modifiers = [nsevent modifierFlags]; Qt::KeyboardModifiers qmodifiers = [QNSView convertKeyModifiers:modifiers]; // calculate the delta and remember the current modifiers for next time static ulong m_lastKnownModifiers; ulong lastKnownModifiers = m_lastKnownModifiers; ulong delta = lastKnownModifiers ^ modifiers; m_lastKnownModifiers = modifiers; const bool swapCtrlAndMeta = !qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta); struct qt_mac_enum_mapper { ulong mac_mask; Qt::Key qt_code; }; static qt_mac_enum_mapper modifier_key_symbols[] = { { NSShiftKeyMask, Qt::Key_Shift }, { NSControlKeyMask, (swapCtrlAndMeta ? Qt::Key_Meta : Qt::Key_Control) }, { NSCommandKeyMask, (swapCtrlAndMeta ? Qt::Key_Control : Qt::Key_Meta) }, { NSAlternateKeyMask, Qt::Key_Alt }, { NSAlphaShiftKeyMask, Qt::Key_CapsLock }, { 0ul, Qt::Key_unknown } }; for (int i = 0; modifier_key_symbols[i].mac_mask != 0u; ++i) { uint mac_mask = modifier_key_symbols[i].mac_mask; if ((delta & mac_mask) == 0u) continue; QWindowSystemInterface::handleKeyEvent(m_platformWindow->window(), timestamp, (lastKnownModifiers & mac_mask) ? QEvent::KeyRelease : QEvent::KeyPress, modifier_key_symbols[i].qt_code, qmodifiers ^ [QNSView convertKeyModifiers:mac_mask]); } } - (void) insertNewline:(id)sender { Q_UNUSED(sender); m_resendKeyEvent = true; } - (void) doCommandBySelector:(SEL)aSelector { [self tryToPerform:aSelector with:self]; } - (void) insertText:(id)aString replacementRange:(NSRange)replacementRange { Q_UNUSED(replacementRange) if (m_sendKeyEvent && m_composingText.isEmpty() && [aString isEqualToString:m_inputSource]) { // don't send input method events for simple text input (let handleKeyEvent send key events instead) return; } QString commitString; if ([aString length]) { if ([aString isKindOfClass:[NSAttributedString class]]) { commitString = QString::fromCFString(reinterpret_cast([aString string])); } else { commitString = QString::fromCFString(reinterpret_cast(aString)); }; } if (QObject *fo = m_platformWindow->window()->focusObject()) { QInputMethodQueryEvent queryEvent(Qt::ImEnabled); if (QCoreApplication::sendEvent(fo, &queryEvent)) { if (queryEvent.value(Qt::ImEnabled).toBool()) { QInputMethodEvent e; e.setCommitString(commitString); QCoreApplication::sendEvent(fo, &e); // prevent handleKeyEvent from sending a key event m_sendKeyEvent = false; } } } m_composingText.clear(); m_composingFocusObject = nullptr; } - (void) setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange { Q_UNUSED(replacementRange) QString preeditString; QList attrs; attrs<([aString string])); int composingLength = preeditString.length(); int index = 0; // Create attributes for individual sections of preedit text while (index < composingLength) { NSRange effectiveRange; NSRange range = NSMakeRange(index, composingLength-index); NSDictionary *attributes = [aString attributesAtIndex:index longestEffectiveRange:&effectiveRange inRange:range]; NSNumber *underlineStyle = [attributes objectForKey:NSUnderlineStyleAttributeName]; if (underlineStyle) { QColor clr (Qt::black); NSColor *color = [attributes objectForKey:NSUnderlineColorAttributeName]; if (color) { clr = qt_mac_toQColor(color); } QTextCharFormat format; format.setFontUnderline(true); format.setUnderlineColor(clr); attrs<(aString)); } if (attrs.isEmpty()) { QTextCharFormat format; format.setFontUnderline(true); attrs<window()->focusObject()) { m_composingFocusObject = fo; QInputMethodQueryEvent queryEvent(Qt::ImEnabled); if (QCoreApplication::sendEvent(fo, &queryEvent)) { if (queryEvent.value(Qt::ImEnabled).toBool()) { QInputMethodEvent e(preeditString, attrs); QCoreApplication::sendEvent(fo, &e); // prevent handleKeyEvent from sending a key event m_sendKeyEvent = false; } } } } - (void)cancelComposingText { if (m_composingText.isEmpty()) return; if (m_composingFocusObject) { QInputMethodQueryEvent queryEvent(Qt::ImEnabled); if (QCoreApplication::sendEvent(m_composingFocusObject, &queryEvent)) { if (queryEvent.value(Qt::ImEnabled).toBool()) { QInputMethodEvent e; QCoreApplication::sendEvent(m_composingFocusObject, &e); } } } m_composingText.clear(); m_composingFocusObject = nullptr; } - (void) unmarkText { if (!m_composingText.isEmpty()) { if (QObject *fo = m_platformWindow->window()->focusObject()) { QInputMethodQueryEvent queryEvent(Qt::ImEnabled); if (QCoreApplication::sendEvent(fo, &queryEvent)) { if (queryEvent.value(Qt::ImEnabled).toBool()) { QInputMethodEvent e; e.setCommitString(m_composingText); QCoreApplication::sendEvent(fo, &e); } } } } m_composingText.clear(); m_composingFocusObject = nullptr; } - (BOOL) hasMarkedText { return (m_composingText.isEmpty() ? NO: YES); } - (NSAttributedString *) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange { Q_UNUSED(actualRange) QObject *fo = m_platformWindow->window()->focusObject(); if (!fo) return nil; QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImCurrentSelection); if (!QCoreApplication::sendEvent(fo, &queryEvent)) return nil; if (!queryEvent.value(Qt::ImEnabled).toBool()) return nil; QString selectedText = queryEvent.value(Qt::ImCurrentSelection).toString(); if (selectedText.isEmpty()) return nil; QCFString string(selectedText.mid(aRange.location, aRange.length)); const NSString *tmpString = reinterpret_cast((CFStringRef)string); return [[[NSAttributedString alloc] initWithString:const_cast(tmpString)] autorelease]; } - (NSRange) markedRange { NSRange range; if (!m_composingText.isEmpty()) { range.location = 0; range.length = m_composingText.length(); } else { range.location = NSNotFound; range.length = 0; } return range; } - (NSRange) selectedRange { NSRange selectedRange = {0, 0}; QObject *fo = m_platformWindow->window()->focusObject(); if (!fo) return selectedRange; QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImCurrentSelection); if (!QCoreApplication::sendEvent(fo, &queryEvent)) return selectedRange; if (!queryEvent.value(Qt::ImEnabled).toBool()) return selectedRange; QString selectedText = queryEvent.value(Qt::ImCurrentSelection).toString(); if (!selectedText.isEmpty()) { selectedRange.location = 0; selectedRange.length = selectedText.length(); } return selectedRange; } - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange { Q_UNUSED(aRange) Q_UNUSED(actualRange) QObject *fo = m_platformWindow->window()->focusObject(); if (!fo) return NSZeroRect; QInputMethodQueryEvent queryEvent(Qt::ImEnabled); if (!QCoreApplication::sendEvent(fo, &queryEvent)) return NSZeroRect; if (!queryEvent.value(Qt::ImEnabled).toBool()) return NSZeroRect; if (!m_platformWindow->window()) return NSZeroRect; // The returned rect is always based on the internal cursor. QRect mr = qApp->inputMethod()->cursorRectangle().toRect(); QPoint mp = m_platformWindow->window()->mapToGlobal(mr.bottomLeft()); NSRect rect; rect.origin.x = mp.x(); rect.origin.y = qt_mac_flipYCoordinate(mp.y()); rect.size.width = mr.width(); rect.size.height = mr.height(); return rect; } - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint { // We don't support cursor movements using mouse while composing. Q_UNUSED(aPoint); return NSNotFound; } - (NSArray*)validAttributesForMarkedText { if (!m_platformWindow) return nil; if (m_platformWindow->window() != QGuiApplication::focusWindow()) return nil; QObject *fo = m_platformWindow->window()->focusObject(); if (!fo) return nil; QInputMethodQueryEvent queryEvent(Qt::ImEnabled); if (!QCoreApplication::sendEvent(fo, &queryEvent)) return nil; if (!queryEvent.value(Qt::ImEnabled).toBool()) return nil; // Support only underline color/style. return [NSArray arrayWithObjects:NSUnderlineColorAttributeName, NSUnderlineStyleAttributeName, nil]; } -(void)registerDragTypes { QMacAutoReleasePool pool; QStringList customTypes = qt_mac_enabledDraggedTypes(); if (currentCustomDragTypes == 0 || *currentCustomDragTypes != customTypes) { if (currentCustomDragTypes == 0) currentCustomDragTypes = new QStringList(); *currentCustomDragTypes = customTypes; const NSString* mimeTypeGeneric = @"com.trolltech.qt.MimeTypeName"; NSMutableArray *supportedTypes = [NSMutableArray arrayWithObjects:NSColorPboardType, NSFilenamesPboardType, NSStringPboardType, NSFilenamesPboardType, NSPostScriptPboardType, NSTIFFPboardType, NSRTFPboardType, NSTabularTextPboardType, NSFontPboardType, NSRulerPboardType, NSFileContentsPboardType, NSColorPboardType, NSRTFDPboardType, NSHTMLPboardType, NSURLPboardType, NSPDFPboardType, NSVCardPboardType, NSFilesPromisePboardType, NSInkTextPboardType, NSMultipleTextSelectionPboardType, mimeTypeGeneric, nil]; // Add custom types supported by the application. for (int i = 0; i < customTypes.size(); i++) { [supportedTypes addObject:customTypes[i].toNSString()]; } [self registerForDraggedTypes:supportedTypes]; } } static QWindow *findEventTargetWindow(QWindow *candidate) { while (candidate) { if (!(candidate->flags() & Qt::WindowTransparentForInput)) return candidate; candidate = candidate->parent(); } return candidate; } static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint point) { return target->mapFromGlobal(source->mapToGlobal(point)); } - (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context { Q_UNUSED(session); Q_UNUSED(context); QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); return qt_mac_mapDropActions(nativeDrag->currentDrag()->supportedActions()); } - (BOOL)ignoreModifierKeysForDraggingSession:(NSDraggingSession *)session { Q_UNUSED(session); // According to the "Dragging Sources" chapter on Cocoa DnD Programming // (https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/DragandDrop/Concepts/dragsource.html), // if the control, option, or command key is pressed, the source’s // operation mask is filtered to only contain a reduced set of operations. // // Since Qt already takes care of tracking the keyboard modifiers, we // don't need (or want) Cocoa to filter anything. Instead, we'll let // the application do the actual filtering. return YES; } - (BOOL)wantsPeriodicDraggingUpdates { // From the documentation: // // "If the destination returns NO, these messages are sent only when the mouse moves // or a modifier flag changes. Otherwise the destination gets the default behavior, // where it receives periodic dragging-updated messages even if nothing changes." // // We do not want these constant drag update events while mouse is stationary, // since we do all animations (autoscroll) with timers. return NO; } - (BOOL)wantsPeriodicDraggingUpdates:(void *)dummy { // This method never gets called. It's a workaround for Apple's // bug: they first respondsToSelector : @selector(wantsPeriodicDraggingUpdates:) // (note ':') and then call -wantsPeriodicDraggingUpdate (without colon). // So, let's make them happy. Q_UNUSED(dummy); return NO; } - (void)updateCursorFromDragResponse:(QPlatformDragQtResponse)response drag:(QCocoaDrag *)drag { const QPixmap pixmapCursor = drag->currentDrag()->dragCursor(response.acceptedAction()); NSCursor *nativeCursor = nil; if (pixmapCursor.isNull()) { switch (response.acceptedAction()) { case Qt::CopyAction: nativeCursor = [NSCursor dragCopyCursor]; break; case Qt::LinkAction: nativeCursor = [NSCursor dragLinkCursor]; break; case Qt::IgnoreAction: // Uncomment the next lines if forbiden cursor wanted on non droppable targets. /*nativeCursor = [NSCursor operationNotAllowedCursor]; break;*/ case Qt::MoveAction: default: nativeCursor = [NSCursor arrowCursor]; break; } } else { NSImage *nsimage = qt_mac_create_nsimage(pixmapCursor); nsimage.size = NSSizeFromCGSize((pixmapCursor.size() / pixmapCursor.devicePixelRatioF()).toCGSize()); nativeCursor = [[NSCursor alloc] initWithImage:nsimage hotSpot:NSZeroPoint]; [nsimage release]; } // change the cursor [nativeCursor set]; // 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, CGPointMake(mousePos.x(), mousePos.y()), kCGMouseButtonLeft // ignored )); CGEventPost(kCGHIDEventTap, moveEvent); CFRelease(moveEvent); } - (NSDragOperation)draggingEntered:(id )sender { return [self handleDrag : sender]; } - (NSDragOperation)draggingUpdated:(id )sender { m_updatingDrag = true; const NSDragOperation ret([self handleDrag : sender]); m_updatingDrag = false; return ret; } // Sends drag update to Qt, return the action - (NSDragOperation)handleDrag:(id )sender { if (!m_platformWindow) return NSDragOperationNone; NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil]; QPoint qt_windowPoint(windowPoint.x, windowPoint.y); Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]); QWindow *target = findEventTargetWindow(m_platformWindow->window()); if (!target) return NSDragOperationNone; // update these so selecting move/copy/link works QGuiApplicationPrivate::modifier_buttons = [QNSView convertKeyModifiers: [[NSApp currentEvent] modifierFlags]]; QPlatformDragQtResponse response(false, Qt::IgnoreAction, QRect()); QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); if (nativeDrag->currentDrag()) { // The drag was started from within the application response = QWindowSystemInterface::handleDrag(target, nativeDrag->platformDropData(), mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), qtAllowed); [self updateCursorFromDragResponse:response drag:nativeDrag]; } else { QCocoaDropData mimeData([sender draggingPasteboard]); response = QWindowSystemInterface::handleDrag(target, &mimeData, mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), qtAllowed); } return qt_mac_mapDropAction(response.acceptedAction()); } - (void)draggingExited:(id )sender { if (!m_platformWindow) return; QWindow *target = findEventTargetWindow(m_platformWindow->window()); if (!target) return; NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil]; QPoint qt_windowPoint(windowPoint.x, windowPoint.y); // Send 0 mime data to indicate drag exit QWindowSystemInterface::handleDrag(target, 0, mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), Qt::IgnoreAction); } // called on drop, send the drop to Qt and return if it was accepted. - (BOOL)performDragOperation:(id )sender { if (!m_platformWindow) return false; QWindow *target = findEventTargetWindow(m_platformWindow->window()); if (!target) return false; NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil]; QPoint qt_windowPoint(windowPoint.x, windowPoint.y); Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]); QPlatformDropQtResponse response(false, Qt::IgnoreAction); QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); if (nativeDrag->currentDrag()) { // The drag was started from within the application response = QWindowSystemInterface::handleDrop(target, nativeDrag->platformDropData(), mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), qtAllowed); } else { QCocoaDropData mimeData([sender draggingPasteboard]); response = QWindowSystemInterface::handleDrop(target, &mimeData, mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), qtAllowed); } if (response.isAccepted()) { QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); nativeDrag->setAcceptedAction(response.acceptedAction()); } return response.isAccepted(); } - (void)draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation { Q_UNUSED(session); Q_UNUSED(operation); if (!m_platformWindow) return; QWindow *target = findEventTargetWindow(m_platformWindow->window()); if (!target) return; // keep our state, and QGuiApplication state (buttons member) in-sync, // or future mouse events will be processed incorrectly NSUInteger pmb = [NSEvent pressedMouseButtons]; for (int buttonNumber = 0; buttonNumber < 32; buttonNumber++) { // see cocoaButton2QtButton() for the 32 value if (!(pmb & (1 << buttonNumber))) m_buttons &= ~cocoaButton2QtButton(buttonNumber); } NSPoint windowPoint = [self.window convertRectFromScreen:NSMakeRect(screenPoint.x, screenPoint.y, 1, 1)].origin; NSPoint nsViewPoint = [self convertPoint: windowPoint fromView: nil]; // NSView/QWindow coordinates QPoint qtWindowPoint(nsViewPoint.x, nsViewPoint.y); QPoint qtScreenPoint = QPoint(screenPoint.x, qt_mac_flipYCoordinate(screenPoint.y)); QWindowSystemInterface::handleMouseEvent(target, mapWindowCoordinates(m_platformWindow->window(), target, qtWindowPoint), qtScreenPoint, m_buttons); } @end @implementation QT_MANGLE_NAMESPACE(QNSView) (QtExtras) - (QCocoaWindow*)platformWindow { return m_platformWindow.data();; } - (BOOL)isMenuView { return m_isMenuView; } @end