diff options
Diffstat (limited to 'src/plugins/platforms/cocoa')
20 files changed, 383 insertions, 249 deletions
diff --git a/src/plugins/platforms/cocoa/cocoa.pro b/src/plugins/platforms/cocoa/cocoa.pro index fec1da334f..02d8b16110 100644 --- a/src/plugins/platforms/cocoa/cocoa.pro +++ b/src/plugins/platforms/cocoa/cocoa.pro @@ -1,10 +1,5 @@ TARGET = qcocoa -PLUGIN_TYPE = platforms -PLUGIN_CLASS_NAME = QCocoaIntegrationPlugin -!equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = - -load(qt_plugin) - OBJECTIVE_SOURCES += main.mm \ qcocoaintegration.mm \ qcocoatheme.mm \ @@ -110,5 +105,8 @@ OTHER_FILES += cocoa.json # DEFINES += QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR # include ($$PWD/../../../../util/accessibilityinspector/accessibilityinspector.pri) -# Window debug support -#DEFINES += QT_COCOA_ENABLE_WINDOW_DEBUG + +PLUGIN_TYPE = platforms +PLUGIN_CLASS_NAME = QCocoaIntegrationPlugin +!equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = - +load(qt_plugin) diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm index bc4cb227a8..1faa806480 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm @@ -63,7 +63,7 @@ void QCocoaAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) return; QMacAccessibilityElement *element = [QMacAccessibilityElement elementWithId: event->uniqueId()]; if (!element) { - qWarning() << "QCocoaAccessibility::notifyAccessibilityUpdate: invalid element"; + qWarning("QCocoaAccessibility::notifyAccessibilityUpdate: invalid element"); return; } @@ -277,7 +277,7 @@ NSArray *unignoredChildren(QAccessibleInterface *interface) if (element) [kids addObject: element]; else - qWarning() << "QCocoaAccessibility: invalid child"; + qWarning("QCocoaAccessibility: invalid child"); } return NSAccessibilityUnignoredChildren(kids); } diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm index 0f8081715b..7128fb72c3 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm @@ -555,7 +555,7 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of - (id)accessibilityHitTest:(NSPoint)point { QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); if (!iface || !iface->isValid()) { -// qDebug() << "Hit test: INVALID"; +// qDebug("Hit test: INVALID"); return NSAccessibilityUnignoredAncestor(self); } @@ -585,7 +585,7 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); if (!iface || !iface->isValid()) { - qWarning() << "FocusedUIElement for INVALID"; + qWarning("FocusedUIElement for INVALID"); return nil; } diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h index 18673a3667..fa05626d18 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.h +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h @@ -57,7 +57,11 @@ public: QPaintDevice *paintDevice() Q_DECL_OVERRIDE; void flush(QWindow *widget, const QRegion ®ion, const QPoint &offset) Q_DECL_OVERRIDE; +#ifndef QT_NO_OPENGL QImage toImage() const Q_DECL_OVERRIDE; +#else + QImage toImage() const; // No QPlatformBackingStore::toImage() for NO_OPENGL builds. +#endif void resize (const QSize &size, const QRegion &) Q_DECL_OVERRIDE; bool scroll(const QRegion &area, int dx, int dy) Q_DECL_OVERRIDE; void beginPaint(const QRegion ®ion) Q_DECL_OVERRIDE; diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.h b/src/plugins/platforms/cocoa/qcocoaglcontext.h index cca541e0e3..5bee708b76 100644 --- a/src/plugins/platforms/cocoa/qcocoaglcontext.h +++ b/src/plugins/platforms/cocoa/qcocoaglcontext.h @@ -63,7 +63,7 @@ public: bool makeCurrent(QPlatformSurface *surface) Q_DECL_OVERRIDE; void doneCurrent() Q_DECL_OVERRIDE; - void (*getProcAddress(const QByteArray &procName)) () Q_DECL_OVERRIDE; + QFunctionPointer getProcAddress(const char *procName) Q_DECL_OVERRIDE; void update(); diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm index e4712d88bc..7a12969972 100644 --- a/src/plugins/platforms/cocoa/qcocoaglcontext.mm +++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm @@ -44,6 +44,7 @@ #include <QtCore/private/qcore_mac_p.h> #include <QtPlatformSupport/private/cglconvenience_p.h> #include <QtPlatformHeaders/qcocoanativecontext.h> +#include <dlfcn.h> #import <AppKit/AppKit.h> @@ -335,9 +336,9 @@ void QCocoaGLContext::doneCurrent() [NSOpenGLContext clearCurrentContext]; } -void (*QCocoaGLContext::getProcAddress(const QByteArray &procName))() +QFunctionPointer QCocoaGLContext::getProcAddress(const char *procName) { - return qcgl_getProcAddress(procName); + return (QFunctionPointer)dlsym(RTLD_DEFAULT, procName); } void QCocoaGLContext::update() diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h index 766561a264..ec2f7f8cf1 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.h +++ b/src/plugins/platforms/cocoa/qcocoahelpers.h @@ -57,6 +57,8 @@ QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcQpaCocoaWindow) + class QPixmap; class QString; diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm index b6b1862d80..c91c67fe79 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.mm +++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm @@ -77,6 +77,8 @@ QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcQpaCocoaWindow, "qt.qpa.cocoa.window"); + // // Conversion Functions // @@ -766,11 +768,11 @@ CGContextRef qt_mac_cg_context(QPaintDevice *pdev) if (data && data->classId() == QPlatformPixmap::RasterClass) { image = data->buffer(); } else { - qDebug() << "qt_mac_cg_context: Unsupported pixmap class"; + qDebug("qt_mac_cg_context: Unsupported pixmap class"); } } else if (pdev->devType() == QInternal::Widget) { // TODO test: image = static_cast<QImage *>(static_cast<const QWidget *>(pdev)->backingStore()->paintDevice()); - qDebug() << "qt_mac_cg_context: not implemented: Widget class"; + qDebug("qt_mac_cg_context: not implemented: Widget class"); } if (!image) diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index f98ddbb14f..c021a551a7 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -428,14 +428,18 @@ void QCocoaIntegration::updateScreens() } siblings << screen; } + + // Set virtual siblings list. All screens in mScreens are siblings, because we ignored the + // mirrors. Note that some of the screens we update the siblings list for here may be deleted + // below, but update anyway to keep the to-be-deleted screens out of the siblings list. + foreach (QCocoaScreen* screen, mScreens) + screen->setVirtualSiblings(siblings); + // Now the leftovers in remainingScreens are no longer current, so we can delete them. foreach (QCocoaScreen* screen, remainingScreens) { mScreens.removeOne(screen); destroyScreen(screen); } - // All screens in mScreens are siblings, because we ignored the mirrors. - foreach (QCocoaScreen* screen, mScreens) - screen->setVirtualSiblings(siblings); } QCocoaScreen *QCocoaIntegration::screenAtIndex(int index) diff --git a/src/plugins/platforms/cocoa/qcocoamenu.h b/src/plugins/platforms/cocoa/qcocoamenu.h index 64299c6cd2..64eeabcc2d 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.h +++ b/src/plugins/platforms/cocoa/qcocoamenu.h @@ -81,8 +81,6 @@ public: inline NSMenu *nsMenu() const { return m_nativeMenu; } - inline NSMenuItem *nsMenuItem() const - { return m_nativeItem; } inline bool isVisible() const { return m_visible; } @@ -91,11 +89,9 @@ public: QList<QCocoaMenuItem *> items() const; QList<QCocoaMenuItem *> merged() const; - void setMenuBar(QCocoaMenuBar *menuBar); - QCocoaMenuBar *menuBar() const; - void setContainingMenuItem(QCocoaMenuItem *menuItem); - QCocoaMenuItem *containingMenuItem() const; + void setAttachedItem(NSMenuItem *item); + NSMenuItem *attachedItem() const; private: QCocoaMenuItem *itemOrNull(int index) const; @@ -103,13 +99,10 @@ private: QList<QCocoaMenuItem *> m_menuItems; NSMenu *m_nativeMenu; - NSMenuItem *m_nativeItem; - NSObject *m_delegate; + NSMenuItem *m_attachedItem; bool m_enabled; bool m_visible; quintptr m_tag; - QCocoaMenuBar *m_menuBar; - QCocoaMenuItem *m_containingMenuItem; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoamenu.mm b/src/plugins/platforms/cocoa/qcocoamenu.mm index 8c576c7cbe..3fc98c071f 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.mm +++ b/src/plugins/platforms/cocoa/qcocoamenu.mm @@ -102,6 +102,28 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QCocoaMenuDelegate); return self; } +- (NSInteger)numberOfItemsInMenu:(NSMenu *)menu +{ + Q_ASSERT(m_menu->nsMenu() == menu); + return m_menu->items().count(); +} + +- (BOOL)menu:(NSMenu *)menu updateItem:(NSMenuItem *)item atIndex:(NSInteger)index shouldCancel:(BOOL)shouldCancel +{ + Q_UNUSED(index); + Q_ASSERT(m_menu->nsMenu() == menu); + if (shouldCancel) { + // TODO detach all submenus + return NO; + } + + QCocoaMenuItem *menuItem = reinterpret_cast<QCocoaMenuItem *>(item.tag); + if (m_menu->items().contains(menuItem)) { + if (QCocoaMenu *itemSubmenu = menuItem->menu()) + itemSubmenu->setAttachedItem(item); + } + return YES; +} - (void)menu:(NSMenu*)menu willHighlightItem:(NSMenuItem*)item { @@ -234,20 +256,16 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QCocoaMenuDelegate); QT_BEGIN_NAMESPACE QCocoaMenu::QCocoaMenu() : + m_attachedItem(0), m_enabled(true), m_visible(true), - m_tag(0), - m_menuBar(0), - m_containingMenuItem(0) + m_tag(0) { QMacAutoReleasePool pool; - m_delegate = [[QCocoaMenuDelegate alloc] initWithMenu:this]; - m_nativeItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; m_nativeMenu = [[NSMenu alloc] initWithTitle:@"Untitled"]; [m_nativeMenu setAutoenablesItems:YES]; - m_nativeMenu.delegate = (QCocoaMenuDelegate *) m_delegate; - [m_nativeItem setSubmenu:m_nativeMenu]; + m_nativeMenu.delegate = [[QCocoaMenuDelegate alloc] initWithMenu:this]; } QCocoaMenu::~QCocoaMenu() @@ -257,14 +275,11 @@ QCocoaMenu::~QCocoaMenu() SET_COCOA_MENU_ANCESTOR(item, 0); } - if (m_containingMenuItem) - m_containingMenuItem->clearMenu(this); - QMacAutoReleasePool pool; - [m_nativeItem setSubmenu:nil]; + NSObject *delegate = m_nativeMenu.delegate; + m_nativeMenu.delegate = nil; + [delegate release]; [m_nativeMenu release]; - [m_delegate release]; - [m_nativeItem release]; } void QCocoaMenu::setText(const QString &text) @@ -272,7 +287,6 @@ void QCocoaMenu::setText(const QString &text) QMacAutoReleasePool pool; QString stripped = qt_mac_removeAmpersandEscapes(text); [m_nativeMenu setTitle:QCFString::toNSString(stripped)]; - [m_nativeItem setTitle:QCFString::toNSString(stripped)]; } void QCocoaMenu::setMinimumWidth(int width) @@ -313,17 +327,13 @@ void QCocoaMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem * void QCocoaMenu::insertNative(QCocoaMenuItem *item, QCocoaMenuItem *beforeItem) { - [item->nsItem() setTarget:m_delegate]; + item->nsItem().target = m_nativeMenu.delegate; if (!item->menu()) [item->nsItem() setAction:@selector(itemFired:)]; if (item->isMerged()) return; - if ([item->nsItem() menu]) { - qWarning("Menu item is already in a menu, remove it from the other menu first before inserting"); - return; - } // if the item we're inserting before is merged, skip along until // we find a non-merged real item to insert ahead of. while (beforeItem && beforeItem->isMerged()) { @@ -451,12 +461,11 @@ void QCocoaMenu::setEnabled(bool enabled) bool QCocoaMenu::isEnabled() const { - return [m_nativeItem isEnabled]; + return m_attachedItem ? [m_attachedItem isEnabled] : m_enabled; } void QCocoaMenu::setVisible(bool visible) { - [m_nativeItem setSubmenu:(visible ? m_nativeMenu : nil)]; m_visible = visible; } @@ -593,8 +602,6 @@ void QCocoaMenu::syncModalState(bool modal) if (!m_enabled) modal = true; - [m_nativeItem setEnabled:!modal]; - foreach (QCocoaMenuItem *item, m_menuItems) { if (item->menu()) { // recurse into submenus item->menu()->syncModalState(modal); @@ -605,25 +612,24 @@ void QCocoaMenu::syncModalState(bool modal) } } -void QCocoaMenu::setMenuBar(QCocoaMenuBar *menuBar) +void QCocoaMenu::setAttachedItem(NSMenuItem *item) { - m_menuBar = menuBar; - SET_COCOA_MENU_ANCESTOR(this, menuBar); -} + if (item == m_attachedItem) + return; -QCocoaMenuBar *QCocoaMenu::menuBar() const -{ - return m_menuBar; -} + if (m_attachedItem) + m_attachedItem.submenu = nil; + + m_attachedItem = item; + + if (m_attachedItem) + m_attachedItem.submenu = m_nativeMenu; -void QCocoaMenu::setContainingMenuItem(QCocoaMenuItem *menuItem) -{ - m_containingMenuItem = menuItem; } -QCocoaMenuItem *QCocoaMenu::containingMenuItem() const +NSMenuItem *QCocoaMenu::attachedItem() const { - return m_containingMenuItem; + return m_attachedItem; } QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoamenubar.h b/src/plugins/platforms/cocoa/qcocoamenubar.h index 0521bcd430..7ce2059450 100644 --- a/src/plugins/platforms/cocoa/qcocoamenubar.h +++ b/src/plugins/platforms/cocoa/qcocoamenubar.h @@ -77,10 +77,10 @@ private: static QCocoaMenuBar *findGlobalMenubar(); bool shouldDisable(QCocoaWindow *active) const; - void insertNativeMenu(QCocoaMenu *menu, QCocoaMenu *beforeMenu); - void removeNativeMenu(QCocoaMenu *menu); - QList<QCocoaMenu*> m_menus; + NSMenuItem *nativeItemForMenu(QCocoaMenu *menu) const; + + QList<QPointer<QCocoaMenu> > m_menus; NSMenu *m_nativeMenu; QCocoaWindow *m_window; }; diff --git a/src/plugins/platforms/cocoa/qcocoamenubar.mm b/src/plugins/platforms/cocoa/qcocoamenubar.mm index c9a42e9d8e..e8b3823012 100644 --- a/src/plugins/platforms/cocoa/qcocoamenubar.mm +++ b/src/plugins/platforms/cocoa/qcocoamenubar.mm @@ -57,7 +57,6 @@ static inline QCocoaMenuLoader *getMenuLoader() return [NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)]; } - QCocoaMenuBar::QCocoaMenuBar() : m_window(0) { @@ -74,11 +73,20 @@ QCocoaMenuBar::~QCocoaMenuBar() #ifdef QT_COCOA_ENABLE_MENU_DEBUG qDebug() << "~QCocoaMenuBar" << this; #endif + foreach (QCocoaMenu *menu, m_menus) { + if (!menu) + continue; + NSMenuItem *item = nativeItemForMenu(menu); + if (menu->attachedItem() == item) + menu->setAttachedItem(nil); + } + [m_nativeMenu release]; static_menubars.removeOne(this); if (m_window && m_window->menubar() == this) { m_window->setMenubar(0); + // Delete the children first so they do not cause // the native menu items to be hidden after // the menu bar was updated @@ -87,24 +95,6 @@ QCocoaMenuBar::~QCocoaMenuBar() } } -void QCocoaMenuBar::insertNativeMenu(QCocoaMenu *menu, QCocoaMenu *beforeMenu) -{ - QMacAutoReleasePool pool; - - if (beforeMenu) { - NSUInteger nativeIndex = [m_nativeMenu indexOfItem:beforeMenu->nsMenuItem()]; - [m_nativeMenu insertItem: menu->nsMenuItem() atIndex: nativeIndex]; - } else { - [m_nativeMenu addItem: menu->nsMenuItem()]; - } - - menu->setMenuBar(this); - syncMenu(static_cast<QPlatformMenu *>(menu)); - if (menu->isVisible()) { - [m_nativeMenu setSubmenu: menu->nsMenu() forItem: menu->nsMenuItem()]; - } -} - void QCocoaMenuBar::insertMenu(QPlatformMenu *platformMenu, QPlatformMenu *before) { QCocoaMenu *menu = static_cast<QCocoaMenu *>(platformMenu); @@ -113,31 +103,40 @@ void QCocoaMenuBar::insertMenu(QPlatformMenu *platformMenu, QPlatformMenu *befor qDebug() << "QCocoaMenuBar" << this << "insertMenu" << menu << "before" << before; #endif - if (m_menus.contains(menu)) { + if (m_menus.contains(QPointer<QCocoaMenu>(menu))) { qWarning("This menu already belongs to the menubar, remove it first"); return; } - if (beforeMenu && !m_menus.contains(beforeMenu)) { + if (beforeMenu && !m_menus.contains(QPointer<QCocoaMenu>(beforeMenu))) { qWarning("The before menu does not belong to the menubar"); return; } - m_menus.insert(beforeMenu ? m_menus.indexOf(beforeMenu) : m_menus.size(), menu); - if (!menu->menuBar()) - insertNativeMenu(menu, beforeMenu); - if (m_window && m_window->window()->isActive()) - updateMenuBarImmediately(); -} + int insertionIndex = beforeMenu ? m_menus.indexOf(beforeMenu) : m_menus.size(); + m_menus.insert(insertionIndex, menu); + + { + QMacAutoReleasePool pool; + NSMenuItem *item = [[[NSMenuItem alloc] init] autorelease]; + item.tag = reinterpret_cast<NSInteger>(menu); + + if (beforeMenu) { + // QMenuBar::toNSMenu() exposes the native menubar and + // the user could have inserted its own items in there. + // Same remark applies to removeMenu(). + NSMenuItem *beforeItem = nativeItemForMenu(beforeMenu); + NSInteger nativeIndex = [m_nativeMenu indexOfItem:beforeItem]; + [m_nativeMenu insertItem:item atIndex:nativeIndex]; + } else { + [m_nativeMenu addItem:item]; + } + } -void QCocoaMenuBar::removeNativeMenu(QCocoaMenu *menu) -{ - QMacAutoReleasePool pool; + syncMenu(menu); - if (menu->menuBar() == this) - menu->setMenuBar(0); - NSUInteger realIndex = [m_nativeMenu indexOfItem:menu->nsMenuItem()]; - [m_nativeMenu removeItemAtIndex: realIndex]; + if (m_window && m_window->window()->isActive()) + updateMenuBarImmediately(); } void QCocoaMenuBar::removeMenu(QPlatformMenu *platformMenu) @@ -147,8 +146,17 @@ void QCocoaMenuBar::removeMenu(QPlatformMenu *platformMenu) qWarning("Trying to remove a menu that does not belong to the menubar"); return; } + + NSMenuItem *item = nativeItemForMenu(menu); + if (menu->attachedItem() == item) + menu->setAttachedItem(nil); m_menus.removeOne(menu); - removeNativeMenu(menu); + + QMacAutoReleasePool pool; + + // See remark in insertMenu(). + NSInteger nativeIndex = [m_nativeMenu indexOfItem:item]; + [m_nativeMenu removeItemAtIndex:nativeIndex]; } void QCocoaMenuBar::syncMenu(QPlatformMenu *menu) @@ -170,7 +178,16 @@ void QCocoaMenuBar::syncMenu(QPlatformMenu *menu) break; } } - [cocoaMenu->nsMenuItem() setHidden:shouldHide]; + + nativeItemForMenu(cocoaMenu).hidden = shouldHide; +} + +NSMenuItem *QCocoaMenuBar::nativeItemForMenu(QCocoaMenu *menu) const +{ + if (!menu) + return nil; + + return [m_nativeMenu itemWithTag:reinterpret_cast<NSInteger>(menu)]; } void QCocoaMenuBar::handleReparent(QWindow *newParentWindow) @@ -297,24 +314,16 @@ void QCocoaMenuBar::updateMenuBarImmediately() qDebug() << "QCocoaMenuBar" << "updateMenuBarImmediately" << cw; #endif bool disableForModal = mb->shouldDisable(cw); - // force a sync? - foreach (QCocoaMenu *m, mb->m_menus) { - mb->syncMenu(m); - m->syncModalState(disableForModal); - } - // reparent shared menu items if necessary. - // We browse the list in reverse order to be sure that the next items are redrawn before the current ones, - // in this way we are sure that "beforeMenu" (see below) is part of the native menu before "m" is redraw - for (int i = mb->m_menus.size() - 1; i >= 0; i--) { - QCocoaMenu *m = mb->m_menus.at(i); - QCocoaMenuBar *menuBar = m->menuBar(); - if (menuBar != mb) { - QCocoaMenu *beforeMenu = i < (mb->m_menus.size() - 1) ? mb->m_menus.at(i + 1) : 0; - if (menuBar) - menuBar->removeNativeMenu(m); - mb->insertNativeMenu(m, beforeMenu); - } + foreach (QCocoaMenu *menu, mb->m_menus) { + if (!menu) + continue; + NSMenuItem *item = mb->nativeItemForMenu(menu); + menu->setAttachedItem(item); + SET_COCOA_MENU_ANCESTOR(menu, mb); + // force a sync? + mb->syncMenu(menu); + menu->syncModalState(disableForModal); } QCocoaMenuLoader *loader = getMenuLoader(); diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.h b/src/plugins/platforms/cocoa/qcocoamenuitem.h index 0d6f67959b..bba9ce3963 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuitem.h +++ b/src/plugins/platforms/cocoa/qcocoamenuitem.h @@ -93,7 +93,6 @@ public: inline bool isSeparator() const { return m_isSeparator; } QCocoaMenu *menu() const { return m_menu; } - void clearMenu(QCocoaMenu *menu); MenuRole effectiveRole() const; private: @@ -105,7 +104,7 @@ private: QString m_text; bool m_textSynced; QIcon m_icon; - QCocoaMenu *m_menu; + QPointer<QCocoaMenu> m_menu; bool m_isVisible; bool m_enabled; bool m_isSeparator; diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.mm b/src/plugins/platforms/cocoa/qcocoamenuitem.mm index 9e4f0b9ad1..de0271ce4d 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuitem.mm +++ b/src/plugins/platforms/cocoa/qcocoamenuitem.mm @@ -142,15 +142,12 @@ void QCocoaMenuItem::setMenu(QPlatformMenu *menu) if (m_menu) { if (COCOA_MENU_ANCESTOR(m_menu) == this) SET_COCOA_MENU_ANCESTOR(m_menu, 0); - if (m_menu->containingMenuItem() == this) - m_menu->setContainingMenuItem(0); } QMacAutoReleasePool pool; m_menu = static_cast<QCocoaMenu *>(menu); if (m_menu) { SET_COCOA_MENU_ANCESTOR(m_menu, this); - m_menu->setContainingMenuItem(this); } else { // we previously had a menu, but no longer // clear out our item so the nexy sync() call builds a new one @@ -159,12 +156,6 @@ void QCocoaMenuItem::setMenu(QPlatformMenu *menu) } } -void QCocoaMenuItem::clearMenu(QCocoaMenu *menu) -{ - if (menu == m_menu) - m_menu = 0; -} - void QCocoaMenuItem::setVisible(bool isVisible) { m_isVisible = isVisible; @@ -226,14 +217,6 @@ NSMenuItem *QCocoaMenuItem::sync() m_native = nil; } - if (m_menu) { - if (m_native != m_menu->nsMenuItem()) { - [m_native release]; - m_native = [m_menu->nsMenuItem() retain]; - [m_native setTag:reinterpret_cast<NSInteger>(this)]; - } - } - if ((m_role != NoRole && !m_textSynced) || m_merged) { NSMenuItem *mergeItem = nil; QCocoaMenuLoader *loader = getMenuLoader(); diff --git a/src/plugins/platforms/cocoa/qcocoasystemsettings.mm b/src/plugins/platforms/cocoa/qcocoasystemsettings.mm index 7fd5537215..be5fa61b8b 100644 --- a/src/plugins/platforms/cocoa/qcocoasystemsettings.mm +++ b/src/plugins/platforms/cocoa/qcocoasystemsettings.mm @@ -153,6 +153,7 @@ static QMacPaletteMap mac_widget_colors[] = { QMacPaletteMap(QPlatformTheme::LabelPalette, kThemeTextColorPlacardActive, kThemeTextColorPlacardInactive), QMacPaletteMap(QPlatformTheme::GroupBoxPalette, kThemeTextColorPlacardActive, kThemeTextColorPlacardInactive), QMacPaletteMap(QPlatformTheme::MenuPalette, kThemeTextColorMenuItemActive, kThemeTextColorMenuItemDisabled), + QMacPaletteMap(QPlatformTheme::MenuBarPalette, kThemeTextColorMenuItemActive, kThemeTextColorMenuItemDisabled), //### TODO: The zeros below gives white-on-black text. QMacPaletteMap(QPlatformTheme::TextEditPalette, 0, 0), QMacPaletteMap(QPlatformTheme::TextLineEditPalette, 0, 0), @@ -177,7 +178,8 @@ QHash<QPlatformTheme::Palette, QPalette*> qt_mac_createRolePalettes() pal.setColor(QPalette::Disabled, QPalette::WindowText, qc); pal.setColor(QPalette::Disabled, QPalette::HighlightedText, qc); } - if (mac_widget_colors[i].paletteRole == QPlatformTheme::MenuPalette) { + if (mac_widget_colors[i].paletteRole == QPlatformTheme::MenuPalette + || mac_widget_colors[i].paletteRole == QPlatformTheme::MenuBarPalette) { pal.setBrush(QPalette::Background, qt_mac_brushForTheme(kThemeBrushMenuBackground)); qc = qt_mac_colorForThemeTextColor(kThemeTextColorMenuItemActive); pal.setBrush(QPalette::ButtonText, qc); diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 9572b5ac4c..01e72303be 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -376,9 +376,8 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw) , m_bottomContentBorderThickness(0) , m_normalGeometry(QRect(0,0,-1,-1)) { -#ifdef QT_COCOA_ENABLE_WINDOW_DEBUG - qDebug() << "QCocoaWindow::QCocoaWindow" << this; -#endif + qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::QCocoaWindow" << window(); + QMacAutoReleasePool pool; if (tlw->type() == Qt::ForeignWindow) { @@ -411,11 +410,10 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw) QCocoaWindow::~QCocoaWindow() { -#ifdef QT_COCOA_ENABLE_WINDOW_DEBUG - qDebug() << "QCocoaWindow::~QCocoaWindow" << this; -#endif + qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::~QCocoaWindow" << window(); QMacAutoReleasePool pool; + [m_nsWindow makeFirstResponder:nil]; [m_nsWindow setContentView:nil]; [m_nsWindow.helper detachFromPlatformWindow]; if (m_isNSWindowChild) { @@ -470,6 +468,8 @@ QSurfaceFormat QCocoaWindow::format() const void QCocoaWindow::setGeometry(const QRect &rectIn) { + qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::setGeometry" << window() << rectIn; + QBoolBlocker inSetGeometry(m_inSetGeometry, true); QRect rect = rectIn; @@ -482,9 +482,7 @@ void QCocoaWindow::setGeometry(const QRect &rectIn) } if (geometry() == rect) return; -#ifdef QT_COCOA_ENABLE_WINDOW_DEBUG - qDebug() << "QCocoaWindow::setGeometry" << this << rect; -#endif + setCocoaGeometry(rect); } @@ -507,6 +505,7 @@ QRect QCocoaWindow::geometry() const void QCocoaWindow::setCocoaGeometry(const QRect &rect) { + qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::setCocoaGeometry" << window() << rect; QMacAutoReleasePool pool; if (m_contentViewIsEmbedded) { @@ -630,6 +629,8 @@ void QCocoaWindow::show(bool becauseOfAncestor) void QCocoaWindow::setVisible(bool visible) { + qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::setVisible" << window() << visible; + if (m_isNSWindowChild && m_hiddenByClipping) return; @@ -639,9 +640,7 @@ void QCocoaWindow::setVisible(bool visible) QCocoaWindow *parentCocoaWindow = 0; if (window()->transientParent()) parentCocoaWindow = static_cast<QCocoaWindow *>(window()->transientParent()->handle()); -#ifdef QT_COCOA_ENABLE_WINDOW_DEBUG - qDebug() << "QCocoaWindow::setVisible" << window() << visible; -#endif + if (visible) { // We need to recreate if the modality has changed as the style mask will need updating if (m_windowModality != window()->modality()) @@ -862,9 +861,6 @@ NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags) if (m_drawContentBorderGradient) styleMask |= NSTexturedBackgroundWindowMask; -#ifdef QT_COCOA_ENABLE_WINDOW_DEBUG - qDebug("windowStyleMask of '%s': flags %X -> styleMask %lX", qPrintable(window()->title()), (int)flags, styleMask); -#endif return styleMask; } @@ -986,7 +982,8 @@ bool QCocoaWindow::isAlertState() const void QCocoaWindow::raise() { - //qDebug() << "raise" << this; + qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::raise" << window(); + // ### handle spaces (see Qt 4 raise_sys in qwidget_mac.mm) if (!m_nsWindow) return; @@ -1006,7 +1003,15 @@ void QCocoaWindow::raise() [parentNSWindow removeChildWindow:m_nsWindow]; [parentNSWindow addChildWindow:m_nsWindow ordered:NSWindowAbove]; } else { - [m_nsWindow orderFront: m_nsWindow]; + { + // Clean up autoreleased temp objects from orderFront immediately. + // Failure to do so has been observed to cause leaks also beyond any outer + // autorelease pool (for example around a complete QWindow + // construct-show-raise-hide-delete cyle), counter to expected autoreleasepool + // behavior. + QMacAutoReleasePool pool; + [m_nsWindow orderFront: m_nsWindow]; + } static bool raiseProcess = qt_mac_resolveOption(true, "QT_MAC_SET_RAISE_PROCESS"); if (raiseProcess) { ProcessSerialNumber psn; @@ -1019,6 +1024,7 @@ void QCocoaWindow::raise() void QCocoaWindow::lower() { + qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::lower" << window(); if (!m_nsWindow) return; if (m_isNSWindowChild) { @@ -1071,13 +1077,11 @@ void QCocoaWindow::propagateSizeHints() if (!m_nsWindow) return; -#ifdef QT_COCOA_ENABLE_WINDOW_DEBUG - qDebug() << "QCocoaWindow::propagateSizeHints" << this; - qDebug() << " min/max" << windowMinimumSize() << windowMaximumSize(); - qDebug() << "size increment" << windowSizeIncrement(); - qDebug() << " basesize" << windowBaseSize(); - qDebug() << " geometry" << windowGeometry(); -#endif + qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::propagateSizeHints" << window() << "\n" + << " min/max" << windowMinimumSize() << windowMaximumSize() + << "size increment" << windowSizeIncrement() + << " basesize" << windowBaseSize() + << " geometry" << windowGeometry(); // Set the minimum content size. const QSize minimumSize = windowMinimumSize(); @@ -1109,6 +1113,7 @@ void QCocoaWindow::propagateSizeHints() void QCocoaWindow::setOpacity(qreal level) { + qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::setOpacity" << level; if (m_nsWindow) { [m_nsWindow setAlphaValue:level]; [m_nsWindow setOpaque: isOpaque()]; @@ -1117,6 +1122,7 @@ void QCocoaWindow::setOpacity(qreal level) void QCocoaWindow::setMask(const QRegion ®ion) { + qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::setMask" << window() << region; if (m_nsWindow) [m_nsWindow setBackgroundColor:[NSColor clearColor]]; @@ -1126,6 +1132,7 @@ void QCocoaWindow::setMask(const QRegion ®ion) bool QCocoaWindow::setKeyboardGrabEnabled(bool grab) { + qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::setKeyboardGrabEnabled" << window() << grab; if (!m_nsWindow) return false; @@ -1138,6 +1145,7 @@ bool QCocoaWindow::setKeyboardGrabEnabled(bool grab) bool QCocoaWindow::setMouseGrabEnabled(bool grab) { + qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::setMouseGrabEnabled" << window() << grab; if (!m_nsWindow) return false; @@ -1155,6 +1163,8 @@ WId QCocoaWindow::winId() const void QCocoaWindow::setParent(const QPlatformWindow *parentWindow) { + qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::setParent" << window() << (parentWindow ? parentWindow->window() : 0); + // recreate the window for compatibility bool unhideAfterRecreate = parentWindow && !m_contentViewIsToBeEmbedded && ![m_contentView isHidden]; recreateWindow(parentWindow); @@ -1238,6 +1248,7 @@ void QCocoaWindow::windowDidEndLiveResize() bool QCocoaWindow::windowShouldClose() { + qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::windowShouldClose" << window(); // This callback should technically only determine if the window // should (be allowed to) close, but since our QPA API to determine // that also involves actually closing the window we do both at the @@ -1278,6 +1289,9 @@ QCocoaGLContext *QCocoaWindow::currentContext() const void QCocoaWindow::recreateWindow(const QPlatformWindow *parentWindow) { + qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::recreateWindow" << window() + << "parent" << (parentWindow ? parentWindow->window() : 0); + bool wasNSWindowChild = m_isNSWindowChild; BOOL requestNSWindowChild = qt_mac_resolveOption(NO, window(), "_q_platform_MacUseNSWindow", "QT_MAC_USE_NSWINDOW"); @@ -1614,10 +1628,6 @@ void QCocoaWindow::syncWindowState(Qt::WindowState newState) } } -#ifdef QT_COCOA_ENABLE_WINDOW_DEBUG - qDebug() << "QCocoaWindow::syncWindowState" << newState << "actual" << predictedState << "was" << m_synchedWindowState << "effectively maximized" << m_effectivelyMaximized; -#endif - // New state is now the current synched state m_synchedWindowState = predictedState; } @@ -1810,7 +1820,9 @@ void QCocoaWindow::exposeWindow() m_isExposed = true; m_exposedGeometry = geometry(); m_exposedDevicePixelRatio = devicePixelRatio(); - QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), m_exposedGeometry.size())); + QRect geometry(QPoint(0, 0), m_exposedGeometry.size()); + qCDebug(lcQpaCocoaWindow) << "QCocoaWindow: exposeWindow" << window() << geometry; + QWindowSystemInterface::handleExposeEvent(window(), geometry); } } @@ -1820,6 +1832,8 @@ void QCocoaWindow::obscureWindow() if (m_isExposed) { m_geometryUpdateExposeAllowed = false; m_isExposed = false; + + qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::obscureWindow" << window(); QWindowSystemInterface::handleExposeEvent(window(), QRegion()); } } @@ -1846,7 +1860,10 @@ void QCocoaWindow::updateExposedGeometry() m_isExposed = true; m_exposedGeometry = geometry(); m_exposedDevicePixelRatio = devicePixelRatio(); - QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), m_exposedGeometry.size())); + + QRect geometry(QPoint(0, 0), m_exposedGeometry.size()); + qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::updateExposedGeometry" << window() << geometry; + QWindowSystemInterface::handleExposeEvent(window(), geometry); } QWindow *QCocoaWindow::childWindowAt(QPoint windowPoint) diff --git a/src/plugins/platforms/cocoa/qnsview.h b/src/plugins/platforms/cocoa/qnsview.h index cbd92df9cf..b5738abf4c 100644 --- a/src/plugins/platforms/cocoa/qnsview.h +++ b/src/plugins/platforms/cocoa/qnsview.h @@ -43,6 +43,7 @@ #include <AppKit/AppKit.h> #include <QtCore/QPointer> +#include <QtCore/QSet> #include <QtGui/QImage> #include <QtGui/QAccessible> @@ -59,6 +60,7 @@ Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper)); @interface QT_MANGLE_NAMESPACE(QNSView) : NSView <NSTextInputClient> { QCocoaBackingStore* m_backingStore; QPoint m_backingStoreOffset; + QRegion m_maskRegion; CGImageRef m_maskImage; uchar *m_maskData; bool m_shouldInvalidateWindowShadow; @@ -66,6 +68,7 @@ Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper)); QCocoaWindow *m_platformWindow; NSTrackingArea *m_trackingArea; Qt::MouseButtons m_buttons; + Qt::MouseButtons m_acceptedMouseDowns; Qt::MouseButtons m_frameStrutButtons; QString m_composingText; bool m_sendKeyEvent; @@ -85,6 +88,7 @@ Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper)); bool m_exposedOnMoveToWindow; NSEvent *m_currentlyInterpretedKeyEvent; bool m_isMenuView; + QSet<quint32> m_acceptedKeyDowns; } - (id)init; @@ -118,6 +122,9 @@ Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper)); - (void)resetMouseButtons; - (void)handleMouseEvent:(NSEvent *)theEvent; +- (bool)handleMouseDownEvent:(NSEvent *)theEvent; +- (bool)handleMouseDraggedEvent:(NSEvent *)theEvent; +- (bool)handleMouseUpEvent:(NSEvent *)theEvent; - (void)mouseDown:(NSEvent *)theEvent; - (void)mouseDragged:(NSEvent *)theEvent; - (void)mouseUp:(NSEvent *)theEvent; @@ -139,7 +146,7 @@ Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper)); - (int) convertKeyCode : (QChar)keyCode; + (Qt::KeyboardModifiers) convertKeyModifiers : (ulong)modifierFlags; -- (void)handleKeyEvent:(NSEvent *)theEvent eventType:(int)eventType; +- (bool)handleKeyEvent:(NSEvent *)theEvent eventType:(int)eventType; - (void)keyDown:(NSEvent *)theEvent; - (void)keyUp:(NSEvent *)theEvent; diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index 360ad3baf7..bb12c30fa3 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -145,6 +145,7 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; m_shouldInvalidateWindowShadow = false; m_window = 0; m_buttons = Qt::NoButton; + m_acceptedMouseDowns = Qt::NoButton; m_frameStrutButtons = Qt::NoButton; m_sendKeyEvent = false; m_subscribesForGlobalFrameNotifications = false; @@ -361,15 +362,16 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; 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; -#ifdef QT_COCOA_ENABLE_WINDOW_DEBUG - qDebug() << "QNSView::udpateGeometry" << m_platformWindow << geometry; -#endif + qCDebug(lcQpaCocoaWindow) << "[QNSView udpateGeometry:]" << m_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 @@ -390,7 +392,7 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; // calles, which Qt and Qt applications do not excpect. if (!m_platformWindow->m_inSetGeometry) QWindowSystemInterface::flushWindowSystemEvents(); - else + else if (isResize) m_backingStore = 0; } } @@ -512,6 +514,8 @@ QT_WARNING_POP - (void) flushBackingStore:(QCocoaBackingStore *)backingStore region:(const QRegion &)region offset:(QPoint)offset { + qCDebug(lcQpaCocoaWindow) << "[QNSView flushBackingStore:]" << m_window << region.rectCount() << region.boundingRect() << offset; + m_backingStore = backingStore; m_backingStoreOffset = offset * m_backingStore->getBackingStoreDevicePixelRatio(); for (const QRect &rect : region) @@ -526,7 +530,7 @@ QT_WARNING_POP - (BOOL) hasMask { - return m_maskImage != 0; + return !m_maskRegion.isEmpty(); } - (BOOL) isOpaque @@ -539,6 +543,7 @@ QT_WARNING_POP - (void) setMaskRegion:(const QRegion *)region { m_shouldInvalidateWindowShadow = true; + m_maskRegion = *region; if (m_maskImage) CGImageRelease(m_maskImage); if (region->isEmpty()) { @@ -574,6 +579,8 @@ QT_WARNING_POP - (void) drawRect:(NSRect)dirtyRect { + qCDebug(lcQpaCocoaWindow) << "[QNSView drawRect:]" << m_window << qt_mac_toQRect(dirtyRect); + #ifndef QT_NO_OPENGL if (m_glContext && m_shouldSetGLContextinDrawRect) { [m_glContext->nsOpenGLContext() setView:self]; @@ -813,6 +820,82 @@ QT_WARNING_POP QWindowSystemInterface::handleFrameStrutMouseEvent(m_window, timestamp, qtWindowPoint, qtScreenPoint, m_frameStrutButtons); } +- (bool)handleMouseDownEvent:(NSEvent *)theEvent +{ + if (m_window && (m_window->flags() & Qt::WindowTransparentForInput)) + return false; + + Qt::MouseButton button = cocoaButton2QtButton([theEvent 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 = 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; + + if (button == Qt::RightButton) + m_sendUpAsRightButton = true; + + m_buttons |= button; + + [self handleMouseEvent:theEvent]; + return true; +} + +- (bool)handleMouseDraggedEvent:(NSEvent *)theEvent +{ + if (m_window && (m_window->flags() & Qt::WindowTransparentForInput)) + return false; + + Qt::MouseButton button = cocoaButton2QtButton([theEvent 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_buttons & (m_sendUpAsRightButton ? Qt::RightButton : Qt::LeftButton))) { + qCWarning(lcQpaCocoaWindow) << "QNSView mouseDragged: Internal mouse button tracking" + << "invalid (missing Qt::LeftButton)"; + } + + [self handleMouseEvent:theEvent]; + return true; +} + +- (bool)handleMouseUpEvent:(NSEvent *)theEvent +{ + if (m_window && (m_window->flags() & Qt::WindowTransparentForInput)) + return false; + + Qt::MouseButton button = cocoaButton2QtButton([theEvent 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 (m_window && (m_window->flags() & Qt::WindowTransparentForInput) ) @@ -851,6 +934,25 @@ QT_WARNING_POP } } + QPointF qtWindowPoint; + QPointF qtScreenPoint; + [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint]; + Q_UNUSED(qtScreenPoint); + + bool masked = 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 { @@ -866,24 +968,58 @@ QT_WARNING_POP - (void)mouseDragged:(NSEvent *)theEvent { - if (m_window && (m_window->flags() & Qt::WindowTransparentForInput) ) - return [super mouseDragged:theEvent]; - if (!(m_buttons & (m_sendUpAsRightButton ? Qt::RightButton : Qt::LeftButton))) - qWarning("QNSView mouseDragged: Internal mouse button tracking invalid (missing Qt::LeftButton)"); - [self handleMouseEvent:theEvent]; + const bool accepted = [self handleMouseDraggedEvent:theEvent]; + if (!accepted) + [super mouseDragged:theEvent]; } - (void)mouseUp:(NSEvent *)theEvent { - if (m_window && (m_window->flags() & Qt::WindowTransparentForInput) ) - return [super mouseUp:theEvent]; - if (m_sendUpAsRightButton) { - m_buttons &= ~Qt::RightButton; - m_sendUpAsRightButton = false; - } else { - m_buttons &= ~Qt::LeftButton; - } - [self handleMouseEvent:theEvent]; + const bool accepted = [self handleMouseUpEvent:theEvent]; + if (!accepted) + [super mouseUp:theEvent]; +} + +- (void)rightMouseDown:(NSEvent *)theEvent +{ + const bool accepted = [self handleMouseDownEvent:theEvent]; + if (!accepted) + [super rightMouseDown:theEvent]; +} + +- (void)rightMouseDragged:(NSEvent *)theEvent +{ + const bool accepted = [self handleMouseDraggedEvent:theEvent]; + if (!accepted) + [super rightMouseDragged:theEvent]; +} + +- (void)rightMouseUp:(NSEvent *)theEvent +{ + const bool accepted = [self handleMouseUpEvent:theEvent]; + if (!accepted) + [super rightMouseUp:theEvent]; +} + +- (void)otherMouseDown:(NSEvent *)theEvent +{ + const bool accepted = [self handleMouseDownEvent:theEvent]; + if (!accepted) + [super otherMouseDown:theEvent]; +} + +- (void)otherMouseDragged:(NSEvent *)theEvent +{ + const bool accepted = [self handleMouseDraggedEvent:theEvent]; + if (!accepted) + [super otherMouseDragged:theEvent]; +} + +- (void)otherMouseUp:(NSEvent *)theEvent +{ + const bool accepted = [self handleMouseUpEvent:theEvent]; + if (!accepted) + [super otherMouseUp:theEvent]; } - (void)updateTrackingAreas @@ -994,58 +1130,6 @@ QT_WARNING_POP m_platformWindow->m_enterLeaveTargetWindow = 0; } -- (void)rightMouseDown:(NSEvent *)theEvent -{ - if (m_window && (m_window->flags() & Qt::WindowTransparentForInput) ) - return [super rightMouseDown:theEvent]; - m_buttons |= Qt::RightButton; - m_sendUpAsRightButton = true; - [self handleMouseEvent:theEvent]; -} - -- (void)rightMouseDragged:(NSEvent *)theEvent -{ - if (m_window && (m_window->flags() & Qt::WindowTransparentForInput) ) - return [super rightMouseDragged:theEvent]; - if (!(m_buttons & Qt::RightButton)) - qWarning("QNSView rightMouseDragged: Internal mouse button tracking invalid (missing Qt::RightButton)"); - [self handleMouseEvent:theEvent]; -} - -- (void)rightMouseUp:(NSEvent *)theEvent -{ - if (m_window && (m_window->flags() & Qt::WindowTransparentForInput) ) - return [super rightMouseUp:theEvent]; - m_buttons &= ~Qt::RightButton; - m_sendUpAsRightButton = false; - [self handleMouseEvent:theEvent]; -} - -- (void)otherMouseDown:(NSEvent *)theEvent -{ - if (m_window && (m_window->flags() & Qt::WindowTransparentForInput) ) - return [super otherMouseDown:theEvent]; - m_buttons |= cocoaButton2QtButton([theEvent buttonNumber]); - [self handleMouseEvent:theEvent]; -} - -- (void)otherMouseDragged:(NSEvent *)theEvent -{ - if (m_window && (m_window->flags() & Qt::WindowTransparentForInput) ) - return [super otherMouseDragged:theEvent]; - if (!(m_buttons & ~(Qt::LeftButton | Qt::RightButton))) - qWarning("QNSView otherMouseDragged: Internal mouse button tracking invalid (missing Qt::MiddleButton or Qt::ExtraButton*)"); - [self handleMouseEvent:theEvent]; -} - -- (void)otherMouseUp:(NSEvent *)theEvent -{ - if (m_window && (m_window->flags() & Qt::WindowTransparentForInput) ) - return [super otherMouseUp:theEvent]; - m_buttons &= ~cocoaButton2QtButton([theEvent buttonNumber]); - [self handleMouseEvent:theEvent]; -} - struct QCocoaTabletDeviceData { QTabletEvent::TabletDevice device; @@ -1413,8 +1497,10 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) } 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_window, qt_timestamp, qt_windowPoint, qt_screenPoint, pixelDelta, angleDelta, currentWheelModifiers, ph, source); + QWindowSystemInterface::handleWheelEvent(m_window, qt_timestamp, qt_windowPoint, qt_screenPoint, pixelDelta, angleDelta, currentWheelModifiers, ph, source, isInverted); } #endif //QT_NO_WHEELEVENT @@ -1439,7 +1525,7 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) return qtMods; } -- (void)handleKeyEvent:(NSEvent *)nsevent eventType:(int)eventType +- (bool)handleKeyEvent:(NSEvent *)nsevent eventType:(int)eventType { ulong timestamp = [nsevent timestamp] * 1000; ulong nativeModifiers = [nsevent modifierFlags]; @@ -1509,26 +1595,46 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) m_sendKeyEvent = true; } - if (m_sendKeyEvent && m_composingText.isEmpty()) + 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 (m_window && (m_window->flags() & Qt::WindowTransparentForInput) ) return [super keyDown:nsevent]; - [self handleKeyEvent:nsevent eventType:int(QEvent::KeyPress)]; + + const bool accepted = [self handleKeyEvent:nsevent eventType:int(QEvent::KeyPress)]; + + // Track keyDown acceptance state for later acceptance of the keyUp. + if (accepted) + m_acceptedKeyDowns.insert([nsevent keyCode]); + + // Propagate the keyDown to the next responder if Qt did not accept it. + if (!accepted) + [super keyDown:nsevent]; } - (void)keyUp:(NSEvent *)nsevent { if (m_window && (m_window->flags() & Qt::WindowTransparentForInput) ) return [super keyUp:nsevent]; - [self handleKeyEvent:nsevent eventType:int(QEvent::KeyRelease)]; + + 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 diff --git a/src/plugins/platforms/cocoa/qt_mac_p.h b/src/plugins/platforms/cocoa/qt_mac_p.h index 18a3d1b3e5..03eae1b2e7 100644 --- a/src/plugins/platforms/cocoa/qt_mac_p.h +++ b/src/plugins/platforms/cocoa/qt_mac_p.h @@ -64,6 +64,7 @@ #include "QtCore/qvariant.h" #include "QtCore/qmimedata.h" #include "QtCore/qpointer.h" +#include "QtCore/qloggingcategory.h" #include "private/qcore_mac_p.h" |