From 4b07ae7457a06b1e7528ac216ab34cf6aa4a92f1 Mon Sep 17 00:00:00 2001 From: Michail Vourlakos Date: Sat, 3 Feb 2018 15:00:44 +0200 Subject: [PATCH] move dock contextmenu to dockmenumanager --- app/CMakeLists.txt | 1 + app/dock/dockmenumanager.cpp | 506 +++++++++++++++++++++++++++++++++++ app/dock/dockmenumanager.h | 78 ++++++ app/dock/dockview.cpp | 466 ++------------------------------ app/dock/dockview.h | 9 +- 5 files changed, 603 insertions(+), 457 deletions(-) create mode 100644 app/dock/dockmenumanager.cpp create mode 100644 app/dock/dockmenumanager.h diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index a1386e5c7..51a79ae15 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -21,6 +21,7 @@ set(lattedock-app_SRCS layoutsDelegates/colorcmbboxdelegate.cpp layoutsDelegates/colorcmbboxitemdelegate.cpp layoutsDelegates/activitycmbboxdelegate.cpp + dock/dockmenumanager.cpp dock/dockview.cpp dock/dockconfigview.cpp dock/panelshadows.cpp diff --git a/app/dock/dockmenumanager.cpp b/app/dock/dockmenumanager.cpp new file mode 100644 index 000000000..d6119ba1d --- /dev/null +++ b/app/dock/dockmenumanager.cpp @@ -0,0 +1,506 @@ +/* +* Copyright 2018 Michail Vourlakos +* +* This file is part of Latte-Dock +* +* Latte-Dock 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. +* +* Latte-Dock 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 "dockmenumanager.h" + +#include "dockview.h" +#include "visibilitymanager.h" +#include "../dockcorona.h" +#include "../layoutmanager.h" + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace Latte { + +DockMenuManager::DockMenuManager(DockView *view) : + QObject(view), + m_dockView(view) +{ +} + +DockMenuManager::~DockMenuManager() +{ +} + +QMenu *DockMenuManager::contextMenu() +{ + return m_contextMenu; +} + +void DockMenuManager::menuAboutToHide() +{ + if (!m_dockView) { + return; + } + + m_contextMenu = 0; + m_dockView->visibility()->setBlockHiding(false); + emit contextMenuChanged(); +} + +bool DockMenuManager::mousePressEvent(QMouseEvent *event) +{ + //qDebug() << "Step -1 ..."; + if (!event || !m_dockView->containment()) { + return false; + } + + //qDebug() << "Step 0..."; + + //even if the menu is executed synchronously, other events may be processed + //by the qml incubator when plasma is loading, so we need to guard there + if (m_contextMenu) { + //qDebug() << "Step 0.5 ..."; + m_contextMenu->close(); + m_contextMenu = 0; + emit contextMenuChanged(); + // PlasmaQuick::ContainmentView::mousePressEvent(event); + return true; + } + + //qDebug() << "1 ..."; + const QString trigger = Plasma::ContainmentActions::eventToString(event); + + if (trigger == "RightButton;NoModifier") { + Plasma::ContainmentActions *plugin = m_dockView->containment()->containmentActions().value(trigger); + + if (!plugin || plugin->contextualActions().isEmpty()) { + event->setAccepted(false); + return false; + } + + //qDebug() << "2 ..."; + //the plugin can be a single action or a context menu + //Don't have an action list? execute as single action + //and set the event position as action data + /*if (plugin->contextualActions().length() == 1) { + QAction *action = plugin->contextualActions().at(0); + action->setData(event->pos()); + action->trigger(); + event->accept(); + return; + }*/ + //FIXME: very inefficient appletAt() implementation + Plasma::Applet *applet = 0; + bool inSystray = false; + + //! initialize the appletContainsMethod on the first right click + if (!m_appletContainsMethod.isValid()) { + updateAppletContainsMethod(); + } + + foreach (Plasma::Applet *appletTemp, m_dockView->containment()->applets()) { + PlasmaQuick::AppletQuickItem *ai = appletTemp->property("_plasma_graphicObject").value(); + + bool appletContainsMouse = false; + + if (m_appletContainsMethod.isValid()) { + QVariant retVal; + m_appletContainsMethod.invoke(m_appletContainsMethodItem, Qt::DirectConnection, Q_RETURN_ARG(QVariant, retVal) + , Q_ARG(QVariant, appletTemp->id()), Q_ARG(QVariant, event->pos())); + appletContainsMouse = retVal.toBool(); + } else { + appletContainsMouse = ai->contains(ai->mapFromItem(m_dockView->contentItem(), event->pos())); + } + + if (ai && ai->isVisible() && appletContainsMouse) { + applet = ai->applet(); + KPluginMetaData meta = applet->kPackage().metadata(); + + //Try to find applets inside a systray + if (meta.pluginId() == "org.kde.plasma.systemtray" || + meta.pluginId() == "org.nomad.systemtray") { + auto systrayId = applet->config().readEntry("SystrayContainmentId"); + applet = 0; + inSystray = true; + Plasma::Containment *cont = containmentById(systrayId.toInt()); + + if (cont) { + foreach (Plasma::Applet *appletCont, cont->applets()) { + PlasmaQuick::AppletQuickItem *ai2 = appletCont->property("_plasma_graphicObject").value(); + + if (ai2 && ai2->isVisible() && ai2->contains(ai2->mapFromItem(m_dockView->contentItem(), event->pos()))) { + applet = ai2->applet(); + break; + } + } + } + + break; + } else { + ai = 0; + } + } + } + + if (!applet && !inSystray) { + applet = m_dockView->containment(); + } + + //qDebug() << "3 ..."; + + if (applet) { + const auto &provides = KPluginMetaData::readStringList(applet->pluginMetaData().rawData(), QStringLiteral("X-Plasma-Provides")); + + //qDebug() << "3.5 ..."; + + if (!provides.contains(QLatin1String("org.kde.plasma.multitasking"))) { + //qDebug() << "4..."; + QMenu *desktopMenu = new QMenu; + desktopMenu->setAttribute(Qt::WA_DeleteOnClose); + m_contextMenu = desktopMenu; + + //! deprecated old code that can be removed if the following plasma approach doesnt + //! create any issues with context menu creation in Latte + /*if (m_dockView->mouseGrabberItem()) { + //workaround, this fixes for me most of the right click menu behavior + m_dockView->mouseGrabberItem()->ungrabMouse(); + return; + }*/ + + //!plasma official code + //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 = [this]() { + if (m_dockView->mouseGrabberItem()) { + m_dockView->mouseGrabberItem()->ungrabMouse(); + } + }; + + //pre 5.8.0 QQuickWindow code is "item->grabMouse(); sendEvent(item, mouseEvent)" + //post 5.8.0 QQuickWindow code is sendEvent(item, mouseEvent); item->grabMouse() + if (QVersionNumber::fromString(qVersion()) > QVersionNumber(5, 8, 0)) { + QTimer::singleShot(0, this, ungrabMouseHack); + } else { + ungrabMouseHack(); + } + + //end workaround + //!end of plasma official code(workaround) + + //qDebug() << "5 ..."; + + if (applet) { + //qDebug() << "5.3 ..."; + emit applet->contextualActionsAboutToShow(); + addAppletActions(desktopMenu, applet, event); + } else { + //qDebug() << "5.6 ..."; + emit m_dockView->containment()->contextualActionsAboutToShow(); + addContainmentActions(desktopMenu, event); + } + + //this is a workaround where Qt now creates the menu widget + //in .exec before oxygen can polish it and set the following attribute + desktopMenu->setAttribute(Qt::WA_TranslucentBackground); + //end workaround + QPoint pos = event->globalPos(); + + if (applet) { + //qDebug() << "6 ..."; + desktopMenu->adjustSize(); + + if (m_dockView->screen()) { + const QRect scr = m_dockView->screen()->geometry(); + int smallStep = 3; + int x = event->globalPos().x() + smallStep; + int y = event->globalPos().y() + smallStep; + + //qDebug()<globalPos().x() > scr.center().x()) { + x = event->globalPos().x() - desktopMenu->width() - smallStep; + } + + if (event->globalPos().y() > scr.center().y()) { + y = event->globalPos().y() - desktopMenu->height() - smallStep; + } + + pos = QPoint(x, y); + } + } + + //qDebug() << "7..."; + + if (desktopMenu->isEmpty()) { + //qDebug() << "7.5 ..."; + delete desktopMenu; + event->accept(); + return false; + } + + connect(desktopMenu, SIGNAL(aboutToHide()), this, SLOT(menuAboutToHide())); + m_dockView->visibility()->setBlockHiding(true); + desktopMenu->popup(pos); + event->setAccepted(true); + emit contextMenuChanged(); + return false; + } + + //qDebug() << "8 ..."; + } + + //qDebug() << "9 ..."; + } + + //qDebug() << "10 ..."; + emit contextMenuChanged(); + return true; + // PlasmaQuick::ContainmentView::mousePressEvent(event); +} + +//! update the appletContainsPos method from Panel view +void DockMenuManager::updateAppletContainsMethod() +{ + for (QQuickItem *item : m_dockView->contentItem()->childItems()) { + if (auto *metaObject = item->metaObject()) { + // not using QMetaObject::invokeMethod to avoid warnings when calling + // this on applets that don't have it or other child items since this + // is pretty much trial and error. + // Also, "var" arguments are treated as QVariant in QMetaObject + + int methodIndex = metaObject->indexOfMethod("appletContainsPos(QVariant,QVariant)"); + + if (methodIndex == -1) { + continue; + } + + m_appletContainsMethod = metaObject->method(methodIndex); + m_appletContainsMethodItem = item; + } + } +} + +void DockMenuManager::addAppletActions(QMenu *desktopMenu, Plasma::Applet *applet, QEvent *event) +{ + if (!m_dockView->containment()) { + return; + } + + foreach (QAction *action, applet->contextualActions()) { + if (action) { + desktopMenu->addAction(action); + } + } + + if (!applet->failedToLaunch()) { + QAction *runAssociatedApplication = applet->actions()->action(QStringLiteral("run associated application")); + + if (runAssociatedApplication && runAssociatedApplication->isEnabled()) { + desktopMenu->addAction(runAssociatedApplication); + } + + QAction *configureApplet = applet->actions()->action(QStringLiteral("configure")); + + if (configureApplet && configureApplet->isEnabled()) { + desktopMenu->addAction(configureApplet); + } + + QAction *appletAlternatives = applet->actions()->action(QStringLiteral("alternatives")); + + if (appletAlternatives && appletAlternatives->isEnabled() && m_dockView->containment()->isUserConfiguring()) { + desktopMenu->addAction(appletAlternatives); + } + } + + QMenu *containmentMenu = new QMenu(i18nc("%1 is the name of the containment", "%1 Options", m_dockView->containment()->title()), desktopMenu); + addContainmentActions(containmentMenu, event); + + if (!containmentMenu->isEmpty()) { + int enabled = 0; + //count number of real actions + QListIterator actionsIt(containmentMenu->actions()); + + while (enabled < 3 && actionsIt.hasNext()) { + QAction *action = actionsIt.next(); + + if (action->isVisible() && !action->isSeparator()) { + ++enabled; + } + } + + desktopMenu->addSeparator(); + + if (enabled) { + //if there is only one, don't create a submenu + // if (enabled < 2) { + foreach (QAction *action, containmentMenu->actions()) { + if (action->isVisible()) { + desktopMenu->addAction(action); + } + } + + // } else { + // desktopMenu->addMenu(containmentMenu); + // } + } + } + + if (m_dockView->containment()->immutability() == Plasma::Types::Mutable && + (m_dockView->containment()->containmentType() != Plasma::Types::PanelContainment || m_dockView->containment()->isUserConfiguring())) { + QAction *closeApplet = applet->actions()->action(QStringLiteral("remove")); + + //qDebug() << "checking for removal" << closeApplet; + if (closeApplet) { + if (!desktopMenu->isEmpty()) { + desktopMenu->addSeparator(); + } + + //qDebug() << "adding close action" << closeApplet->isEnabled() << closeApplet->isVisible(); + desktopMenu->addAction(closeApplet); + } + } +} + +void DockMenuManager::addContainmentActions(QMenu *desktopMenu, QEvent *event) +{ + if (!m_dockView->containment()) { + return; + } + + if (m_dockView->containment()->corona()->immutability() != Plasma::Types::Mutable && + !KAuthorized::authorizeAction(QStringLiteral("plasma/containment_actions"))) { + //qDebug() << "immutability"; + return; + } + + //this is what ContainmentPrivate::prepareContainmentActions was + const QString trigger = Plasma::ContainmentActions::eventToString(event); + //"RightButton;NoModifier" + Plasma::ContainmentActions *plugin = m_dockView->containment()->containmentActions().value(trigger); + + if (!plugin) { + return; + } + + if (plugin->containment() != m_dockView->containment()) { + plugin->setContainment(m_dockView->containment()); + // now configure it + KConfigGroup cfg(m_dockView->containment()->corona()->config(), "ActionPlugins"); + cfg = KConfigGroup(&cfg, QString::number(m_dockView->containment()->containmentType())); + KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger); + plugin->restore(pluginConfig); + } + + QList actions = plugin->contextualActions(); + + if (actions.isEmpty()) { + //it probably didn't bother implementing the function. give the user a chance to set + //a better plugin. note that if the user sets no-plugin this won't happen... + if ((m_dockView->containment()->containmentType() != Plasma::Types::PanelContainment && + m_dockView->containment()->containmentType() != Plasma::Types::CustomPanelContainment) && + m_dockView->containment()->actions()->action(QStringLiteral("configure"))) { + auto *dockCorona = qobject_cast(m_dockView->corona()); + + if (dockCorona) { + desktopMenu->addAction(dockCorona->layoutManager()->addWidgetsAction()); + } + + desktopMenu->addAction(m_dockView->containment()->actions()->action(QStringLiteral("configure"))); + } + } else { + auto *dockCorona = qobject_cast(m_dockView->corona()); + + desktopMenu->addSeparator(); + + if (dockCorona && dockCorona->layoutManager()->menuLayouts().count() > 1) { + const QIcon identityIcon = QIcon::fromTheme("user-identity"); + QMenu *layoutsMenu = new QMenu(desktopMenu); + + QAction *layoutsAction = desktopMenu->addMenu(layoutsMenu); // new QAction(identityIcon, i18n("Layouts"), desktopMenu); + layoutsAction->setIcon(identityIcon); + layoutsAction->setCheckable(false); + layoutsAction->setText(i18n("Layouts")); + layoutsAction->setStatusTip(i18n("Switch to another layout")); + + QStringList activeLayouts = dockCorona->layoutManager()->activeLayoutsNames(); + Dock::LayoutsMemoryUsage memoryUsage = dockCorona->layoutManager()->memoryUsage(); + QString currentName = dockCorona->layoutManager()->currentLayoutName(); + + foreach (auto layout, dockCorona->layoutManager()->menuLayouts()) { + QString currentText = (memoryUsage == Latte::Dock::MultipleLayouts && layout == currentName) ? + (" " + i18nc("current layout", "(Current)")) : ""; + QString layoutName = layout + currentText; + + QAction *layoutAction = new QAction(layoutName, layoutsMenu); + + layoutAction->setCheckable(true); + + if (activeLayouts.contains(layout)) { + layoutAction->setChecked(true); + } else { + layoutAction->setChecked(false); + } + + connect(layoutAction, &QAction::triggered, this, [this, dockCorona, layout] { + dockCorona->layoutManager()->switchToLayout(layout); + }); + + layoutsMenu->addAction(layoutAction); + } + + layoutsMenu->addSeparator(); + + QAction *editLayoutsAction = new QAction(i18n("Configure..."), layoutsMenu); + + connect(editLayoutsAction, &QAction::triggered, this, [this, dockCorona] { + dockCorona->layoutManager()->showLatteSettingsDialog(Dock::LayoutPage); + }); + + layoutsMenu->addAction(editLayoutsAction); + + } + + desktopMenu->addAction(dockCorona->layoutManager()->addWidgetsAction()); + + desktopMenu->addActions(actions); + } + + return; +} + +Plasma::Containment *DockMenuManager::containmentById(uint id) +{ + foreach (auto containment, m_dockView->corona()->containments()) { + if (id == containment->id()) { + return containment; + } + } + + return 0; +} + +} diff --git a/app/dock/dockmenumanager.h b/app/dock/dockmenumanager.h new file mode 100644 index 000000000..e85df74ee --- /dev/null +++ b/app/dock/dockmenumanager.h @@ -0,0 +1,78 @@ +/* +* Copyright 2018 Michail Vourlakos +* +* This file is part of Latte-Dock +* +* Latte-Dock 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. +* +* Latte-Dock 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 DOCKMENUMANAGER_H +#define DOCKMENUMANAGER_H + +#include +#include +#include +#include +#include +#include + +namespace Plasma { +class Applet; +class Containment; +} + +namespace Latte { +class DockView; +} + +namespace Latte { + +class DockMenuManager : public QObject { + Q_OBJECT + +public: + DockMenuManager(DockView *view); + ~DockMenuManager() override; + + QMenu *contextMenu(); + + bool mousePressEvent(QMouseEvent *event); + +signals: + void contextMenuChanged(); + +private slots: + void menuAboutToHide(); + +private: + void addAppletActions(QMenu *desktopMenu, Plasma::Applet *applet, QEvent *event); + void addContainmentActions(QMenu *desktopMenu, QEvent *event); + void updateAppletContainsMethod(); + + Plasma::Containment *containmentById(uint id); + +private: + QMenu *m_contextMenu{nullptr}; + QMetaMethod m_appletContainsMethod; + QQuickItem *m_appletContainsMethodItem{nullptr}; + + DockView *m_dockView; + + friend class DockView; +}; + +} + +#endif // DOCKMENUMANAGER_H diff --git a/app/dock/dockview.cpp b/app/dock/dockview.cpp index c56a3888f..8aad25aac 100644 --- a/app/dock/dockview.cpp +++ b/app/dock/dockview.cpp @@ -21,6 +21,7 @@ #include "dockview.h" #include "dockconfigview.h" +#include "dockmenumanager.h" #include "panelshadows_p.h" #include "visibilitymanager.h" @@ -66,7 +67,7 @@ const int MAX_SCREEN_HEIGHT_SECONDARY_CONFIG_WIN = 768; //! are needed in order for window flags to be set correctly DockView::DockView(Plasma::Corona *corona, QScreen *targetScreen, bool dockWindowBehavior) : PlasmaQuick::ContainmentView(corona), - m_contextMenu(nullptr) + m_menuManager(new DockMenuManager(this)) { setTitle(corona->kPackage().metadata().name()); setIcon(qGuiApp->windowIcon()); @@ -174,6 +175,10 @@ DockView::~DockView() m_secondaryConfigView->setVisible(false); } + if (m_menuManager) { + m_menuManager->deleteLater(); + } + if (m_visibility) delete m_visibility; @@ -229,6 +234,8 @@ void DockView::init() connect(this, SIGNAL(normalThicknessChanged()), corona(), SIGNAL(availableScreenRectChanged())); connect(this, SIGNAL(shadowChanged()), corona(), SIGNAL(availableScreenRectChanged())); + connect(m_menuManager, &DockMenuManager::contextMenuChanged, this, &DockView::contextMenuIsShownChanged); + initSignalingForLocationChangeSliding(); ///!!!!! @@ -1035,7 +1042,11 @@ void DockView::setAlternativesIsShown(bool show) bool DockView::contextMenuIsShown() const { - return m_contextMenu; + if (!m_menuManager) { + return false; + } + + return m_menuManager->contextMenu(); } int DockView::currentThickness() const @@ -1832,459 +1843,14 @@ QVariantList DockView::containmentActions() //!BEGIN overriding context menus behavior -void DockView::menuAboutToHide() -{ - m_contextMenu = 0; - m_visibility->setBlockHiding(false); - emit contextMenuIsShownChanged(); -} - - -void DockView::mouseReleaseEvent(QMouseEvent *event) -{ - if (!event || !this->containment()) { - return; - } - - PlasmaQuick::ContainmentView::mouseReleaseEvent(event); - event->setAccepted(this->containment()->containmentActions().contains(Plasma::ContainmentActions::eventToString(event))); -} - void DockView::mousePressEvent(QMouseEvent *event) { - //qDebug() << "Step -1 ..."; - if (!event || !this->containment()) { - return; - } - - //qDebug() << "Step 0..."; - - //even if the menu is executed synchronously, other events may be processed - //by the qml incubator when plasma is loading, so we need to guard there - if (m_contextMenu) { - //qDebug() << "Step 0.5 ..."; - m_contextMenu->close(); - m_contextMenu = 0; - emit contextMenuIsShownChanged(); - PlasmaQuick::ContainmentView::mousePressEvent(event); - return; - } - - //qDebug() << "1 ..."; - const QString trigger = Plasma::ContainmentActions::eventToString(event); - - if (trigger == "RightButton;NoModifier") { - Plasma::ContainmentActions *plugin = this->containment()->containmentActions().value(trigger); - - if (!plugin || plugin->contextualActions().isEmpty()) { - event->setAccepted(false); - return; - } - - //qDebug() << "2 ..."; - //the plugin can be a single action or a context menu - //Don't have an action list? execute as single action - //and set the event position as action data - /*if (plugin->contextualActions().length() == 1) { - QAction *action = plugin->contextualActions().at(0); - action->setData(event->pos()); - action->trigger(); - event->accept(); - return; - }*/ - //FIXME: very inefficient appletAt() implementation - Plasma::Applet *applet = 0; - bool inSystray = false; - - //! initialize the appletContainsMethod on the first right click - if (!m_appletContainsMethod.isValid()) { - updateAppletContainsMethod(); - } - - foreach (Plasma::Applet *appletTemp, this->containment()->applets()) { - PlasmaQuick::AppletQuickItem *ai = appletTemp->property("_plasma_graphicObject").value(); - - bool appletContainsMouse = false; - - if (m_appletContainsMethod.isValid()) { - QVariant retVal; - m_appletContainsMethod.invoke(m_appletContainsMethodItem, Qt::DirectConnection, Q_RETURN_ARG(QVariant, retVal) - , Q_ARG(QVariant, appletTemp->id()), Q_ARG(QVariant, event->pos())); - appletContainsMouse = retVal.toBool(); - } else { - appletContainsMouse = ai->contains(ai->mapFromItem(contentItem(), event->pos())); - } - - if (ai && ai->isVisible() && appletContainsMouse) { - applet = ai->applet(); - KPluginMetaData meta = applet->kPackage().metadata(); - - //Try to find applets inside a systray - if (meta.pluginId() == "org.kde.plasma.systemtray" || - meta.pluginId() == "org.nomad.systemtray") { - auto systrayId = applet->config().readEntry("SystrayContainmentId"); - applet = 0; - inSystray = true; - Plasma::Containment *cont = containmentById(systrayId.toInt()); - - if (cont) { - foreach (Plasma::Applet *appletCont, cont->applets()) { - PlasmaQuick::AppletQuickItem *ai2 = appletCont->property("_plasma_graphicObject").value(); - - if (ai2 && ai2->isVisible() && ai2->contains(ai2->mapFromItem(contentItem(), event->pos()))) { - applet = ai2->applet(); - break; - } - } - } - - break; - } else { - ai = 0; - } - } - } - - if (!applet && !inSystray) { - applet = this->containment(); - } - - //qDebug() << "3 ..."; - - if (applet) { - const auto &provides = KPluginMetaData::readStringList(applet->pluginMetaData().rawData(), QStringLiteral("X-Plasma-Provides")); - - //qDebug() << "3.5 ..."; - - if (!provides.contains(QLatin1String("org.kde.plasma.multitasking"))) { - //qDebug() << "4..."; - QMenu *desktopMenu = new QMenu; - desktopMenu->setAttribute(Qt::WA_DeleteOnClose); - m_contextMenu = desktopMenu; - - //! deprecated old code that can be removed if the following plasma approach doesnt - //! create any issues with context menu creation in Latte - /*if (this->mouseGrabberItem()) { - //workaround, this fixes for me most of the right click menu behavior - this->mouseGrabberItem()->ungrabMouse(); - return; - }*/ - - //!plasma official code - //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 = [this]() { - if (this->mouseGrabberItem()) { - this->mouseGrabberItem()->ungrabMouse(); - } - }; - - //pre 5.8.0 QQuickWindow code is "item->grabMouse(); sendEvent(item, mouseEvent)" - //post 5.8.0 QQuickWindow code is sendEvent(item, mouseEvent); item->grabMouse() - if (QVersionNumber::fromString(qVersion()) > QVersionNumber(5, 8, 0)) { - QTimer::singleShot(0, this, ungrabMouseHack); - } else { - ungrabMouseHack(); - } - - //end workaround - //!end of plasma official code(workaround) - - //qDebug() << "5 ..."; - - if (applet) { - //qDebug() << "5.3 ..."; - emit applet->contextualActionsAboutToShow(); - addAppletActions(desktopMenu, applet, event); - } else { - //qDebug() << "5.6 ..."; - emit this->containment()->contextualActionsAboutToShow(); - addContainmentActions(desktopMenu, event); - } - - //this is a workaround where Qt now creates the menu widget - //in .exec before oxygen can polish it and set the following attribute - desktopMenu->setAttribute(Qt::WA_TranslucentBackground); - //end workaround - QPoint pos = event->globalPos(); - - if (applet) { - //qDebug() << "6 ..."; - desktopMenu->adjustSize(); - - if (this->screen()) { - const QRect scr = this->screen()->geometry(); - int smallStep = 3; - int x = event->globalPos().x() + smallStep; - int y = event->globalPos().y() + smallStep; - - //qDebug()<globalPos().x() > scr.center().x()) { - x = event->globalPos().x() - desktopMenu->width() - smallStep; - } - - if (event->globalPos().y() > scr.center().y()) { - y = event->globalPos().y() - desktopMenu->height() - smallStep; - } - - pos = QPoint(x, y); - } - } - - //qDebug() << "7..."; - - if (desktopMenu->isEmpty()) { - //qDebug() << "7.5 ..."; - delete desktopMenu; - event->accept(); - return; - } - - connect(desktopMenu, SIGNAL(aboutToHide()), this, SLOT(menuAboutToHide())); - m_visibility->setBlockHiding(true); - desktopMenu->popup(pos); - event->setAccepted(true); - emit contextMenuIsShownChanged(); - return; - } - - //qDebug() << "8 ..."; - } - - //qDebug() << "9 ..."; - } - - //qDebug() << "10 ..."; + bool result = m_menuManager->mousePressEvent(event); emit contextMenuIsShownChanged(); - PlasmaQuick::ContainmentView::mousePressEvent(event); -} - -//! update the appletContainsPos method from Panel view -void DockView::updateAppletContainsMethod() -{ - for (QQuickItem *item : contentItem()->childItems()) { - if (auto *metaObject = item->metaObject()) { - // not using QMetaObject::invokeMethod to avoid warnings when calling - // this on applets that don't have it or other child items since this - // is pretty much trial and error. - // Also, "var" arguments are treated as QVariant in QMetaObject - - int methodIndex = metaObject->indexOfMethod("appletContainsPos(QVariant,QVariant)"); - - if (methodIndex == -1) { - continue; - } - - m_appletContainsMethod = metaObject->method(methodIndex); - m_appletContainsMethodItem = item; - } - } -} - -void DockView::addAppletActions(QMenu *desktopMenu, Plasma::Applet *applet, QEvent *event) -{ - if (!this->containment()) { - return; - } - - foreach (QAction *action, applet->contextualActions()) { - if (action) { - desktopMenu->addAction(action); - } - } - - if (!applet->failedToLaunch()) { - QAction *runAssociatedApplication = applet->actions()->action(QStringLiteral("run associated application")); - - if (runAssociatedApplication && runAssociatedApplication->isEnabled()) { - desktopMenu->addAction(runAssociatedApplication); - } - - QAction *configureApplet = applet->actions()->action(QStringLiteral("configure")); - - if (configureApplet && configureApplet->isEnabled()) { - desktopMenu->addAction(configureApplet); - } - - QAction *appletAlternatives = applet->actions()->action(QStringLiteral("alternatives")); - - if (appletAlternatives && appletAlternatives->isEnabled() && containment()->isUserConfiguring()) { - desktopMenu->addAction(appletAlternatives); - } - } - - QMenu *containmentMenu = new QMenu(i18nc("%1 is the name of the containment", "%1 Options", this->containment()->title()), desktopMenu); - addContainmentActions(containmentMenu, event); - - if (!containmentMenu->isEmpty()) { - int enabled = 0; - //count number of real actions - QListIterator actionsIt(containmentMenu->actions()); - - while (enabled < 3 && actionsIt.hasNext()) { - QAction *action = actionsIt.next(); - - if (action->isVisible() && !action->isSeparator()) { - ++enabled; - } - } - - desktopMenu->addSeparator(); - - if (enabled) { - //if there is only one, don't create a submenu - // if (enabled < 2) { - foreach (QAction *action, containmentMenu->actions()) { - if (action->isVisible()) { - desktopMenu->addAction(action); - } - } - - // } else { - // desktopMenu->addMenu(containmentMenu); - // } - } - } - - if (this->containment()->immutability() == Plasma::Types::Mutable && - (this->containment()->containmentType() != Plasma::Types::PanelContainment || this->containment()->isUserConfiguring())) { - QAction *closeApplet = applet->actions()->action(QStringLiteral("remove")); - - //qDebug() << "checking for removal" << closeApplet; - if (closeApplet) { - if (!desktopMenu->isEmpty()) { - desktopMenu->addSeparator(); - } - - //qDebug() << "adding close action" << closeApplet->isEnabled() << closeApplet->isVisible(); - desktopMenu->addAction(closeApplet); - } - } -} - -void DockView::addContainmentActions(QMenu *desktopMenu, QEvent *event) -{ - if (!this->containment()) { - return; - } - - if (this->containment()->corona()->immutability() != Plasma::Types::Mutable && - !KAuthorized::authorizeAction(QStringLiteral("plasma/containment_actions"))) { - //qDebug() << "immutability"; - return; - } - - //this is what ContainmentPrivate::prepareContainmentActions was - const QString trigger = Plasma::ContainmentActions::eventToString(event); - //"RightButton;NoModifier" - Plasma::ContainmentActions *plugin = this->containment()->containmentActions().value(trigger); - - if (!plugin) { - return; - } - - if (plugin->containment() != this->containment()) { - plugin->setContainment(this->containment()); - // now configure it - KConfigGroup cfg(this->containment()->corona()->config(), "ActionPlugins"); - cfg = KConfigGroup(&cfg, QString::number(this->containment()->containmentType())); - KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger); - plugin->restore(pluginConfig); - } - - QList actions = plugin->contextualActions(); - - if (actions.isEmpty()) { - //it probably didn't bother implementing the function. give the user a chance to set - //a better plugin. note that if the user sets no-plugin this won't happen... - if ((this->containment()->containmentType() != Plasma::Types::PanelContainment && - this->containment()->containmentType() != Plasma::Types::CustomPanelContainment) && - this->containment()->actions()->action(QStringLiteral("configure"))) { - auto *dockCorona = qobject_cast(this->corona()); - - if (dockCorona) { - desktopMenu->addAction(dockCorona->layoutManager()->addWidgetsAction()); - } - - desktopMenu->addAction(this->containment()->actions()->action(QStringLiteral("configure"))); - } - } else { - auto *dockCorona = qobject_cast(this->corona()); - - desktopMenu->addSeparator(); - - if (dockCorona && dockCorona->layoutManager()->menuLayouts().count() > 1) { - const QIcon identityIcon = QIcon::fromTheme("user-identity"); - QMenu *layoutsMenu = new QMenu(desktopMenu); - - QAction *layoutsAction = desktopMenu->addMenu(layoutsMenu); // new QAction(identityIcon, i18n("Layouts"), desktopMenu); - layoutsAction->setIcon(identityIcon); - layoutsAction->setCheckable(false); - layoutsAction->setText(i18n("Layouts")); - layoutsAction->setStatusTip(i18n("Switch to another layout")); - - QStringList activeLayouts = dockCorona->layoutManager()->activeLayoutsNames(); - Dock::LayoutsMemoryUsage memoryUsage = dockCorona->layoutManager()->memoryUsage(); - QString currentName = dockCorona->layoutManager()->currentLayoutName(); - - foreach (auto layout, dockCorona->layoutManager()->menuLayouts()) { - QString currentText = (memoryUsage == Latte::Dock::MultipleLayouts && layout == currentName) ? - (" " + i18nc("current layout", "(Current)")) : ""; - QString layoutName = layout + currentText; - - QAction *layoutAction = new QAction(layoutName, layoutsMenu); - layoutAction->setCheckable(true); - - if (activeLayouts.contains(layout)) { - layoutAction->setChecked(true); - } else { - layoutAction->setChecked(false); - } - - connect(layoutAction, &QAction::triggered, this, [this, dockCorona, layout] { - dockCorona->layoutManager()->switchToLayout(layout); - }); - - layoutsMenu->addAction(layoutAction); - } - - layoutsMenu->addSeparator(); - - QAction *editLayoutsAction = new QAction(i18n("Configure..."), layoutsMenu); - - connect(editLayoutsAction, &QAction::triggered, this, [this, dockCorona] { - dockCorona->layoutManager()->showLatteSettingsDialog(Dock::LayoutPage); - }); - - layoutsMenu->addAction(editLayoutsAction); - - } - - desktopMenu->addAction(dockCorona->layoutManager()->addWidgetsAction()); - - desktopMenu->addActions(actions); - } - - return; -} - -Plasma::Containment *DockView::containmentById(uint id) -{ - foreach (auto containment, corona()->containments()) { - if (id == containment->id()) { - return containment; - } + if (result) { + PlasmaQuick::ContainmentView::mousePressEvent(event); } - - return 0; } //!END overriding context menus behavior diff --git a/app/dock/dockview.h b/app/dock/dockview.h index d7d81bd8f..3fb626921 100644 --- a/app/dock/dockview.h +++ b/app/dock/dockview.h @@ -51,6 +51,7 @@ class PlasmaShellSurface; } namespace Latte { +class DockMenuManager; class Layout; } @@ -225,7 +226,6 @@ protected slots: protected: bool event(QEvent *ev) override; void mousePressEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; signals: void addInternalViewSplitter(); @@ -283,7 +283,6 @@ signals: private slots: void availableScreenRectChanged(); void hideWindowsForSlidingOut(); - void menuAboutToHide(); void statusChanged(Plasma::Types::ItemStatus); void screenChanged(QScreen *screen); void updateEffects(); @@ -292,8 +291,6 @@ private slots: void saveConfig(); private: - void addAppletActions(QMenu *desktopMenu, Plasma::Applet *applet, QEvent *event); - void addContainmentActions(QMenu *desktopMenu, QEvent *event); void applyActivitiesToWindows(); void initSignalingForLocationChangeSliding(); void setupWaylandIntegration(); @@ -331,14 +328,12 @@ private: QRect m_localGeometry; QRect m_absGeometry; QRect m_maskArea; - QMenu *m_contextMenu; - QMetaMethod m_appletContainsMethod; - QQuickItem *m_appletContainsMethodItem{nullptr}; Layout *m_managedLayout{nullptr}; QPointer m_configView; QPointer m_secondaryConfigView; QPointer m_visibility; + QPointer m_menuManager; QPointer m_screenToFollow; QString m_screenToFollowId;