From c7ed644fd1979b5a8c29fae1005d8a70801fab14 Mon Sep 17 00:00:00 2001 From: Gabriel de Dietrich Date: Tue, 1 Apr 2014 18:27:59 +0200 Subject: QMenu: Add support for QWidgetAction on Mac MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The implementation follows a similar logic as in Qt 4 making sure we properly interface with the QPA backend. We also make sure to delay moving the action widget to the QPA menu until it's about to show. We still don't support moving the action widget back from QPA world into Qt, as explained in the QWidgetAction documentation. Task-number: QTBUG-19840 Change-Id: I47f6359b0806f967d80c67fbb1f36c3d5ec8603e Reviewed-by: Gabriel de Dietrich Reviewed-by: Morten Johan Sørvig --- src/widgets/widgets/qmenu.cpp | 38 ++++++++++++++++++++++++++++++++++++-- src/widgets/widgets/qmenu.h | 1 + src/widgets/widgets/qmenu_mac.mm | 19 +++++++++++++++++++ src/widgets/widgets/qmenu_p.h | 4 ++++ 4 files changed, 60 insertions(+), 2 deletions(-) diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp index 4cf39b3bb5..403ebe7f49 100644 --- a/src/widgets/widgets/qmenu.cpp +++ b/src/widgets/widgets/qmenu.cpp @@ -50,6 +50,9 @@ #include "qlayout.h" #include "qpainter.h" #include +#ifdef Q_OS_OSX +#include "qmacnativewidget_mac.h" +#endif #include "qapplication.h" #include "qdesktopwidget.h" #ifndef QT_NO_ACCESSIBILITY @@ -163,7 +166,7 @@ void QMenuPrivate::setPlatformMenu(QPlatformMenu *menu) platformMenu = menu; if (!platformMenu.isNull()) { - QObject::connect(platformMenu, SIGNAL(aboutToShow()), q, SIGNAL(aboutToShow())); + QObject::connect(platformMenu, SIGNAL(aboutToShow()), q, SLOT(_q_platformMenuAboutToShow())); QObject::connect(platformMenu, SIGNAL(aboutToHide()), q, SIGNAL(aboutToHide())); } } @@ -1104,6 +1107,8 @@ void QMenuPrivate::_q_actionTriggered() Q_Q(QMenu); if (QAction *action = qobject_cast(q->sender())) { QPointer actionGuard = action; + if (platformMenu && widgetItems.value(action)) + platformMenu->dismiss(); emit q->triggered(action); if (!activationRecursionGuard && actionGuard) { //in case the action has not been activated by the mouse @@ -1134,6 +1139,24 @@ void QMenuPrivate::_q_actionHovered() } } +void QMenuPrivate::_q_platformMenuAboutToShow() +{ + Q_Q(QMenu); + +#ifdef Q_OS_OSX + if (platformMenu) + Q_FOREACH (QAction *action, q->actions()) + if (QWidget *widget = widgetItems.value(const_cast(action))) + if (widget->parent() == q) { + QPlatformMenuItem *menuItem = platformMenu->menuItemForTag(reinterpret_cast(action)); + moveWidgetToPlatformItem(widget, menuItem); + platformMenu->syncMenuItem(menuItem); + } +#endif + + emit q->aboutToShow(); +} + bool QMenuPrivate::hasMouseMoved(const QPoint &globalPos) { //determines if the mouse has moved (ie its initial position has @@ -2996,8 +3019,19 @@ void QMenu::actionEvent(QActionEvent *e) if (e->action() == d->currentAction) d->currentAction = 0; if (QWidgetAction *wa = qobject_cast(e->action())) { - if (QWidget *widget = d->widgetItems.value(wa)) + if (QWidget *widget = d->widgetItems.value(wa)) { +#ifdef Q_OS_OSX + QWidget *p = widget->parentWidget(); + if (p != this && qobject_cast(p)) { + // This widget was reparented into a native Mac view + // (see QMenuPrivate::moveWidgetToPlatformItem). + // Reset the parent and delete the native widget. + widget->setParent(this); + p->deleteLater(); + } +#endif wa->releaseWidget(widget); + } } d->widgetItems.remove(e->action()); } diff --git a/src/widgets/widgets/qmenu.h b/src/widgets/widgets/qmenu.h index 8a8eaf3bae..fef7903278 100644 --- a/src/widgets/widgets/qmenu.h +++ b/src/widgets/widgets/qmenu.h @@ -195,6 +195,7 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_actionTriggered()) Q_PRIVATE_SLOT(d_func(), void _q_actionHovered()) Q_PRIVATE_SLOT(d_func(), void _q_overrideMenuActionDestroyed()) + Q_PRIVATE_SLOT(d_func(), void _q_platformMenuAboutToShow()) protected: QMenu(QMenuPrivate &dd, QWidget* parent = 0); diff --git a/src/widgets/widgets/qmenu_mac.mm b/src/widgets/widgets/qmenu_mac.mm index 41c4481b74..5e304d058b 100644 --- a/src/widgets/widgets/qmenu_mac.mm +++ b/src/widgets/widgets/qmenu_mac.mm @@ -44,6 +44,8 @@ #include "qmenu.h" #include "qmenubar.h" +#include "qmenubar_p.h" +#include "qmacnativewidget_mac.h" #include #include @@ -115,6 +117,23 @@ void QMenu::setAsDockMenu() \sa QMenu:setAsDockMenu() */ +void QMenuPrivate::moveWidgetToPlatformItem(QWidget *widget, QPlatformMenuItem* item) +{ + QMacNativeWidget *container = new QMacNativeWidget; + QObject::connect(platformMenu, SIGNAL(destroyed()), container, SLOT(deleteLater())); + container->resize(widget->sizeHint()); + widget->setParent(container); + + NSView *containerView = container->nativeView(); + QWindow *containerWindow = container->windowHandle(); + Qt::WindowFlags wf = containerWindow->flags(); + containerWindow->setFlags(wf | Qt::SubWindow); + [(NSView *)widget->winId() setAutoresizingMask:NSViewWidthSizable]; + + item->setNativeContents((WId)containerView); + container->show(); +} + #endif //QT_NO_MENU #ifndef QT_NO_MENUBAR diff --git a/src/widgets/widgets/qmenu_p.h b/src/widgets/widgets/qmenu_p.h index 9d9851af64..71bf33e1ce 100644 --- a/src/widgets/widgets/qmenu_p.h +++ b/src/widgets/widgets/qmenu_p.h @@ -110,6 +110,9 @@ public: void init(); void setPlatformMenu(QPlatformMenu *menu); void syncPlatformMenu(); +#ifdef Q_OS_OSX + void moveWidgetToPlatformItem(QWidget *w, QPlatformMenuItem* item); +#endif static QMenuPrivate *get(QMenu *m) { return m->d_func(); } int scrollerHeight() const; @@ -223,6 +226,7 @@ public: void _q_actionTriggered(); void _q_actionHovered(); + void _q_platformMenuAboutToShow(); bool hasMouseMoved(const QPoint &globalPos); -- cgit v1.2.3