From 93938d60b84df81733da7b3f697332fc76fe085b Mon Sep 17 00:00:00 2001 From: Kai Uwe Broulik Date: Wed, 11 Jan 2017 10:21:03 +0100 Subject: [PATCH] Restore global menu support This brings back global menu support in KWin. The DBusMenu infrastructure is different that we just read the DBus service name and menu object path from the windows rather than passing around window IDs on DBus which won't work on Wayland. Differential Revision: https://phabricator.kde.org/D3089 --- CMakeLists.txt | 3 + abstract_client.cpp | 55 +++++++++ abstract_client.h | 38 +++++- appmenu.cpp | 111 ++++++++++++++++++ appmenu.h | 74 ++++++++++++ atoms.cpp | 2 + atoms.h | 2 + client.cpp | 32 +++++ client.h | 8 ++ decorations/decoratedclient.cpp | 25 +++- decorations/decoratedclient.h | 8 +- decorations/settings.cpp | 1 + events.cpp | 4 + .../declarative-plugin/buttonsmodel.cpp | 1 + .../declarative-plugin/previewclient.cpp | 23 +++- .../declarative-plugin/previewclient.h | 9 +- .../declarative-plugin/previewsettings.cpp | 1 + manage.cpp | 6 + org.kde.kappmenu.xml | 28 +++++ shell_client.cpp | 15 +++ shell_client.h | 2 + useractions.cpp | 6 + workspace.cpp | 3 + workspace.h | 2 + 24 files changed, 454 insertions(+), 5 deletions(-) create mode 100644 appmenu.cpp create mode 100644 appmenu.h create mode 100644 org.kde.kappmenu.xml diff --git a/CMakeLists.txt b/CMakeLists.txt index efd2362dfa..affa160954 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -435,6 +435,7 @@ set(kwin_KDEINIT_SRCS wayland_server.cpp wayland_cursor_theme.cpp virtualkeyboard.cpp + appmenu.cpp ) if(KWIN_BUILD_TABBOX) @@ -485,6 +486,8 @@ qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS ${kwin_effects_dbus_xml} effects.h KWin: qt5_add_dbus_interface( kwin_KDEINIT_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.ScreenSaver.xml screenlocker_interface) +qt5_add_dbus_interface( kwin_KDEINIT_SRCS org.kde.kappmenu.xml appmenu_interface ) + qt5_add_resources( kwin_KDEINIT_SRCS resources.qrc ) ki18n_wrap_ui(kwin_KDEINIT_SRCS diff --git a/abstract_client.cpp b/abstract_client.cpp index a33821a91c..6183fd316b 100644 --- a/abstract_client.cpp +++ b/abstract_client.cpp @@ -18,6 +18,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "abstract_client.h" + +#include "appmenu.h" #include "decorations/decoratedclient.h" #include "decorations/decorationpalette.h" #include "decorations/decorationbridge.h" @@ -82,6 +84,10 @@ AbstractClient::AbstractClient() } } ); + + connect(ApplicationMenu::self(), &ApplicationMenu::applicationMenuEnabledChanged, this, [this] { + emit hasApplicationMenuChanged(hasApplicationMenu()); + }); } AbstractClient::~AbstractClient() @@ -1676,4 +1682,53 @@ QString AbstractClient::iconFromDesktopFile() const return df.readIcon(); } +bool AbstractClient::hasApplicationMenu() const +{ + return ApplicationMenu::self()->applicationMenuEnabled() && !m_applicationMenuServiceName.isEmpty() && !m_applicationMenuObjectPath.isEmpty(); +} + +void AbstractClient::updateApplicationMenuServiceName(const QString &serviceName) +{ + const bool old_hasApplicationMenu = hasApplicationMenu(); + + m_applicationMenuServiceName = serviceName; + + const bool new_hasApplicationMenu = hasApplicationMenu(); + + if (old_hasApplicationMenu != new_hasApplicationMenu) { + emit hasApplicationMenuChanged(new_hasApplicationMenu); + } +} + +void AbstractClient::updateApplicationMenuObjectPath(const QString &objectPath) +{ + const bool old_hasApplicationMenu = hasApplicationMenu(); + + m_applicationMenuObjectPath = objectPath; + + const bool new_hasApplicationMenu = hasApplicationMenu(); + + if (old_hasApplicationMenu != new_hasApplicationMenu) { + emit hasApplicationMenuChanged(new_hasApplicationMenu); + } +} + +void AbstractClient::setApplicationMenuActive(bool applicationMenuActive) +{ + if (m_applicationMenuActive != applicationMenuActive) { + m_applicationMenuActive = applicationMenuActive; + emit applicationMenuActiveChanged(applicationMenuActive); + } +} + +void AbstractClient::showApplicationMenu(int actionId) +{ + if (isDecorated()) { + decoration()->showApplicationMenu(actionId); + } else { + // we don't know where the application menu button will be, show it in the top left corner instead + Workspace::self()->showApplicationMenu(QRect(), this, actionId); + } +} + } diff --git a/abstract_client.h b/abstract_client.h index 25feb7f821..e509fac939 100644 --- a/abstract_client.h +++ b/abstract_client.h @@ -255,6 +255,16 @@ class KWIN_EXPORT AbstractClient : public Toplevel * (e.g. "/opt/kde/share/org.kde.foo.desktop") in case it's not in a standard location. **/ Q_PROPERTY(QByteArray desktopFileName READ desktopFileName NOTIFY desktopFileNameChanged) + + /** + * Whether an application menu is available for this Client + */ + Q_PROPERTY(bool hasApplicationMenu READ hasApplicationMenu NOTIFY hasApplicationMenuChanged) + /** + * Whether the application menu for this Client is currently opened + */ + Q_PROPERTY(bool applicationMenuActive READ applicationMenuActive NOTIFY applicationMenuActiveChanged) + public: virtual ~AbstractClient(); @@ -624,6 +634,25 @@ public: // TODO: remove boolean trap static bool belongToSameApplication(const AbstractClient* c1, const AbstractClient* c2, bool active_hack = false); + bool hasApplicationMenu() const; + bool applicationMenuActive() const { + return m_applicationMenuActive; + } + void setApplicationMenuActive(bool applicationMenuActive); + + QString applicationMenuServiceName() const { + return m_applicationMenuServiceName; + } + QString applicationMenuObjectPath() const { + return m_applicationMenuObjectPath; + } + + /** + * Request showing the application menu bar + * @param actionId The DBus menu ID of the action that should be highlighted, 0 for the root menu + */ + void showApplicationMenu(int actionId); + public Q_SLOTS: virtual void closeWindow() = 0; @@ -663,6 +692,8 @@ Q_SIGNALS: void shadeableChanged(bool); void maximizeableChanged(bool); void desktopFileNameChanged(); + void hasApplicationMenuChanged(bool); + void applicationMenuActiveChanged(bool); protected: AbstractClient(); @@ -944,6 +975,9 @@ protected: void setDesktopFileName(const QByteArray &name); QString iconFromDesktopFile() const; + void updateApplicationMenuServiceName(const QString &serviceName); + void updateApplicationMenuObjectPath(const QString &objectPath); + private: void handlePaletteChange(); QSharedPointer m_tabBoxClient; @@ -1010,9 +1044,11 @@ private: QPointer client; QElapsedTimer doubleClickTimer; } m_decoration; - QByteArray m_desktopFileName; + bool m_applicationMenuActive = false; + QString m_applicationMenuServiceName; + QString m_applicationMenuObjectPath; static bool s_haveResizeEffect; }; diff --git a/appmenu.cpp b/appmenu.cpp new file mode 100644 index 0000000000..4ce96f511a --- /dev/null +++ b/appmenu.cpp @@ -0,0 +1,111 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (c) 2011 Lionel Chauvin +Copyright (c) 2011,2012 Cédric Bellegarde +Copyright (C) 2013 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, 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 General Public License +along with this program. If not, see . +*********************************************************************/ +#include "appmenu.h" +#include "client.h" +#include "workspace.h" +#include + +#include + +using namespace KWin; + +KWIN_SINGLETON_FACTORY(ApplicationMenu) + +ApplicationMenu::ApplicationMenu(QObject *parent) + : QObject(parent) + , m_appmenuInterface(new OrgKdeKappmenuInterface(QStringLiteral("org.kde.kappmenu"), QStringLiteral("/KAppMenu"), QDBusConnection::sessionBus(), this)) +{ + connect(m_appmenuInterface, &OrgKdeKappmenuInterface::showRequest, this, &ApplicationMenu::slotShowRequest); + connect(m_appmenuInterface, &OrgKdeKappmenuInterface::menuShown, this, &ApplicationMenu::slotMenuShown); + connect(m_appmenuInterface, &OrgKdeKappmenuInterface::menuHidden, this, &ApplicationMenu::slotMenuHidden); + connect(m_appmenuInterface, &OrgKdeKappmenuInterface::reconfigured, this, &ApplicationMenu::slotReconfigured); + + updateApplicationMenuEnabled(); +} + +ApplicationMenu::~ApplicationMenu() +{ + s_self = nullptr; +} + +void ApplicationMenu::slotReconfigured() +{ + updateApplicationMenuEnabled(); +} + +bool ApplicationMenu::applicationMenuEnabled() const +{ + return m_applicationMenuEnabled; +} + +void ApplicationMenu::updateApplicationMenuEnabled() +{ + const bool old_enabled = m_applicationMenuEnabled; + + KConfigGroup config(KSharedConfig::openConfig(QStringLiteral("kdeglobals")), QStringLiteral("Appmenu Style")); + const QString &menuStyle = config.readEntry(QStringLiteral("Style")); + + const bool enabled = (menuStyle == QLatin1String("Decoration")); + + if (old_enabled != enabled) { + m_applicationMenuEnabled = enabled; + emit applicationMenuEnabledChanged(enabled); + } +} + +void ApplicationMenu::slotShowRequest(const QString &serviceName, const QDBusObjectPath &menuObjectPath, int actionId) +{ + if (AbstractClient *c = findAbstractClientWithApplicationMenu(serviceName, menuObjectPath)) { + c->showApplicationMenu(actionId); + } +} + +void ApplicationMenu::slotMenuShown(const QString &serviceName, const QDBusObjectPath &menuObjectPath) +{ + if (AbstractClient *c = findAbstractClientWithApplicationMenu(serviceName, menuObjectPath)) { + c->setApplicationMenuActive(true); + } +} + +void ApplicationMenu::slotMenuHidden(const QString &serviceName, const QDBusObjectPath &menuObjectPath) +{ + if (AbstractClient *c = findAbstractClientWithApplicationMenu(serviceName, menuObjectPath)) { + c->setApplicationMenuActive(false); + } +} + +void ApplicationMenu::showApplicationMenu(const QPoint &p, AbstractClient *c, int actionId) +{ + m_appmenuInterface->showMenu(p.x(), p.y(), c->applicationMenuServiceName(), QDBusObjectPath(c->applicationMenuObjectPath()), actionId); +} + +AbstractClient *ApplicationMenu::findAbstractClientWithApplicationMenu(const QString &serviceName, const QDBusObjectPath &menuObjectPath) +{ + if (serviceName.isEmpty() || menuObjectPath.path().isEmpty()) { + return nullptr; + } + + return Workspace::self()->findAbstractClient([&](const AbstractClient *c) { + return c->applicationMenuServiceName() == serviceName + && c->applicationMenuObjectPath() == menuObjectPath.path(); + }); +} diff --git a/appmenu.h b/appmenu.h new file mode 100644 index 0000000000..1a6af23e2b --- /dev/null +++ b/appmenu.h @@ -0,0 +1,74 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (c) 2011 Lionel Chauvin +Copyright (c) 2011,2012 Cédric Bellegarde +Copyright (C) 2013 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, 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 General Public License +along with this program. If not, see . +*********************************************************************/ +#ifndef KWIN_APPLICATIONMENU_H +#define KWIN_APPLICATIONMENU_H +// KWin +#include +// Qt +#include +// xcb +#include + +class QPoint; +class OrgKdeKappmenuInterface; +class QDBusObjectPath; + +namespace KWin +{ + +class AbstractClient; + +class ApplicationMenu : public QObject +{ + Q_OBJECT + +public: + ~ApplicationMenu() override; + + void showApplicationMenu(const QPoint &pos, AbstractClient *c, int actionId); + + bool applicationMenuEnabled() const; + +signals: + void applicationMenuEnabledChanged(bool enabled); + +private Q_SLOTS: + void slotReconfigured(); + void slotShowRequest(const QString &serviceName, const QDBusObjectPath &menuObjectPath, int actionId); + void slotMenuShown(const QString &serviceName, const QDBusObjectPath &menuObjectPath); + void slotMenuHidden(const QString &serviceName, const QDBusObjectPath &menuObjectPath); + +private: + void updateApplicationMenuEnabled(); + + OrgKdeKappmenuInterface *m_appmenuInterface; + + AbstractClient *findAbstractClientWithApplicationMenu(const QString &serviceName, const QDBusObjectPath &menuObjectPath); + + bool m_applicationMenuEnabled = false; + + KWIN_SINGLETON(ApplicationMenu) +}; + +} + +#endif // KWIN_APPLICATIONMENU_H diff --git a/atoms.cpp b/atoms.cpp index 55a8aa368f..ee9e8e29e7 100644 --- a/atoms.cpp +++ b/atoms.cpp @@ -59,6 +59,8 @@ Atoms::Atoms() , kwin_dbus_service(QByteArrayLiteral("_ORG_KDE_KWIN_DBUS_SERVICE")) , utf8_string(QByteArrayLiteral("UTF8_STRING")) , wl_surface_id(QByteArrayLiteral("WL_SURFACE_ID")) + , kde_net_wm_appmenu_service_name(QByteArrayLiteral("_KDE_NET_WM_APPMENU_SERVICE_NAME")) + , kde_net_wm_appmenu_object_path(QByteArrayLiteral("_KDE_NET_WM_APPMENU_OBJECT_PATH")) , m_dtSmWindowInfo(QByteArrayLiteral("_DT_SM_WINDOW_INFO")) , m_motifSupport(QByteArrayLiteral("_MOTIF_WM_INFO")) , m_helpersRetrieved(false) diff --git a/atoms.h b/atoms.h index 4b1e97c69b..2c7b2d9f13 100644 --- a/atoms.h +++ b/atoms.h @@ -68,6 +68,8 @@ public: Xcb::Atom kwin_dbus_service; Xcb::Atom utf8_string; Xcb::Atom wl_surface_id; + Xcb::Atom kde_net_wm_appmenu_service_name; + Xcb::Atom kde_net_wm_appmenu_object_path; /** * @internal diff --git a/client.cpp b/client.cpp index 99934d2c33..1413951ac4 100644 --- a/client.cpp +++ b/client.cpp @@ -2152,5 +2152,37 @@ QSize Client::resizeIncrements() const return m_geometryHints.resizeIncrements(); } +Xcb::StringProperty Client::fetchApplicationMenuServiceName() const +{ + return Xcb::StringProperty(m_client, atoms->kde_net_wm_appmenu_service_name); +} + +void Client::readApplicationMenuServiceName(Xcb::StringProperty &property) +{ + updateApplicationMenuServiceName(QString::fromUtf8(property)); +} + +void Client::checkApplicationMenuServiceName() +{ + Xcb::StringProperty property = fetchApplicationMenuServiceName(); + readApplicationMenuServiceName(property); +} + +Xcb::StringProperty Client::fetchApplicationMenuObjectPath() const +{ + return Xcb::StringProperty(m_client, atoms->kde_net_wm_appmenu_object_path); +} + +void Client::readApplicationMenuObjectPath(Xcb::StringProperty &property) +{ + updateApplicationMenuObjectPath(QString::fromUtf8(property)); +} + +void Client::checkApplicationMenuObjectPath() +{ + Xcb::StringProperty property = fetchApplicationMenuObjectPath(); + readApplicationMenuObjectPath(property); +} + } // namespace diff --git a/client.h b/client.h index 8be241dd2c..25deaf0461 100644 --- a/client.h +++ b/client.h @@ -331,6 +331,14 @@ public: **/ void showOnScreenEdge() override; + Xcb::StringProperty fetchApplicationMenuServiceName() const; + void readApplicationMenuServiceName(Xcb::StringProperty &property); + void checkApplicationMenuServiceName(); + + Xcb::StringProperty fetchApplicationMenuObjectPath() const; + void readApplicationMenuObjectPath(Xcb::StringProperty &property); + void checkApplicationMenuObjectPath(); + static void cleanupX11(); public Q_SLOTS: diff --git a/decorations/decoratedclient.cpp b/decorations/decoratedclient.cpp index 0bb012c50a..907b80d08c 100644 --- a/decorations/decoratedclient.cpp +++ b/decorations/decoratedclient.cpp @@ -39,7 +39,7 @@ namespace Decoration DecoratedClientImpl::DecoratedClientImpl(AbstractClient *client, KDecoration2::DecoratedClient *decoratedClient, KDecoration2::Decoration *decoration) : QObject() - , DecoratedClientPrivate(decoratedClient, decoration) + , ApplicationMenuEnabledDecoratedClientPrivate(decoratedClient, decoration) , m_client(client) , m_clientSize(client->clientSize()) , m_renderer(nullptr) @@ -110,6 +110,9 @@ DecoratedClientImpl::DecoratedClientImpl(AbstractClient *client, KDecoration2::D connect(client, &AbstractClient::maximizeableChanged, decoratedClient, &KDecoration2::DecoratedClient::maximizeableChanged); connect(client, &AbstractClient::paletteChanged, decoratedClient, &KDecoration2::DecoratedClient::paletteChanged); + + connect(client, &AbstractClient::hasApplicationMenuChanged, decoratedClient, &KDecoration2::DecoratedClient::hasApplicationMenuChanged); + connect(client, &AbstractClient::applicationMenuActiveChanged, decoratedClient, &KDecoration2::DecoratedClient::applicationMenuActiveChanged); } DecoratedClientImpl::~DecoratedClientImpl() = default; @@ -203,6 +206,16 @@ void DecoratedClientImpl::requestShowWindowMenu() Workspace::self()->showWindowMenu(QRect(Cursor::pos(), Cursor::pos()), m_client); } +void DecoratedClientImpl::requestShowApplicationMenu(const QRect &rect, int actionId) +{ + Workspace::self()->showApplicationMenu(rect, m_client, actionId); +} + +void DecoratedClientImpl::showApplicationMenu(int actionId) +{ + decoration()->showApplicationMenu(actionId); +} + void DecoratedClientImpl::requestToggleMaximization(Qt::MouseButtons buttons) { QMetaObject::invokeMethod(this, "delayedRequestToggleMaximization", Qt::QueuedConnection, Q_ARG(Options::WindowOperation, options->operationMaxButtonClick(buttons))); @@ -265,6 +278,16 @@ Qt::Edges DecoratedClientImpl::adjacentScreenEdges() const return edges; } +bool DecoratedClientImpl::hasApplicationMenu() const +{ + return m_client->hasApplicationMenu(); +} + +bool DecoratedClientImpl::isApplicationMenuActive() const +{ + return m_client->applicationMenuActive(); +} + void DecoratedClientImpl::createRenderer() { if (Compositor::self()->hasScene()) { diff --git a/decorations/decoratedclient.h b/decorations/decoratedclient.h index d31b1dfdd4..e01a2e6a05 100644 --- a/decorations/decoratedclient.h +++ b/decorations/decoratedclient.h @@ -35,7 +35,7 @@ namespace Decoration class Renderer; -class DecoratedClientImpl : public QObject, public KDecoration2::DecoratedClientPrivate +class DecoratedClientImpl : public QObject, public KDecoration2::ApplicationMenuEnabledDecoratedClientPrivate { Q_OBJECT public: @@ -69,16 +69,22 @@ public: Qt::Edges adjacentScreenEdges() const override; + bool hasApplicationMenu() const override; + bool isApplicationMenuActive() const override; + void requestClose() override; void requestContextHelp() override; void requestToggleMaximization(Qt::MouseButtons buttons) override; void requestMinimize() override; void requestShowWindowMenu() override; + void requestShowApplicationMenu(const QRect &rect, int actionId) override; void requestToggleKeepAbove() override; void requestToggleKeepBelow() override; void requestToggleOnAllDesktops() override; void requestToggleShade() override; + void showApplicationMenu(int actionId); + AbstractClient *client() { return m_client; } diff --git a/decorations/settings.cpp b/decorations/settings.cpp index c1eb22c021..92deb2519c 100644 --- a/decorations/settings.cpp +++ b/decorations/settings.cpp @@ -155,6 +155,7 @@ void SettingsImpl::readSettings() KConfigGroup config = kwinApp()->config()->group(QStringLiteral("org.kde.kdecoration2")); const auto &left = readDecorationButtons(config, "ButtonsOnLeft", QVector({ KDecoration2::DecorationButtonType::Menu, + KDecoration2::DecorationButtonType::ApplicationMenu, KDecoration2::DecorationButtonType::OnAllDesktops })); if (left != m_leftButtons) { diff --git a/events.cpp b/events.cpp index 9740e493cb..d100747a1f 100644 --- a/events.cpp +++ b/events.cpp @@ -923,6 +923,10 @@ void Client::propertyNotifyEvent(xcb_property_notify_event_t *e) updateShowOnScreenEdge(); else if (e->atom == atoms->gtk_frame_extents) detectGtkFrameExtents(); + else if (e->atom == atoms->kde_net_wm_appmenu_service_name) + checkApplicationMenuServiceName(); + else if (e->atom == atoms->kde_net_wm_appmenu_object_path) + checkApplicationMenuObjectPath(); break; } } diff --git a/kcmkwin/kwindecoration/declarative-plugin/buttonsmodel.cpp b/kcmkwin/kwindecoration/declarative-plugin/buttonsmodel.cpp index b2ac124ed3..26d7790156 100644 --- a/kcmkwin/kwindecoration/declarative-plugin/buttonsmodel.cpp +++ b/kcmkwin/kwindecoration/declarative-plugin/buttonsmodel.cpp @@ -38,6 +38,7 @@ ButtonsModel::ButtonsModel(const QVector< DecorationButtonType > &buttons, QObje ButtonsModel::ButtonsModel(QObject* parent) : ButtonsModel(QVector({ DecorationButtonType::Menu, + DecorationButtonType::ApplicationMenu, DecorationButtonType::OnAllDesktops, DecorationButtonType::Minimize, DecorationButtonType::Maximize, diff --git a/kcmkwin/kwindecoration/declarative-plugin/previewclient.cpp b/kcmkwin/kwindecoration/declarative-plugin/previewclient.cpp index 146915f533..7d7c413c2f 100644 --- a/kcmkwin/kwindecoration/declarative-plugin/previewclient.cpp +++ b/kcmkwin/kwindecoration/declarative-plugin/previewclient.cpp @@ -36,7 +36,7 @@ namespace Preview PreviewClient::PreviewClient(DecoratedClient *c, Decoration *decoration) : QObject(decoration) - , DecoratedClientPrivate(c, decoration) + , ApplicationMenuEnabledDecoratedClientPrivate(c, decoration) , m_colorSchemeManager(new KColorSchemeManager(this)) , m_colorSchemeIndex(0) , m_icon(QIcon::fromTheme(QStringLiteral("start-here-kde"))) @@ -309,6 +309,16 @@ Qt::Edges PreviewClient::adjacentScreenEdges() const return edges; } +bool PreviewClient::hasApplicationMenu() const +{ + return true; +} + +bool PreviewClient::isApplicationMenuActive() const +{ + return false; +} + bool PreviewClient::bordersBottomEdge() const { return m_bordersBottomEdge; @@ -408,6 +418,17 @@ void PreviewClient::requestShowWindowMenu() emit showWindowMenuRequested(); } +void PreviewClient::requestShowApplicationMenu(const QRect &rect, int actionId) +{ + Q_UNUSED(rect); + Q_UNUSED(actionId); +} + +void PreviewClient::showApplicationMenu(int actionId) +{ + Q_UNUSED(actionId) +} + void PreviewClient::requestToggleOnAllDesktops() { setDesktop(isOnAllDesktops() ? 1 : -1); diff --git a/kcmkwin/kwindecoration/declarative-plugin/previewclient.h b/kcmkwin/kwindecoration/declarative-plugin/previewclient.h index 806a4fbd3f..47ff2c415d 100644 --- a/kcmkwin/kwindecoration/declarative-plugin/previewclient.h +++ b/kcmkwin/kwindecoration/declarative-plugin/previewclient.h @@ -33,7 +33,7 @@ namespace KDecoration2 { namespace Preview { -class PreviewClient : public QObject, public DecoratedClientPrivate +class PreviewClient : public QObject, public ApplicationMenuEnabledDecoratedClientPrivate { Q_OBJECT Q_PROPERTY(KDecoration2::Decoration *decoration READ decoration CONSTANT) @@ -97,6 +97,9 @@ public: QColor color(ColorGroup group, ColorRole role) const override; Qt::Edges adjacentScreenEdges() const override; + bool hasApplicationMenu() const override; + bool isApplicationMenuActive() const override; + void requestClose() override; void requestContextHelp() override; void requestToggleMaximization(Qt::MouseButtons buttons) override; @@ -105,8 +108,11 @@ public: void requestToggleKeepBelow() override; void requestToggleShade() override; void requestShowWindowMenu() override; + void requestShowApplicationMenu(const QRect &rect, int actionId) override; void requestToggleOnAllDesktops() override; + void showApplicationMenu(int actionId); + void setCaption(const QString &caption); void setActive(bool active); void setCloseable(bool closeable); @@ -176,6 +182,7 @@ Q_SIGNALS: void bordersBottomEdgeChanged(bool); void showWindowMenuRequested(); + void showApplicationMenuRequested(); void minimizeRequested(); void closeRequested(); diff --git a/kcmkwin/kwindecoration/declarative-plugin/previewsettings.cpp b/kcmkwin/kwindecoration/declarative-plugin/previewsettings.cpp index c5c767a652..dd996dab3c 100644 --- a/kcmkwin/kwindecoration/declarative-plugin/previewsettings.cpp +++ b/kcmkwin/kwindecoration/declarative-plugin/previewsettings.cpp @@ -72,6 +72,7 @@ PreviewSettings::PreviewSettings(DecorationSettings *parent) , m_closeOnDoubleClick(false) , m_leftButtons(new ButtonsModel(QVector({ DecorationButtonType::Menu, + DecorationButtonType::ApplicationMenu, DecorationButtonType::OnAllDesktops }), this)) , m_rightButtons(new ButtonsModel(QVector({ diff --git a/manage.cpp b/manage.cpp index bf372a4d75..a1f927b928 100644 --- a/manage.cpp +++ b/manage.cpp @@ -104,6 +104,9 @@ bool Client::manage(xcb_window_t w, bool isMapped) auto firstInTabBoxCookie = fetchFirstInTabBox(); auto transientCookie = fetchTransient(); auto activitiesCookie = fetchActivities(); + auto applicationMenuServiceNameCookie = fetchApplicationMenuServiceName(); + auto applicationMenuObjectPathCookie = fetchApplicationMenuObjectPath(); + m_geometryHints.init(window()); m_motif.init(window()); info = new WinInfo(this, m_client, rootWindow(), properties, properties2); @@ -391,6 +394,9 @@ bool Client::manage(xcb_window_t w, bool isMapped) readColorScheme(colorSchemeCookie); + readApplicationMenuServiceName(applicationMenuServiceNameCookie); + readApplicationMenuObjectPath(applicationMenuObjectPathCookie); + updateDecoration(false); // Also gravitates // TODO: Is CentralGravity right here, when resizing is done after gravitating? plainResize(rules()->checkSize(sizeForClientSize(geom.size()), !isMapped)); diff --git a/org.kde.kappmenu.xml b/org.kde.kappmenu.xml new file mode 100644 index 0000000000..d29d3ee86b --- /dev/null +++ b/org.kde.kappmenu.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/shell_client.cpp b/shell_client.cpp index 172f10e2a4..db96bafe74 100644 --- a/shell_client.cpp +++ b/shell_client.cpp @@ -56,6 +56,8 @@ along with this program. If not, see . using namespace KWayland::Server; static const QByteArray s_schemePropertyName = QByteArrayLiteral("KDE_COLOR_SCHEME_PATH"); +static const QByteArray s_appMenuServiceNamePropertyName = QByteArrayLiteral("KDE_APPMENU_SERVICE_NAME"); +static const QByteArray s_appMenuObjectPathPropertyName = QByteArrayLiteral("KDE_APPMENU_OBJECT_PATH"); namespace KWin { @@ -252,6 +254,7 @@ void ShellClient::init() } updateColorScheme(QString()); + updateApplicationMenu(); } void ShellClient::destroyClient() @@ -1234,6 +1237,10 @@ bool ShellClient::eventFilter(QObject *watched, QEvent *event) QDynamicPropertyChangeEvent *pe = static_cast(event); if (pe->propertyName() == s_schemePropertyName) { updateColorScheme(rules()->checkDecoColor(m_qtExtendedSurface->property(pe->propertyName().constData()).toString())); + } else if (pe->propertyName() == s_appMenuServiceNamePropertyName) { + updateApplicationMenuServiceName(m_qtExtendedSurface->property(pe->propertyName().constData()).toString()); + } else if (pe->propertyName() == s_appMenuObjectPathPropertyName) { + updateApplicationMenuObjectPath(m_qtExtendedSurface->property(pe->propertyName().constData()).toString()); } } return false; @@ -1466,4 +1473,12 @@ void ShellClient::killWindow() QTimer::singleShot(5000, c, &ClientConnection::destroy); } +void ShellClient::updateApplicationMenu() +{ + if (m_qtExtendedSurface) { + updateApplicationMenuServiceName(m_qtExtendedSurface->property(s_appMenuObjectPathPropertyName).toString()); + updateApplicationMenuObjectPath(m_qtExtendedSurface->property(s_appMenuServiceNamePropertyName).toString()); + } +} + } diff --git a/shell_client.h b/shell_client.h index 6c3f38eb86..54fca4d187 100644 --- a/shell_client.h +++ b/shell_client.h @@ -137,6 +137,8 @@ public: // TODO: const-ref void placeIn(QRect &area); + void updateApplicationMenu(); + protected: void addDamage(const QRegion &damage) override; bool belongsToSameApplication(const AbstractClient *other, bool active_hack) const override; diff --git a/useractions.cpp b/useractions.cpp index 983ad8795f..502be96b3c 100755 --- a/useractions.cpp +++ b/useractions.cpp @@ -45,6 +45,7 @@ along with this program. If not, see . #include "activities.h" #include #endif +#include "appmenu.h" #include @@ -1702,6 +1703,11 @@ void Workspace::showWindowMenu(const QRect &pos, AbstractClient* cl) m_userActionsMenu->show(pos, cl); } +void Workspace::showApplicationMenu(const QRect &pos, AbstractClient *c, int actionId) +{ + ApplicationMenu::self()->showApplicationMenu(c->geometry().topLeft() + pos.bottomLeft(), c, actionId); +} + /*! Closes the popup client */ diff --git a/workspace.cpp b/workspace.cpp index acbbcf4879..360f9db6ef 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -27,6 +27,7 @@ along with this program. If not, see . #ifdef KWIN_BUILD_ACTIVITIES #include "activities.h" #endif +#include "appmenu.h" #include "atoms.h" #include "client.h" #include "composite.h" @@ -128,6 +129,8 @@ Workspace::Workspace(const QString &sessionKey) // If KWin was already running it saved its configuration after loosing the selection -> Reread QFuture reparseConfigFuture = QtConcurrent::run(options, &Options::reparseConfiguration); + ApplicationMenu::create(this); + _self = this; // first initialize the extensions diff --git a/workspace.h b/workspace.h index d2e49bd72a..b390f9614c 100644 --- a/workspace.h +++ b/workspace.h @@ -285,6 +285,8 @@ public: return m_userActionsMenu; } + void showApplicationMenu(const QRect &pos, AbstractClient *c, int actionId); + void updateMinimizedOfTransients(AbstractClient*); void updateOnAllDesktopsOfTransients(AbstractClient*); void checkTransients(xcb_window_t w);