D-Bus system tray icon: submenus can be created after context menu

QMenuPrivate::init() calls QPlatformTheme::createPlatformMenu() to
create a platform menu, but on Linux, that returns null for now.
QSystemTrayIcon::setContextMenu() results in recursive calls to
QSystemTrayIconPrivate::addPlatformMenu() which then calls
QDBusTrayIcon::createMenu() for the context menu and each submenu.
However if a submenu is added afterwards, a corresponding platform
menu is not immediately created.  Now copyActionToPlatformItem()
will detect and create the missing platform submenu, by calling
a new method: DBusPlatformMenu::createSubMenu().  This is because
there is no way of knowing that it's a tray-specific context menu
(which is so far the only case in which we use DBusPlatformMenu).
So, QPlatformMenu::createSubMenu() needs to exist as a new
QPA interface for this use case.

Task-number: QTBUG-45803
Change-Id: Ib319e873082196515ea0580d70d069099cf2c175
Reviewed-by: Jørgen Lind <jorgen.lind@theqtcompany.com>
bb10
Shawn Rutledge 2015-05-19 12:36:55 +02:00
parent 2858a3c91b
commit d8bfd812c3
5 changed files with 24 additions and 7 deletions

View File

@ -44,4 +44,9 @@ QPlatformMenuItem *QPlatformMenu::createMenuItem() const
return QGuiApplicationPrivate::platformTheme()->createPlatformMenuItem();
}
QPlatformMenu *QPlatformMenu::createSubMenu() const
{
return QGuiApplicationPrivate::platformTheme()->createPlatformMenu();
}
QT_END_NAMESPACE

View File

@ -123,6 +123,7 @@ public:
virtual QPlatformMenuItem *menuItemForTag(quintptr tag) const = 0;
virtual QPlatformMenuItem *createMenuItem() const;
virtual QPlatformMenu *createSubMenu() const;
Q_SIGNALS:
void aboutToShow();
void aboutToHide();

View File

@ -75,6 +75,9 @@ void QDBusPlatformMenuItem::setIcon(const QIcon &icon)
m_icon = icon;
}
/*!
Set a submenu under this menu item.
*/
void QDBusPlatformMenuItem::setMenu(QPlatformMenu *menu)
{
m_subMenu = static_cast<QDBusPlatformMenu *>(menu);
@ -242,8 +245,12 @@ const QList<QDBusPlatformMenuItem *> QDBusPlatformMenu::items() const
QPlatformMenuItem *QDBusPlatformMenu::createMenuItem() const
{
QDBusPlatformMenuItem *ret = new QDBusPlatformMenuItem();
ret->setMenu(const_cast<QDBusPlatformMenu *>(this));
return ret;
}
QPlatformMenu *QDBusPlatformMenu::createSubMenu() const
{
return new QDBusPlatformMenu;
}
QT_END_NAMESPACE

View File

@ -124,7 +124,7 @@ public:
quintptr tag()const Q_DECL_OVERRIDE { return m_tag; }
void setTag(quintptr tag) Q_DECL_OVERRIDE;
const QString text() { return m_text; }
const QString text() const { return m_text; }
void setText(const QString &text) Q_DECL_OVERRIDE;
void setIcon(const QIcon &icon) Q_DECL_OVERRIDE;
void setEnabled(bool enabled) Q_DECL_OVERRIDE;
@ -150,6 +150,7 @@ public:
const QList<QDBusPlatformMenuItem *> items() const;
QPlatformMenuItem *createMenuItem() const Q_DECL_OVERRIDE;
QPlatformMenu *createSubMenu() const Q_DECL_OVERRIDE;
bool operator==(const QDBusPlatformMenu& other) { return m_tag == other.m_tag; }
@ -175,6 +176,7 @@ private:
uint m_revision;
QHash<quintptr, QDBusPlatformMenuItem *> m_itemsByTag;
QList<QDBusPlatformMenuItem *> m_items;
QDBusPlatformMenuItem *m_containingMenuItem;
static QList<QDBusPlatformMenu *> m_topLevelMenus;
};

View File

@ -183,7 +183,7 @@ void QMenuPrivate::setPlatformMenu(QPlatformMenu *menu)
}
// forward declare function
static void copyActionToPlatformItem(const QAction *action, QPlatformMenuItem* item);
static void copyActionToPlatformItem(const QAction *action, QPlatformMenuItem *item, QPlatformMenu *itemsMenu);
void QMenuPrivate::syncPlatformMenu()
{
@ -200,7 +200,7 @@ void QMenuPrivate::syncPlatformMenu()
menuItem->setTag(reinterpret_cast<quintptr>(action));
QObject::connect(menuItem, SIGNAL(activated()), action, SLOT(trigger()), Qt::QueuedConnection);
QObject::connect(menuItem, SIGNAL(hovered()), action, SIGNAL(hovered()), Qt::QueuedConnection);
copyActionToPlatformItem(action, menuItem);
copyActionToPlatformItem(action, menuItem, platformMenu.data());
platformMenu->insertMenuItem(menuItem, beforeItem);
beforeItem = menuItem;
}
@ -3105,7 +3105,7 @@ QMenu::timerEvent(QTimerEvent *e)
}
}
static void copyActionToPlatformItem(const QAction *action, QPlatformMenuItem* item)
static void copyActionToPlatformItem(const QAction *action, QPlatformMenuItem *item, QPlatformMenu *itemsMenu)
{
item->setText(action->text());
item->setIsSeparator(action->isSeparator());
@ -3131,6 +3131,8 @@ static void copyActionToPlatformItem(const QAction *action, QPlatformMenuItem* i
item->setEnabled(action->isEnabled());
if (action->menu()) {
if (!action->menu()->platformMenu())
action->menu()->setPlatformMenu(itemsMenu->createSubMenu());
item->setMenu(action->menu()->platformMenu());
} else {
item->setMenu(0);
@ -3185,7 +3187,7 @@ void QMenu::actionEvent(QActionEvent *e)
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()));
copyActionToPlatformItem(e->action(), menuItem);
copyActionToPlatformItem(e->action(), menuItem, d->platformMenu);
QPlatformMenuItem* beforeItem = d->platformMenu->menuItemForTag(reinterpret_cast<quintptr>(e->before()));
d->platformMenu->insertMenuItem(menuItem, beforeItem);
} else if (e->type() == QEvent::ActionRemoved) {
@ -3195,7 +3197,7 @@ void QMenu::actionEvent(QActionEvent *e)
} else if (e->type() == QEvent::ActionChanged) {
QPlatformMenuItem *menuItem = d->platformMenu->menuItemForTag(reinterpret_cast<quintptr>(e->action()));
if (menuItem) {
copyActionToPlatformItem(e->action(), menuItem);
copyActionToPlatformItem(e->action(), menuItem, d->platformMenu);
d->platformMenu->syncMenuItem(menuItem);
}
}