Commit b1264794 authored by Kai Uwe Broulik's avatar Kai Uwe Broulik 🍇

[AppMenuModel] Monitor window properties

In case the application sets the menu after the window is shown like is the case with Firefox.

Differential Revision: https://phabricator.kde.org/D9635
parent b711b707
......@@ -31,6 +31,7 @@
#endif
#include <QAction>
#include <QGuiApplication>
#include <QMenu>
#include <QDebug>
#include <QDBusConnection>
......@@ -42,6 +43,10 @@
static const QByteArray s_x11AppMenuServiceNamePropertyName = QByteArrayLiteral("_KDE_NET_WM_APPMENU_SERVICE_NAME");
static const QByteArray s_x11AppMenuObjectPathPropertyName = QByteArrayLiteral("_KDE_NET_WM_APPMENU_OBJECT_PATH");
#if HAVE_X11
static QHash<QByteArray, xcb_atom_t> s_atoms;
#endif
class KDBusMenuImporter : public DBusMenuImporter
{
......@@ -123,12 +128,18 @@ void AppMenuModel::update()
void AppMenuModel::onActiveWindowChanged(WId id)
{
qApp->removeNativeEventFilter(this);
if (!id) {
setMenuAvailable(false);
emit modelNeedsUpdate();
return;
}
#if HAVE_X11
if (KWindowSystem::isPlatformX11()) {
auto *c = QX11Info::connection();
static QHash<QByteArray, xcb_atom_t> s_atoms;
auto getWindowPropertyString = [c, this](WId id, const QByteArray &name) -> QByteArray {
QByteArray value;
if (!s_atoms.contains(name)) {
......@@ -193,6 +204,11 @@ void AppMenuModel::onActiveWindowChanged(WId id)
return;
}
// monitor whether an app menu becomes available later
// this can happen when an app starts, shows its window, and only later announces global menu (e.g. Firefox)
qApp->installNativeEventFilter(this);
m_currentWindowId = id;
//no menu found, set it to unavailable
setMenuAvailable(false);
emit modelNeedsUpdate();
......@@ -274,3 +290,36 @@ void AppMenuModel::updateApplicationMenu(const QString &serviceName, const QStri
});
}
bool AppMenuModel::nativeEventFilter(const QByteArray &eventType, void *message, long *result)
{
Q_UNUSED(result);
if (!KWindowSystem::isPlatformX11() || eventType != "xcb_generic_event_t") {
return false;
}
#if HAVE_X11
auto e = static_cast<xcb_generic_event_t *>(message);
const uint8_t type = e->response_type & ~0x80;
if (type == XCB_PROPERTY_NOTIFY) {
auto *event = reinterpret_cast<xcb_property_notify_event_t *>(e);
if (event->window == m_currentWindowId) {
auto serviceNameAtom = s_atoms.value(s_x11AppMenuServiceNamePropertyName);
auto objectPathAtom = s_atoms.value(s_x11AppMenuObjectPathPropertyName);
if (serviceNameAtom != XCB_ATOM_NONE && objectPathAtom != XCB_ATOM_NONE) { // shouldn't happen
if (event->atom == serviceNameAtom || event->atom == objectPathAtom) {
// see if we now have a menu
onActiveWindowChanged(KWindowSystem::activeWindow());
}
}
}
}
#else
Q_UNUSED(message);
#endif
return false;
}
......@@ -20,6 +20,7 @@
******************************************************************/
#include <QAbstractListModel>
#include <QAbstractNativeEventFilter>
#include <QStringList>
#include <KWindowSystem>
#include <QPointer>
......@@ -30,7 +31,7 @@ class QModelIndex;
class QDBusServiceWatcher;
class KDBusMenuImporter;
class AppMenuModel : public QAbstractListModel
class AppMenuModel : public QAbstractListModel, public QAbstractNativeEventFilter
{
Q_OBJECT
......@@ -57,6 +58,9 @@ public:
signals:
void requestActivateIndex(int index);
protected:
bool nativeEventFilter(const QByteArray &eventType, void *message, long int *result) override;
private Q_SLOTS:
void onActiveWindowChanged(WId id);
void update();
......@@ -68,6 +72,8 @@ signals:
private:
bool m_menuAvailable;
WId m_currentWindowId = 0;
QPointer<QMenu> m_menu;
QStringList m_activeMenu;
QList<QAction *> m_activeActions;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment