summaryrefslogtreecommitdiffstats
path: root/src/platformsupport/dbusmenu
diff options
context:
space:
mode:
authorDmitry Shachnev <mitya57@gmail.com>2016-01-21 20:43:50 +0300
committerDmitry Shachnev <mitya57@gmail.com>2016-02-08 10:43:06 +0000
commit9c7f37e648024a8c7129e332dfb12b3ebd1ebf25 (patch)
tree9ff22f3f7b67960bd110331147cd859e156593fb /src/platformsupport/dbusmenu
parent01859cc12126b7205f5b54d383a9aa88db756f21 (diff)
dbusmenu: Refactor the code to allow dynamic updating of menus
* Transfer propertiesUpdated and updated signals from submenus to parent menus. Without this, the adaptor only receives this signal from top-level menu items, and doesn't receive it from items of submenus. Connect to these signals when a menu item is added or synced, and disconnect when it is removed. * Make QDBusPlatformMenus use IDs of items containing them, not their own IDs (own IDs do not make any sense since they are not exported over D-Bus). * Store toplevel menus per-adaptor, to make it possible to export multiple menus (for example a menubar and a tray icon menu). * Adjust the QDBusMenuLayoutItem::populate methods to always get the menu via its containing item and to populate the menus recursively. * Map D-Bus menu AboutToShow method to platform menu aboutToShow method, and map hovered and closed events to hovered and aboutToHide signals. (QTBUG-46293) * Always set the visible property on item. Otherwise, when an item becomes visible, the D-Bus menu still thinks it's invisible because that property was not changed back to true. (QTBUG-48647) * Call emitUpdated from insertMenuItem and removeMenuItem methods, as they really update layout. Do not call it from syncMenuItem, it changes only properties but not the layout. * Start revision numbering with 1, because libdbusmenu-based hosts ignore updated signal with revision=1. Task-number: QTBUG-46293 Task-number: QTBUG-48647 Change-Id: Icf713405db0443e25462c1a19046df7689fe5e78 Reviewed-by: Shawn Rutledge <shawn.rutledge@theqtcompany.com> Reviewed-by: Błażej Szczygieł <spaz16@wp.pl>
Diffstat (limited to 'src/platformsupport/dbusmenu')
-rw-r--r--src/platformsupport/dbusmenu/qdbusmenuadaptor.cpp34
-rw-r--r--src/platformsupport/dbusmenu/qdbusmenuadaptor_p.h5
-rw-r--r--src/platformsupport/dbusmenu/qdbusmenutypes.cpp35
-rw-r--r--src/platformsupport/dbusmenu/qdbusmenutypes_p.h2
-rw-r--r--src/platformsupport/dbusmenu/qdbusplatformmenu.cpp71
-rw-r--r--src/platformsupport/dbusmenu/qdbusplatformmenu_p.h9
6 files changed, 102 insertions, 54 deletions
diff --git a/src/platformsupport/dbusmenu/qdbusmenuadaptor.cpp b/src/platformsupport/dbusmenu/qdbusmenuadaptor.cpp
index ef5f30470a..0e9ea57406 100644
--- a/src/platformsupport/dbusmenu/qdbusmenuadaptor.cpp
+++ b/src/platformsupport/dbusmenu/qdbusmenuadaptor.cpp
@@ -51,8 +51,9 @@
QT_BEGIN_NAMESPACE
-QDBusMenuAdaptor::QDBusMenuAdaptor(QObject *parent)
- : QDBusAbstractAdaptor(parent)
+QDBusMenuAdaptor::QDBusMenuAdaptor(QDBusPlatformMenu *topLevelMenu)
+ : QDBusAbstractAdaptor(topLevelMenu)
+ , m_topLevelMenu(topLevelMenu)
{
setAutoRelaySignals(true);
}
@@ -80,7 +81,17 @@ uint QDBusMenuAdaptor::version() const
bool QDBusMenuAdaptor::AboutToShow(int id)
{
qCDebug(qLcMenu) << id;
- return false;
+ if (id == 0) {
+ emit m_topLevelMenu->aboutToShow();
+ } else {
+ QDBusPlatformMenuItem *item = QDBusPlatformMenuItem::byId(id);
+ if (item) {
+ const QDBusPlatformMenu *menu = static_cast<const QDBusPlatformMenu *>(item->menu());
+ if (menu)
+ emit const_cast<QDBusPlatformMenu *>(menu)->aboutToShow();
+ }
+ }
+ return false; // updateNeeded (we don't know that, so false)
}
QList<int> QDBusMenuAdaptor::AboutToShowGroup(const QList<int> &ids, QList<int> &idErrors)
@@ -88,6 +99,8 @@ QList<int> QDBusMenuAdaptor::AboutToShowGroup(const QList<int> &ids, QList<int>
qCDebug(qLcMenu) << ids;
Q_UNUSED(idErrors)
idErrors.clear();
+ Q_FOREACH (int id, ids)
+ AboutToShow(id);
return QList<int>(); // updatesNeeded
}
@@ -97,9 +110,20 @@ void QDBusMenuAdaptor::Event(int id, const QString &eventId, const QDBusVariant
Q_UNUSED(timestamp)
QDBusPlatformMenuItem *item = QDBusPlatformMenuItem::byId(id);
qCDebug(qLcMenu) << id << (item ? item->text() : QLatin1String("")) << eventId;
- // Events occur on both menus and menuitems, but we only care if it's an item being clicked.
if (item && eventId == QLatin1String("clicked"))
item->trigger();
+ if (item && eventId == QLatin1String("hovered"))
+ emit item->hovered();
+ if (eventId == QLatin1String("closed")) {
+ // There is no explicit AboutToHide method, so map closed event to aboutToHide method
+ const QDBusPlatformMenu *menu = Q_NULLPTR;
+ if (item)
+ menu = static_cast<const QDBusPlatformMenu *>(item->menu());
+ else if (id == 0)
+ menu = m_topLevelMenu;
+ if (menu)
+ emit const_cast<QDBusPlatformMenu *>(menu)->aboutToHide();
+ }
}
QList<int> QDBusMenuAdaptor::EventGroup(const QDBusMenuEventList &events)
@@ -117,7 +141,7 @@ QDBusMenuItemList QDBusMenuAdaptor::GetGroupProperties(const QList<int> &ids, co
uint QDBusMenuAdaptor::GetLayout(int parentId, int recursionDepth, const QStringList &propertyNames, QDBusMenuLayoutItem &layout)
{
- uint ret = layout.populate(parentId, recursionDepth, propertyNames);
+ uint ret = layout.populate(parentId, recursionDepth, propertyNames, m_topLevelMenu);
qCDebug(qLcMenu) << parentId << "depth" << recursionDepth << propertyNames << layout.m_id << layout.m_properties << "revision" << ret << layout;
return ret;
}
diff --git a/src/platformsupport/dbusmenu/qdbusmenuadaptor_p.h b/src/platformsupport/dbusmenu/qdbusmenuadaptor_p.h
index 98eae53f0b..7bec4ad8f3 100644
--- a/src/platformsupport/dbusmenu/qdbusmenuadaptor_p.h
+++ b/src/platformsupport/dbusmenu/qdbusmenuadaptor_p.h
@@ -140,7 +140,7 @@ class QDBusMenuAdaptor: public QDBusAbstractAdaptor
" </interface>\n"
"")
public:
- QDBusMenuAdaptor(QObject *parent);
+ QDBusMenuAdaptor(QDBusPlatformMenu *topLevelMenu);
virtual ~QDBusMenuAdaptor();
public: // PROPERTIES
@@ -166,6 +166,9 @@ Q_SIGNALS: // SIGNALS
void ItemActivationRequested(int id, uint timestamp);
void ItemsPropertiesUpdated(const QDBusMenuItemList &updatedProps, const QDBusMenuItemKeysList &removedProps);
void LayoutUpdated(uint revision, int parent);
+
+private:
+ QDBusPlatformMenu *m_topLevelMenu;
};
QT_END_NAMESPACE
diff --git a/src/platformsupport/dbusmenu/qdbusmenutypes.cpp b/src/platformsupport/dbusmenu/qdbusmenutypes.cpp
index 9f356bf281..b642038e7f 100644
--- a/src/platformsupport/dbusmenu/qdbusmenutypes.cpp
+++ b/src/platformsupport/dbusmenu/qdbusmenutypes.cpp
@@ -80,29 +80,27 @@ const QDBusArgument &operator>>(const QDBusArgument &arg, QDBusMenuItemKeys &key
return arg;
}
-uint QDBusMenuLayoutItem::populate(int id, int depth, const QStringList &propertyNames)
+uint QDBusMenuLayoutItem::populate(int id, int depth, const QStringList &propertyNames, const QDBusPlatformMenu *topLevelMenu)
{
qCDebug(qLcMenu) << id << "depth" << depth << propertyNames;
m_id = id;
if (id == 0) {
m_properties.insert(QLatin1String("children-display"), QLatin1String("submenu"));
- Q_FOREACH (const QDBusPlatformMenu *menu, QDBusPlatformMenu::topLevelMenus()) {
- if (menu)
- populate(menu, depth, propertyNames);
- }
+ if (topLevelMenu)
+ populate(topLevelMenu, depth, propertyNames);
return 1; // revision
}
- const QDBusPlatformMenu *menu = QDBusPlatformMenu::byId(id);
- if (!menu) {
- QDBusPlatformMenuItem *item = QDBusPlatformMenuItem::byId(id);
- if (item)
- menu = static_cast<const QDBusPlatformMenu *>(item->menu());
+ QDBusPlatformMenuItem *item = QDBusPlatformMenuItem::byId(id);
+ if (item) {
+ const QDBusPlatformMenu *menu = static_cast<const QDBusPlatformMenu *>(item->menu());
+
+ if (menu) {
+ if (depth != 0)
+ populate(menu, depth, propertyNames);
+ return menu->revision();
+ }
}
- if (depth != 0 && menu)
- populate(menu, depth, propertyNames);
- if (menu)
- return menu->revision();
return 1; // revision
}
@@ -118,11 +116,13 @@ void QDBusMenuLayoutItem::populate(const QDBusPlatformMenu *menu, int depth, con
void QDBusMenuLayoutItem::populate(const QDBusPlatformMenuItem *item, int depth, const QStringList &propertyNames)
{
- Q_UNUSED(depth)
- Q_UNUSED(propertyNames)
m_id = item->dbusID();
QDBusMenuItem proxy(item);
m_properties = proxy.m_properties;
+
+ const QDBusPlatformMenu *menu = static_cast<const QDBusPlatformMenu *>(item->menu());
+ if (depth != 0 && menu)
+ populate(menu, depth, propertyNames);
}
const QDBusArgument &operator<<(QDBusArgument &arg, const QDBusMenuLayoutItem &item)
@@ -199,8 +199,7 @@ QDBusMenuItem::QDBusMenuItem(const QDBusPlatformMenuItem *item)
m_properties.insert(QLatin1String("icon-data"), buf.data());
}
}
- if (!item->isVisible())
- m_properties.insert(QLatin1String("visible"), false);
+ m_properties.insert(QLatin1String("visible"), item->isVisible());
}
QDBusMenuItemList QDBusMenuItem::items(const QList<int> &ids, const QStringList &propertyNames)
diff --git a/src/platformsupport/dbusmenu/qdbusmenutypes_p.h b/src/platformsupport/dbusmenu/qdbusmenutypes_p.h
index 047e3c597e..7ff44bbeff 100644
--- a/src/platformsupport/dbusmenu/qdbusmenutypes_p.h
+++ b/src/platformsupport/dbusmenu/qdbusmenutypes_p.h
@@ -96,7 +96,7 @@ typedef QVector<QDBusMenuItemKeys> QDBusMenuItemKeysList;
class QDBusMenuLayoutItem
{
public:
- uint populate(int id, int depth, const QStringList &propertyNames);
+ uint populate(int id, int depth, const QStringList &propertyNames, const QDBusPlatformMenu *topLevelMenu);
void populate(const QDBusPlatformMenu *menu, int depth, const QStringList &propertyNames);
void populate(const QDBusPlatformMenuItem *item, int depth, const QStringList &propertyNames);
diff --git a/src/platformsupport/dbusmenu/qdbusplatformmenu.cpp b/src/platformsupport/dbusmenu/qdbusplatformmenu.cpp
index 62f041bc86..9f99ef97e5 100644
--- a/src/platformsupport/dbusmenu/qdbusplatformmenu.cpp
+++ b/src/platformsupport/dbusmenu/qdbusplatformmenu.cpp
@@ -41,9 +41,7 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(qLcMenu, "qt.qpa.menu")
static int nextDBusID = 1;
-QHash<int, QDBusPlatformMenu *> menusByID;
QHash<int, QDBusPlatformMenuItem *> menuItemsByID;
-QList<QDBusPlatformMenu *> QDBusPlatformMenu::m_topLevelMenus;
QDBusPlatformMenuItem::QDBusPlatformMenuItem(quintptr tag)
: m_tag(tag ? tag : reinterpret_cast<quintptr>(this)) // QMenu will overwrite this later
@@ -85,7 +83,11 @@ void QDBusPlatformMenuItem::setIcon(const QIcon &icon)
*/
void QDBusPlatformMenuItem::setMenu(QPlatformMenu *menu)
{
- m_subMenu = static_cast<QDBusPlatformMenu *>(menu);
+ if (m_subMenu)
+ static_cast<QDBusPlatformMenu *>(m_subMenu)->setContainingMenuItem(Q_NULLPTR);
+ m_subMenu = menu;
+ if (menu)
+ static_cast<QDBusPlatformMenu *>(menu)->setContainingMenuItem(this);
}
void QDBusPlatformMenuItem::setEnabled(bool enabled)
@@ -130,7 +132,11 @@ void QDBusPlatformMenuItem::trigger()
QDBusPlatformMenuItem *QDBusPlatformMenuItem::byId(int id)
{
- return menuItemsByID[id];
+ // We need to check contains because otherwise QHash would insert
+ // a default-constructed nullptr value into menuItemsByID
+ if (menuItemsByID.contains(id))
+ return menuItemsByID[id];
+ return Q_NULLPTR;
}
QList<const QDBusPlatformMenuItem *> QDBusPlatformMenuItem::byIds(const QList<int> &ids)
@@ -149,18 +155,13 @@ QDBusPlatformMenu::QDBusPlatformMenu(quintptr tag)
, m_isEnabled(true)
, m_isVisible(true)
, m_isSeparator(false)
- , m_dbusID(nextDBusID++)
- , m_revision(0)
+ , m_revision(1)
+ , m_containingMenuItem(Q_NULLPTR)
{
- menusByID.insert(m_dbusID, this);
- // Assume it's top-level until we find out otherwise
- m_topLevelMenus << this;
}
QDBusPlatformMenu::~QDBusPlatformMenu()
{
- menusByID.remove(m_dbusID);
- m_topLevelMenus.removeOne(this);
}
void QDBusPlatformMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before)
@@ -174,38 +175,59 @@ void QDBusPlatformMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMen
else
m_items.insert(idx, item);
m_itemsByTag.insert(item->tag(), item);
- // If a menu is found as a submenu under an item, we know that it's not a top-level menu.
if (item->menu())
- m_topLevelMenus.removeOne(const_cast<QDBusPlatformMenu *>(static_cast<const QDBusPlatformMenu *>(item->menu())));
+ syncSubMenu(static_cast<const QDBusPlatformMenu *>(item->menu()));
+ emitUpdated();
}
void QDBusPlatformMenu::removeMenuItem(QPlatformMenuItem *menuItem)
{
- m_items.removeAll(static_cast<QDBusPlatformMenuItem *>(menuItem));
+ QDBusPlatformMenuItem *item = static_cast<QDBusPlatformMenuItem *>(menuItem);
+ m_items.removeAll(item);
m_itemsByTag.remove(menuItem->tag());
+ if (item->menu()) {
+ // disconnect from the signals we connected to in syncSubMenu()
+ const QDBusPlatformMenu *menu = static_cast<const QDBusPlatformMenu *>(item->menu());
+ disconnect(menu, &QDBusPlatformMenu::propertiesUpdated,
+ this, &QDBusPlatformMenu::propertiesUpdated);
+ disconnect(menu, &QDBusPlatformMenu::updated,
+ this, &QDBusPlatformMenu::updated);
+ }
+ emitUpdated();
+}
+
+void QDBusPlatformMenu::syncSubMenu(const QDBusPlatformMenu *menu)
+{
+ // The adaptor is only connected to the propertiesUpdated signal of the top-level
+ // menu, so the submenus should transfer their signals to their parents.
+ connect(menu, &QDBusPlatformMenu::propertiesUpdated,
+ this, &QDBusPlatformMenu::propertiesUpdated, Qt::UniqueConnection);
+ connect(menu, &QDBusPlatformMenu::updated,
+ this, &QDBusPlatformMenu::updated, Qt::UniqueConnection);
}
void QDBusPlatformMenu::syncMenuItem(QPlatformMenuItem *menuItem)
{
+ QDBusPlatformMenuItem *item = static_cast<QDBusPlatformMenuItem *>(menuItem);
+ // if a submenu was added to this item, we need to connect to its signals
+ if (item->menu())
+ syncSubMenu(static_cast<const QDBusPlatformMenu *>(item->menu()));
// TODO keep around copies of the QDBusMenuLayoutItems so they can be updated?
// or eliminate them by putting dbus streaming operators in this class instead?
// or somehow tell the dbusmenu client that something has changed, so it will ask for properties again
- emitUpdated();
QDBusMenuItemList updated;
QDBusMenuItemKeysList removed;
- updated << QDBusMenuItem(static_cast<QDBusPlatformMenuItem *>(menuItem));
+ updated << QDBusMenuItem(item);
qCDebug(qLcMenu) << updated;
emit propertiesUpdated(updated, removed);
}
-QDBusPlatformMenu *QDBusPlatformMenu::byId(int id)
-{
- return menusByID[id];
-}
-
void QDBusPlatformMenu::emitUpdated()
{
- emit updated(++m_revision, m_dbusID);
+ if (m_containingMenuItem)
+ emit updated(++m_revision, m_containingMenuItem->dbusID());
+ else
+ emit updated(++m_revision, 0);
}
void QDBusPlatformMenu::setTag(quintptr tag)
@@ -233,6 +255,11 @@ void QDBusPlatformMenu::setVisible(bool isVisible)
m_isVisible = isVisible;
}
+void QDBusPlatformMenu::setContainingMenuItem(QDBusPlatformMenuItem *item)
+{
+ m_containingMenuItem = item;
+}
+
QPlatformMenuItem *QDBusPlatformMenu::menuItemAt(int position) const
{
return m_items.at(position);
diff --git a/src/platformsupport/dbusmenu/qdbusplatformmenu_p.h b/src/platformsupport/dbusmenu/qdbusplatformmenu_p.h
index 5892391299..f2419b5378 100644
--- a/src/platformsupport/dbusmenu/qdbusplatformmenu_p.h
+++ b/src/platformsupport/dbusmenu/qdbusplatformmenu_p.h
@@ -130,6 +130,7 @@ public:
~QDBusPlatformMenu();
void insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) Q_DECL_OVERRIDE;
void removeMenuItem(QPlatformMenuItem *menuItem) Q_DECL_OVERRIDE;
+ void syncSubMenu(const QDBusPlatformMenu *menu);
void syncMenuItem(QPlatformMenuItem *menuItem) Q_DECL_OVERRIDE;
void syncSeparatorsCollapsible(bool enable) Q_DECL_OVERRIDE { Q_UNUSED(enable); }
@@ -147,8 +148,7 @@ public:
void setMinimumWidth(int width) Q_DECL_OVERRIDE { Q_UNUSED(width); }
void setFont(const QFont &font) Q_DECL_OVERRIDE { Q_UNUSED(font); }
void setMenuType(MenuType type) Q_DECL_OVERRIDE { Q_UNUSED(type); }
-
- int dbusID() const { return m_dbusID; }
+ void setContainingMenuItem(QDBusPlatformMenuItem *item);
void showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item) Q_DECL_OVERRIDE
{
@@ -169,9 +169,6 @@ public:
bool operator==(const QDBusPlatformMenu& other) { return m_tag == other.m_tag; }
- static QDBusPlatformMenu* byId(int id);
- static QList<QDBusPlatformMenu *> topLevelMenus() { return m_topLevelMenus; }
-
uint revision() const { return m_revision; }
void emitUpdated();
@@ -187,12 +184,10 @@ private:
bool m_isEnabled;
bool m_isVisible;
bool m_isSeparator;
- int m_dbusID;
uint m_revision;
QHash<quintptr, QDBusPlatformMenuItem *> m_itemsByTag;
QList<QDBusPlatformMenuItem *> m_items;
QDBusPlatformMenuItem *m_containingMenuItem;
- static QList<QDBusPlatformMenu *> m_topLevelMenus;
};
QT_END_NAMESPACE