File plasma-512.patch of Package plasmoid-active-window-control
From 41cf4067b90ef1e47e9a7f744009a6dd9516910f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Kostoln=C3=BD?= <clearmartin@zoho.com>
Date: Wed, 10 Jan 2018 22:50:29 +0100
Subject: [PATCH] C++ plugin along with copied dbusmenuqt and appmenumodel
updated
added INSTALL file with install instructions
---
INSTALL | 7 ++
lib/CMakeLists.txt | 6 +-
lib/TODO | 0
...vewindowcontrolapplet.cpp => appmenuapplet.cpp} | 137 +++++++++++++--------
...activewindowcontrolapplet.h => appmenuapplet.h} | 16 +--
plugin/CMakeLists.txt | 32 ++---
plugin/appmenumodel.cpp | 76 ++++++++++--
plugin/appmenumodel.h | 19 ++-
...vewindowcontrolplugin.cpp => appmenuplugin.cpp} | 4 +-
...activewindowcontrolplugin.h => appmenuplugin.h} | 6 +-
plugin/libdbusmenuqt/CMakeLists.txt | 2 -
plugin/libdbusmenuqt/dbusmenuimporter.cpp | 18 ++-
plugin/libdbusmenuqt/dbusmenuimporter.h | 3 +
plugin/libdbusmenuqt/test/CMakeLists.txt | 3 -
plugin/libdbusmenuqt/test/README | 2 -
plugin/libdbusmenuqt/test/main.cpp | 84 -------------
plugin/qmldir | 2 +-
17 files changed, 218 insertions(+), 199 deletions(-)
create mode 100644 INSTALL
delete mode 100644 lib/TODO
rename lib/{activewindowcontrolapplet.cpp => appmenuapplet.cpp} (58%)
rename lib/{activewindowcontrolapplet.h => appmenuapplet.h} (83%)
rename plugin/{activewindowcontrolplugin.cpp => appmenuplugin.cpp} (92%)
rename plugin/{activewindowcontrolplugin.h => appmenuplugin.h} (90%)
delete mode 100644 plugin/libdbusmenuqt/test/CMakeLists.txt
delete mode 100644 plugin/libdbusmenuqt/test/README
delete mode 100644 plugin/libdbusmenuqt/test/main.cpp
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..f991263
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,7 @@
+INSTALATION INSTRUCTIONS
+
+$ mkdir build
+$ cd build
+$ cmake .. -DCMAKE_INSTALL_PREFIX=/usr
+$ make
+$ make install
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index fd02426..48e9f27 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -1,8 +1,8 @@
-set(activewindowcontrolapplet_SRCS
- activewindowcontrolapplet.cpp
+set(appmenuapplet_SRCS
+ appmenuapplet.cpp
)
-add_library(plasma_applet_activewindowcontrol MODULE ${activewindowcontrolapplet_SRCS})
+add_library(plasma_applet_activewindowcontrol MODULE ${appmenuapplet_SRCS})
kcoreaddons_desktop_to_json(plasma_applet_activewindowcontrol ../package/metadata.desktop)
diff --git a/lib/TODO b/lib/TODO
deleted file mode 100644
index e69de29..0000000
diff --git a/lib/activewindowcontrolapplet.cpp b/lib/appmenuapplet.cpp
similarity index 58%
rename from lib/activewindowcontrolapplet.cpp
rename to lib/appmenuapplet.cpp
index 005a385..082ead3 100644
--- a/lib/activewindowcontrolapplet.cpp
+++ b/lib/appmenuapplet.cpp
@@ -19,7 +19,7 @@
*
*/
-#include "activewindowcontrolapplet.h"
+#include "appmenuapplet.h"
#include "../plugin/appmenumodel.h"
#include <QAction>
@@ -30,31 +30,61 @@
#include <QQuickWindow>
#include <QScreen>
#include <QDBusConnection>
+#include <QDBusMessage>
+#include <QDBusPendingCall>
+#include <QDBusConnectionInterface>
+#include <QTimer>
-ActiveWindowControlApplet::ActiveWindowControlApplet(QObject *parent, const QVariantList &data)
+int AppMenuApplet::s_refs = 0;
+
+static const QString s_viewService(QStringLiteral("org.kde.kappmenuview"));
+
+AppMenuApplet::AppMenuApplet(QObject *parent, const QVariantList &data)
: Plasma::Applet(parent, data)
{
+ ++s_refs;
+ //if we're the first, regster the service
+ if (s_refs == 1) {
+ QDBusConnection::sessionBus().interface()->registerService(s_viewService,
+ QDBusConnectionInterface::QueueService,
+ QDBusConnectionInterface::DontAllowReplacement);
+ }
+ /*it registers or unregisters the service when the destroyed value of the applet change,
+ and not in the dtor, because:
+ when we "delete" an applet, it just hides it for about a minute setting its status
+ to destroyed, in order to be able to do a clean undo: if we undo, there will be
+ another destroyedchanged and destroyed will be false.
+ When this happens, if we are the only appmenu applet existing, the dbus interface
+ will have to be registered again*/
+ connect(this, &Applet::destroyedChanged, this, [this](bool destroyed) {
+ if (destroyed) {
+ //if we were the last, unregister
+ if (--s_refs == 0) {
+ QDBusConnection::sessionBus().interface()->unregisterService(s_viewService);
+ }
+ } else {
+ //if we're the first, regster the service
+ if (++s_refs == 1) {
+ QDBusConnection::sessionBus().interface()->registerService(s_viewService,
+ QDBusConnectionInterface::QueueService,
+ QDBusConnectionInterface::DontAllowReplacement);
+ }
+ }
+ });
}
-ActiveWindowControlApplet::~ActiveWindowControlApplet() = default;
+AppMenuApplet::~AppMenuApplet() = default;
-void ActiveWindowControlApplet::init()
+void AppMenuApplet::init()
{
- // TODO Wayland PlasmaShellSurface stuff
- QDBusConnection::sessionBus().connect(QStringLiteral("org.kde.kappmenu"),
- QStringLiteral("/KAppMenu"),
- QStringLiteral("org.kde.kappmenu"),
- QStringLiteral("reconfigured"),
- this, SLOT(updateAppletEnabled()));
- updateAppletEnabled();
}
-AppMenuModel *ActiveWindowControlApplet::model() const
+AppMenuModel *AppMenuApplet::model() const
{
return m_model;
}
-void ActiveWindowControlApplet::setModel(AppMenuModel *model)
+void AppMenuApplet::setModel(AppMenuModel *model)
{
if (m_model != model) {
m_model = model;
@@ -62,12 +92,12 @@ void ActiveWindowControlApplet::setModel(AppMenuModel *model)
}
}
-int ActiveWindowControlApplet::view() const
+int AppMenuApplet::view() const
{
return m_viewType;
}
-void ActiveWindowControlApplet::setView(int type)
+void AppMenuApplet::setView(int type)
{
if (m_viewType != type) {
m_viewType = type;
@@ -75,12 +105,12 @@ void ActiveWindowControlApplet::setView(int type)
}
}
-int ActiveWindowControlApplet::currentIndex() const
+int AppMenuApplet::currentIndex() const
{
return m_currentIndex;
}
-void ActiveWindowControlApplet::setCurrentIndex(int currentIndex)
+void AppMenuApplet::setCurrentIndex(int currentIndex)
{
if (m_currentIndex != currentIndex) {
m_currentIndex = currentIndex;
@@ -88,12 +118,12 @@ void ActiveWindowControlApplet::setCurrentIndex(int currentIndex)
}
}
-QQuickItem *ActiveWindowControlApplet::buttonGrid() const
+QQuickItem *AppMenuApplet::buttonGrid() const
{
return m_buttonGrid;
}
-void ActiveWindowControlApplet::setButtonGrid(QQuickItem *buttonGrid)
+void AppMenuApplet::setButtonGrid(QQuickItem *buttonGrid)
{
if (m_buttonGrid != buttonGrid) {
m_buttonGrid = buttonGrid;
@@ -101,34 +131,11 @@ void ActiveWindowControlApplet::setButtonGrid(QQuickItem *buttonGrid)
}
}
-bool ActiveWindowControlApplet::appletEnabled() const
-{
- return m_appletEnabled;
-}
-
-void ActiveWindowControlApplet::updateAppletEnabled()
-{
- KConfigGroup config(KSharedConfig::openConfig(QStringLiteral("kdeglobals")), QStringLiteral("Appmenu Style"));
- const QString &menuStyle = config.readEntry(QStringLiteral("Style"));
-
- const bool enabled = (menuStyle == QLatin1String("Widget"));
-
- if (m_appletEnabled != enabled) {
- m_appletEnabled = enabled;
- emit appletEnabledChanged();
- }
-}
-
-QMenu *ActiveWindowControlApplet::createMenu(int idx) const
+QMenu *AppMenuApplet::createMenu(int idx) const
{
QMenu *menu = nullptr;
QAction *action = nullptr;
- if (!m_model) {
- qDebug() << "model not available";
- return menu;
- }
-
if (view() == CompactView) {
menu = new QMenu();
for (int i=0; i<m_model->rowCount(); i++) {
@@ -150,24 +157,40 @@ QMenu *ActiveWindowControlApplet::createMenu(int idx) const
return menu;
}
-void ActiveWindowControlApplet::onMenuAboutToHide()
+void AppMenuApplet::onMenuAboutToHide()
{
setCurrentIndex(-1);
}
-void ActiveWindowControlApplet::trigger(QQuickItem *ctx, int idx)
+void AppMenuApplet::trigger(QQuickItem *ctx, int idx)
{
if (m_currentIndex == idx) {
return;
}
+ if (!ctx || !ctx->window() || !ctx->window()->screen()) {
+ return;
+ }
+
QMenu *actionMenu = createMenu(idx);
if (actionMenu) {
- if (ctx && ctx->window() && ctx->window()->mouseGrabberItem()) {
- // FIXME event forge thing enters press and hold move mode :/
- ctx->window()->mouseGrabberItem()->ungrabMouse();
- }
+ //this is a workaround where Qt will fail to realise a mouse has been released
+ // this happens if a window which does not accept focus spawns a new window that takes focus and X grab
+ // whilst the mouse is depressed
+ // https://bugreports.qt.io/browse/QTBUG-59044
+ // this causes the next click to go missing
+
+ //by releasing manually we avoid that situation
+ auto ungrabMouseHack = [ctx]() {
+ if (ctx && ctx->window() && ctx->window()->mouseGrabberItem()) {
+ // FIXME event forge thing enters press and hold move mode :/
+ ctx->window()->mouseGrabberItem()->ungrabMouse();
+ }
+ };
+
+ QTimer::singleShot(0, ctx, ungrabMouseHack);
+ //end workaround
const auto &geo = ctx->window()->screen()->availableVirtualGeometry();
@@ -185,8 +208,16 @@ void ActiveWindowControlApplet::trigger(QQuickItem *ctx, int idx)
actionMenu->installEventFilter(this);
}
+ setStatus(Plasma::Types::AcceptingInputStatus);
+ actionMenu->winId();//create window handle
+ actionMenu->windowHandle()->setTransientParent(ctx->window());
+
actionMenu->popup(pos);
+ //we can return to passive immediately, an autohide panel will stay open whilst
+ //any transient window is showing
+ setStatus(Plasma::Types::PassiveStatus);
+
if (view() == FullView) {
// hide the old menu only after showing the new one to avoid brief flickering
// in other windows as they briefly re-gain focus
@@ -200,13 +231,13 @@ void ActiveWindowControlApplet::trigger(QQuickItem *ctx, int idx)
setCurrentIndex(idx);
// FIXME TODO connect only once
- connect(actionMenu, &QMenu::aboutToHide, this, &ActiveWindowControlApplet::onMenuAboutToHide, Qt::UniqueConnection);
+ connect(actionMenu, &QMenu::aboutToHide, this, &AppMenuApplet::onMenuAboutToHide, Qt::UniqueConnection);
return;
}
}
// FIXME TODO doesn't work on submenu
-bool ActiveWindowControlApplet::eventFilter(QObject *watched, QEvent *event)
+bool AppMenuApplet::eventFilter(QObject *watched, QEvent *event)
{
auto *menu = qobject_cast<QMenu *>(watched);
if (!menu) {
@@ -258,6 +289,6 @@ bool ActiveWindowControlApplet::eventFilter(QObject *watched, QEvent *event)
return false;
}
-K_EXPORT_PLASMA_APPLET_WITH_JSON(activewindowcontrol, ActiveWindowControlApplet, "metadata.json")
+K_EXPORT_PLASMA_APPLET_WITH_JSON(appmenu, AppMenuApplet, "metadata.json")
-#include "activewindowcontrolapplet.moc"
+#include "appmenuapplet.moc"
diff --git a/lib/activewindowcontrolapplet.h b/lib/appmenuapplet.h
similarity index 83%
rename from lib/activewindowcontrolapplet.h
rename to lib/appmenuapplet.h
index 8f8c2df..0633c32 100644
--- a/lib/activewindowcontrolapplet.h
+++ b/lib/appmenuapplet.h
@@ -29,12 +29,10 @@ class QQuickItem;
class QMenu;
class AppMenuModel;
-class ActiveWindowControlApplet : public Plasma::Applet
+class AppMenuApplet : public Plasma::Applet
{
Q_OBJECT
- Q_PROPERTY(bool appletEnabled READ appletEnabled NOTIFY appletEnabledChanged)
-
Q_PROPERTY(AppMenuModel* model READ model WRITE setModel NOTIFY modelChanged)
Q_PROPERTY(int view READ view WRITE setView NOTIFY viewChanged)
@@ -49,8 +47,8 @@ class ActiveWindowControlApplet : public Plasma::Applet
CompactView
};
- explicit ActiveWindowControlApplet(QObject *parent, const QVariantList &data);
- ~ActiveWindowControlApplet() override;
+ explicit AppMenuApplet(QObject *parent, const QVariantList &data);
+ ~AppMenuApplet() override;
void init() override;
@@ -65,22 +63,18 @@ class ActiveWindowControlApplet : public Plasma::Applet
int view() const;
void setView(int type);
- bool appletEnabled() const;
-
signals:
void modelChanged();
void viewChanged();
void currentIndexChanged();
void buttonGridChanged();
- void appletEnabledChanged();
void requestActivateIndex(int index);
public slots:
- void updateAppletEnabled();
void trigger(QQuickItem *ctx, int idx);
protected:
- bool eventFilter(QObject *watched, QEvent *event);
+ bool eventFilter(QObject *watched, QEvent *event) Q_DECL_OVERRIDE;
private:
QMenu *createMenu(int idx) const;
@@ -90,8 +84,8 @@ public slots:
int m_currentIndex = -1;
int m_viewType = FullView;
- bool m_appletEnabled = true;
QPointer<QMenu> m_currentMenu;
QPointer<QQuickItem> m_buttonGrid;
QPointer<AppMenuModel> m_model;
+ static int s_refs;
};
diff --git a/plugin/CMakeLists.txt b/plugin/CMakeLists.txt
index 8f855ac..1a4efb9 100644
--- a/plugin/CMakeLists.txt
+++ b/plugin/CMakeLists.txt
@@ -1,27 +1,24 @@
-set(activewindowcontrolplugin_SRCS
+set(appmenuapplet_SRCS
appmenumodel.cpp
- activewindowcontrolplugin.cpp
- )
+ appmenuplugin.cpp
+)
-add_library(activewindowcontrolplugin SHARED ${activewindowcontrolplugin_SRCS})
-
-cmake_policy(SET CMP0053 NEW)
-cmake_policy(SET CMP0063 NEW)
+add_library(appmenuplugin SHARED ${appmenuapplet_SRCS})
+# load dbusmenuqt
find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Widgets DBus)
-
+cmake_policy(SET CMP0063 NEW)
add_subdirectory(libdbusmenuqt)
-target_link_libraries(activewindowcontrolplugin
+target_link_libraries(appmenuplugin
Qt5::Core
Qt5::Widgets
Qt5::Quick
- Qt5::DBus
KF5::Plasma
KF5::WindowSystem
- dbusmenuqt
- )
+ dbusmenuqt)
+# load X11 libraries
find_package(X11)
set_package_properties(X11 PROPERTIES DESCRIPTION "X11 libraries"
URL "http://www.x.org"
@@ -45,15 +42,12 @@ endif()
configure_file(config-X11.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-X11.h)
if(HAVE_X11)
- find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED X11Extras)
- find_package(XCB MODULE REQUIRED COMPONENTS XCB RANDR)
+ find_package(XCB MODULE REQUIRED COMPONENTS XCB)
set_package_properties(XCB PROPERTIES TYPE REQUIRED)
- target_link_libraries(activewindowcontrolplugin
- Qt5::X11Extras
- XCB::XCB
- )
+ target_link_libraries(appmenuplugin Qt5::X11Extras XCB::XCB)
endif()
-install(TARGETS activewindowcontrolplugin DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/private/activeWindowControl)
+# install plugin
+install(TARGETS appmenuplugin DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/private/activeWindowControl)
install(FILES qmldir DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/private/activeWindowControl)
diff --git a/plugin/appmenumodel.cpp b/plugin/appmenumodel.cpp
index 9d7d5e8..3b41ec6 100644
--- a/plugin/appmenumodel.cpp
+++ b/plugin/appmenumodel.cpp
@@ -25,22 +25,28 @@
#include <config-X11.h>
-#ifdef HAVE_X11
+#if HAVE_X11
#include <QX11Info>
#include <xcb/xcb.h>
#endif
#include <QAction>
+#include <QGuiApplication>
#include <QMenu>
#include <QDebug>
#include <QDBusConnection>
#include <QDBusConnectionInterface>
+#include <QDBusServiceWatcher>
#include <dbusmenuimporter.h>
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
{
@@ -60,17 +66,19 @@ class KDBusMenuImporter : public DBusMenuImporter
};
AppMenuModel::AppMenuModel(QObject *parent)
- : QAbstractListModel(parent)
+ : QAbstractListModel(parent),
+ m_serviceWatcher(new QDBusServiceWatcher(this))
{
connect(KWindowSystem::self(), &KWindowSystem::activeWindowChanged, this, &AppMenuModel::onActiveWindowChanged);
connect(this, &AppMenuModel::modelNeedsUpdate, this, &AppMenuModel::update, Qt::UniqueConnection);
onActiveWindowChanged(KWindowSystem::activeWindow());
+ m_serviceWatcher->setConnection(QDBusConnection::sessionBus());
//if our current DBus connection gets lost, close the menu
//we'll select the new menu when the focus changes
- connect(QDBusConnection::sessionBus().interface(), &QDBusConnectionInterface::serviceOwnerChanged, this, [this](const QString &serviceName, const QString &oldOwner, const QString &newOwner)
+ connect(m_serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, [this](const QString &serviceName)
{
- if (serviceName == m_serviceName && newOwner.isEmpty()) {
+ if (serviceName == m_serviceName) {
setMenuAvailable(false);
emit modelNeedsUpdate();
}
@@ -120,12 +128,18 @@ void AppMenuModel::update()
void AppMenuModel::onActiveWindowChanged(WId id)
{
-#ifdef HAVE_X11
+ 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)) {
@@ -190,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();
@@ -234,6 +253,8 @@ void AppMenuModel::updateApplicationMenu(const QString &serviceName, const QStri
}
m_serviceName = serviceName;
+ m_serviceWatcher->setWatchedServices(QStringList({m_serviceName}));
+
m_menuObjectPath = menuObjectPath;
if (m_importer) {
@@ -259,5 +280,46 @@ void AppMenuModel::updateApplicationMenu(const QString &serviceName, const QStri
setMenuAvailable(true);
emit modelNeedsUpdate();
});
+
+ connect(m_importer.data(), &DBusMenuImporter::actionActivationRequested, this, [this](QAction *action) {
+ // TODO submenus
+ auto it = std::find(m_activeActions.constBegin(), m_activeActions.constEnd(), action);
+ if (it != m_activeActions.constEnd()) {
+ requestActivateIndex(it - m_activeActions.constBegin());
+ }
+ });
+}
+
+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;
}
diff --git a/plugin/appmenumodel.h b/plugin/appmenumodel.h
index 77ad5e9..8d7ef92 100644
--- a/plugin/appmenumodel.h
+++ b/plugin/appmenumodel.h
@@ -20,6 +20,7 @@
******************************************************************/
#include <QAbstractListModel>
+#include <QAbstractNativeEventFilter>
#include <QStringList>
#include <KWindowSystem>
#include <QPointer>
@@ -27,9 +28,10 @@
class QMenu;
class QAction;
class QModelIndex;
+class QDBusServiceWatcher;
class KDBusMenuImporter;
-class AppMenuModel : public QAbstractListModel
+class AppMenuModel : public QAbstractListModel, public QAbstractNativeEventFilter
{
Q_OBJECT
@@ -44,15 +46,21 @@ class AppMenuModel : public QAbstractListModel
ActionRole
};
- QVariant data(const QModelIndex &index, int role) const;
- int rowCount(const QModelIndex &parent = QModelIndex()) const;
- QHash<int, QByteArray> roleNames() const;
+ QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
+ QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE;
void updateApplicationMenu(const QString &serviceName, const QString &menuObjectPath);
bool menuAvailable() const;
void setMenuAvailable(bool set);
+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();
@@ -64,10 +72,13 @@ private Q_SLOTS:
private:
bool m_menuAvailable;
+ WId m_currentWindowId = 0;
+
QPointer<QMenu> m_menu;
QStringList m_activeMenu;
QList<QAction *> m_activeActions;
+ QDBusServiceWatcher *m_serviceWatcher;
QString m_serviceName;
QString m_menuObjectPath;
diff --git a/plugin/activewindowcontrolplugin.cpp b/plugin/appmenuplugin.cpp
similarity index 92%
rename from plugin/activewindowcontrolplugin.cpp
rename to plugin/appmenuplugin.cpp
index a5fe7de..76535cb 100644
--- a/plugin/activewindowcontrolplugin.cpp
+++ b/plugin/appmenuplugin.cpp
@@ -19,13 +19,13 @@
*
******************************************************************/
-#include "activewindowcontrolplugin.h"
+#include "appmenuplugin.h"
#include "appmenumodel.h"
#include <QtQml>
#include <QQmlEngine>
-void ActiveWindowControlPlugin::registerTypes(const char *uri)
+void AppmenuPlugin::registerTypes(const char *uri)
{
Q_ASSERT(uri == QLatin1String("org.kde.private.activeWindowControl"));
qmlRegisterType<AppMenuModel>(uri, 1, 0, "AppMenuModel");
diff --git a/plugin/activewindowcontrolplugin.h b/plugin/appmenuplugin.h
similarity index 90%
rename from plugin/activewindowcontrolplugin.h
rename to plugin/appmenuplugin.h
index 6c587e0..583cec8 100644
--- a/plugin/activewindowcontrolplugin.h
+++ b/plugin/appmenuplugin.h
@@ -19,12 +19,12 @@
*
******************************************************************/
-#ifndef ACTIVEWINDOWCONTROLPLUGIN_H
-#define ACTIVEWINDOWCONTROLPLUGIN_H
+#ifndef APPMENUPLUGIN_H
+#define APPMENUPLUGIN_H
#include <QQmlExtensionPlugin>
-class ActiveWindowControlPlugin : public QQmlExtensionPlugin
+class AppmenuPlugin : public QQmlExtensionPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
diff --git a/plugin/libdbusmenuqt/CMakeLists.txt b/plugin/libdbusmenuqt/CMakeLists.txt
index 49d3420..b92e5d6 100644
--- a/plugin/libdbusmenuqt/CMakeLists.txt
+++ b/plugin/libdbusmenuqt/CMakeLists.txt
@@ -18,5 +18,3 @@ target_link_libraries(dbusmenuqt
Qt5::DBus
Qt5::Widgets
)
-
-add_subdirectory(test)
diff --git a/plugin/libdbusmenuqt/dbusmenuimporter.cpp b/plugin/libdbusmenuqt/dbusmenuimporter.cpp
index 2aac5cd..707764f 100644
--- a/plugin/libdbusmenuqt/dbusmenuimporter.cpp
+++ b/plugin/libdbusmenuqt/dbusmenuimporter.cpp
@@ -362,6 +362,11 @@ void DBusMenuImporterPrivate::slotItemsPropertiesUpdated(const DBusMenuItemList
}
}
+QAction *DBusMenuImporter::actionForId(int id) const
+{
+ return d->m_actionForId.value(id);
+}
+
void DBusMenuImporter::slotItemActivationRequested(int id, uint /*timestamp*/)
{
QAction *action = d->m_actionForId.value(id);
@@ -426,12 +431,10 @@ void DBusMenuImporter::slotGetLayoutFinished(QDBusPendingCallWatcher *watcher)
sendClickedEvent(id);
});
- if (action->menu()) {
- auto menu = action->menu();
- connect(menu, &QMenu::aboutToShow, this, [menu, this]() {
- updateMenu(menu);
- });
+ if (QMenu *menuAction = action->menu()) {
+ connect(menuAction, &QMenu::aboutToShow, this, &DBusMenuImporter::slotMenuAboutToShow, Qt::UniqueConnection);
}
+ connect(menu, &QMenu::aboutToHide, this, &DBusMenuImporter::slotMenuAboutToHide, Qt::UniqueConnection);
menu->addAction(action);
} else {
@@ -441,6 +444,9 @@ void DBusMenuImporter::slotGetLayoutFinished(QDBusPendingCallWatcher *watcher)
filteredKeys.removeOne("toggle-type");
filteredKeys.removeOne("children-display");
d->updateAction(*it, dbusMenuItem.properties, filteredKeys);
+ // Move the action to the tail so we can keep the order same as the dbus request.
+ menu->removeAction(action);
+ menu->addAction(action);
}
}
@@ -518,6 +524,8 @@ void DBusMenuImporter::slotMenuAboutToShow()
QMenu *menu = qobject_cast<QMenu*>(sender());
Q_ASSERT(menu);
+ updateMenu(menu);
+
QAction *action = menu->menuAction();
Q_ASSERT(action);
diff --git a/plugin/libdbusmenuqt/dbusmenuimporter.h b/plugin/libdbusmenuqt/dbusmenuimporter.h
index 6d3fd4f..6f91ee9 100644
--- a/plugin/libdbusmenuqt/dbusmenuimporter.h
+++ b/plugin/libdbusmenuqt/dbusmenuimporter.h
@@ -47,6 +47,9 @@ class DBusMenuImporter : public QObject
~DBusMenuImporter() override;
+
+ QAction *actionForId(int id) const;
+
/**
* The menu created from listening to the DBusMenuExporter over DBus
*/
diff --git a/plugin/libdbusmenuqt/test/CMakeLists.txt b/plugin/libdbusmenuqt/test/CMakeLists.txt
deleted file mode 100644
index a950a6f..0000000
--- a/plugin/libdbusmenuqt/test/CMakeLists.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-add_executable(appmenutest main.cpp)
-target_link_libraries(appmenutest
- Qt5::Widgets)
diff --git a/plugin/libdbusmenuqt/test/README b/plugin/libdbusmenuqt/test/README
deleted file mode 100644
index 99dd020..0000000
--- a/plugin/libdbusmenuqt/test/README
+++ /dev/null
@@ -1,2 +0,0 @@
-App with a menu, designed for use testing appmenu QPTs/applets/kded modules
-small enough that we can attach debuggers and breakpoints without drowning in data
diff --git a/plugin/libdbusmenuqt/test/main.cpp b/plugin/libdbusmenuqt/test/main.cpp
deleted file mode 100644
index 74a0e05..0000000
--- a/plugin/libdbusmenuqt/test/main.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright 2017 David Edmundson <davidedmundson@kde.org>
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Library General Public License as
- * published by the Free Software Foundation; either version 2, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include <QApplication>
-
-#include <QMainWindow>
-#include <QMenuBar>
-#include <QDateTime>
-#include <QIcon>
-#include <QDebug>
-
-class MainWindow : public QMainWindow
-{
-public:
- MainWindow();
-};
-
-MainWindow::MainWindow() :
- QMainWindow()
-{
- /*set an initial menu with the following
- Menu A
- - Item
- - Checkable Item
- - Item With Icon
- - A separator
- - Menu B
- - Item B1
- Menu C
- - DynamicItem ${timestamp}
-
- TopLevelItem
- */
-
- QAction *t;
- auto menuA = new QMenu("Menu A", this);
- menuA->addAction("Item");
-
- t = menuA->addAction("Checkable Item");
- t->setCheckable(true);
-
- t = menuA->addAction(QIcon::fromTheme("document-edit"), "Item with icon");
-
- menuA->addSeparator();
-
- auto menuB = new QMenu("Menu B", this);
- menuB->addAction("Item B1");
- menuA->addMenu(menuB);
-
- menuBar()->addMenu(menuA);
-
- auto menuC = new QMenu("Menu C", this);
- connect(menuC, &QMenu::aboutToShow, this, [menuC]() {
- menuC->clear();
- menuC->addAction("Dynamic Item " + QDateTime::currentDateTime().toString());
- });
-
- menuBar()->addMenu(menuC);
-
- menuBar()->addAction("Top Level Item");
-}
-
-int main(int argc, char **argv)
-{
- QApplication app(argc, argv);
- MainWindow mw;
- mw.show();
- return app.exec();
-}
diff --git a/plugin/qmldir b/plugin/qmldir
index e09e45e..e85fbc8 100644
--- a/plugin/qmldir
+++ b/plugin/qmldir
@@ -1,3 +1,3 @@
module org.kde.private.activeWindowControl
-plugin activewindowcontrolplugin
+plugin appmenuplugin