Allow QPlatformSystemTrayIcon to create the QPlatformMenu

In case the QPlatformTheme does not provide a QPlatformMenu the
QPlatformSystemTrayIcon is not able to forward the menu because the
QPlatformMenu pointer passed to updateMenu is always null. Providing a
QPlatformMenu in the QPlatformTheme implementation should not be a
requirement for having the menu in the system tray icon. There are
cases where no QPlatformMenu should be created by the theme, e.g. if
the X11 implementation of QSystemTrayIcon gets provided by the xcb
plugin.

The change adds a virtual method to QPlatformSystemTrayIcon to create
a QPlatformMenu. This method is called from the QPA implementation of
QSystemTrayIcon if the QMenu's platformMenu is not present. Thus the
system tray icon is able to provide a custom implementation of the
menu. This gets installed through a new internal method in QMenu to set
the platform menu. It creates the required connections and sync the
state to the QMenu to the newly created QPlatformMenu.

Last but not least QPlatformMenu is extended by a method to create a
QPlatformMenuItem. The default implementation delegates to the platform
theme. This allows to provide the menu item implementation for the
system tray icon without providing the QPlatformMenuItem through the
platform theme.

Change-Id: I17234bd8bcf8c05f8bd786feff0cf8f860430e82
Reviewed-by: Kevin Krammer <kevin.krammer@kdab.com>
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
Reviewed-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
This commit is contained in:
Martin Gräßlin 2014-01-29 10:57:34 +01:00 committed by The Qt Project
parent 6aef733a7a
commit 824f080468
9 changed files with 132 additions and 5 deletions

View File

@ -121,7 +121,8 @@ SOURCES += \
kernel/qplatformservices.cpp \
kernel/qplatformscreenpageflipper.cpp \
kernel/qplatformsystemtrayicon_qpa.cpp \
kernel/qplatformsessionmanager.cpp
kernel/qplatformsessionmanager.cpp \
kernel/qplatformmenu.cpp
contains(QT_CONFIG, opengl)|contains(QT_CONFIG, opengles2) {
HEADERS += \

View File

@ -0,0 +1,55 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Copyright (C) 2014 Martin Graesslin <mgraesslin@kde.org>
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtGui module 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 Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qplatformmenu.h"
#include <qpa/qplatformtheme.h>
#include <private/qguiapplication_p.h>
QT_BEGIN_NAMESPACE
QPlatformMenuItem *QPlatformMenu::createMenuItem() const
{
return QGuiApplicationPrivate::platformTheme()->createPlatformMenuItem();
}
QT_END_NAMESPACE

View File

@ -116,6 +116,8 @@ public:
virtual QPlatformMenuItem *menuItemAt(int position) const = 0;
virtual QPlatformMenuItem *menuItemForTag(quintptr tag) const = 0;
virtual QPlatformMenuItem *createMenuItem() const;
Q_SIGNALS:
void aboutToShow();
void aboutToHide();

View File

@ -83,6 +83,8 @@ public:
virtual bool isSystemTrayAvailable() const = 0;
virtual bool supportsMessages() const = 0;
virtual QPlatformMenu *createMenu() const;
Q_SIGNALS:
void activated(QPlatformSystemTrayIcon::ActivationReason reason);
void messageClicked();

View File

@ -159,6 +159,22 @@ QPlatformSystemTrayIcon::~QPlatformSystemTrayIcon()
\sa activated()
*/
/*!
This method is called in case there is no QPlatformMenu available when
updating the menu. This allows the abstraction to provide a menu for the
system tray icon even if normally a non-native menu is used.
The default implementation returns a null pointer.
\sa updateMenu()
\since 5.3
*/
QPlatformMenu *QPlatformSystemTrayIcon::createMenu() const
{
return Q_NULLPTR;
}
QT_END_NAMESPACE
#include "moc_qplatformsystemtrayicon.cpp"

View File

@ -99,8 +99,14 @@ void QSystemTrayIconPrivate::updateIcon_sys()
void QSystemTrayIconPrivate::updateMenu_sys()
{
if (qpa_sys && menu)
if (qpa_sys && menu) {
if (!menu->platformMenu()) {
QPlatformMenu *platformMenu = qpa_sys->createMenu();
if (platformMenu)
menu->setPlatformMenu(platformMenu);
}
qpa_sys->updateMenu(menu->platformMenu());
}
}
void QSystemTrayIconPrivate::updateToolTip_sys()

View File

@ -153,13 +153,48 @@ void QMenuPrivate::init()
scroll->scrollFlags = QMenuPrivate::QMenuScroller::ScrollNone;
}
platformMenu = QGuiApplicationPrivate::platformTheme()->createPlatformMenu();
setPlatformMenu(QGuiApplicationPrivate::platformTheme()->createPlatformMenu());
}
void QMenuPrivate::setPlatformMenu(QPlatformMenu *menu)
{
Q_Q(QMenu);
if (!platformMenu.isNull() && !platformMenu->parent())
delete platformMenu.data();
platformMenu = menu;
if (!platformMenu.isNull()) {
QObject::connect(platformMenu, SIGNAL(aboutToShow()), q, SIGNAL(aboutToShow()));
QObject::connect(platformMenu, SIGNAL(aboutToHide()), q, SIGNAL(aboutToHide()));
}
}
// forward declare function
static void copyActionToPlatformItem(const QAction *action, QPlatformMenuItem* item);
void QMenuPrivate::syncPlatformMenu()
{
Q_Q(QMenu);
if (platformMenu.isNull())
return;
QPlatformMenuItem *beforeItem = Q_NULLPTR;
QListIterator<QAction*> it(q->actions());
it.toBack();
while (it.hasPrevious()) {
QPlatformMenuItem *menuItem = platformMenu->createMenuItem();
QAction *action = it.previous();
menuItem->setTag(reinterpret_cast<quintptr>(action));
QObject::connect(menuItem, SIGNAL(activated()), action, SLOT(trigger()));
QObject::connect(menuItem, SIGNAL(hovered()), action, SIGNAL(hovered()));
copyActionToPlatformItem(action, menuItem);
platformMenu->insertMenuItem(menuItem, beforeItem);
beforeItem = menuItem;
}
platformMenu->syncSeparatorsCollapsible(collapsibleSeparators);
platformMenu->setEnabled(q->isEnabled());
}
int QMenuPrivate::scrollerHeight() const
{
Q_Q(const QMenu);
@ -2976,8 +3011,7 @@ void QMenu::actionEvent(QActionEvent *e)
if (!d->platformMenu.isNull()) {
if (e->type() == QEvent::ActionAdded) {
QPlatformMenuItem *menuItem =
QGuiApplicationPrivate::platformTheme()->createPlatformMenuItem();
QPlatformMenuItem *menuItem = d->platformMenu->createMenuItem();
menuItem->setTag(reinterpret_cast<quintptr>(e->action()));
QObject::connect(menuItem, SIGNAL(activated()), e->action(), SLOT(trigger()));
QObject::connect(menuItem, SIGNAL(hovered()), e->action(), SIGNAL(hovered()));
@ -3154,6 +3188,14 @@ QPlatformMenu *QMenu::platformMenu()
return d_func()->platformMenu;
}
/*!\internal
*/
void QMenu::setPlatformMenu(QPlatformMenu *platformMenu)
{
d_func()->setPlatformMenu(platformMenu);
d_func()->syncPlatformMenu();
}
/*!
\property QMenu::separatorsCollapsible
\since 4.2

View File

@ -140,6 +140,7 @@ public:
void setNoReplayFor(QWidget *widget);
QPlatformMenu *platformMenu();
void setPlatformMenu(QPlatformMenu *platformMenu);
#ifdef Q_OS_WINCE
HMENU wceMenu();

View File

@ -108,6 +108,8 @@ public:
#endif
}
void init();
void setPlatformMenu(QPlatformMenu *menu);
void syncPlatformMenu();
static QMenuPrivate *get(QMenu *m) { return m->d_func(); }
int scrollerHeight() const;