QMenu: Add support for QWidgetAction on Mac

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 <gabriel.dedietrich@digia.com>
Reviewed-by: Morten Johan Sørvig <morten.sorvig@digia.com>
bb10
Gabriel de Dietrich 2014-04-01 18:27:59 +02:00 committed by The Qt Project
parent 379042b84c
commit c7ed644fd1
4 changed files with 60 additions and 2 deletions

View File

@ -50,6 +50,9 @@
#include "qlayout.h"
#include "qpainter.h"
#include <qpa/qplatformtheme.h>
#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<QAction *>(q->sender())) {
QPointer<QAction> 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<QAction *>(action)))
if (widget->parent() == q) {
QPlatformMenuItem *menuItem = platformMenu->menuItemForTag(reinterpret_cast<quintptr>(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<QWidgetAction *>(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<QMacNativeWidget *>(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());
}

View File

@ -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);

View File

@ -44,6 +44,8 @@
#include "qmenu.h"
#include "qmenubar.h"
#include "qmenubar_p.h"
#include "qmacnativewidget_mac.h"
#include <QtCore/QDebug>
#include <QtGui/QGuiApplication>
@ -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

View File

@ -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);