Commit 86fe9869 authored by Kai Uwe Broulik's avatar Kai Uwe Broulik 🍇

[Notifications] Use custom DragHelper instead of Qt's Drag attached property

Qt's Drag attached property doesn't like it at all when it gets deleted during drag. (QTBUG-65701)
Even though we try very hard to keep the popup open, when you have multiple notification applets, their dialogs don't know each
other but access a shared data engine.

This means that, since there can only we one hovered dialog at a time, another notification applet might still dismiss the
notification and have our dialog close.

By using a singleton NotificationHelper we 1.) decouple the Drag from the item and 2.) share the "is dragging" state between applets.

BUG: 388684
FIXED-IN: 5.12.0

Differential Revision: https://phabricator.kde.org/D9754
parent f9a71714
......@@ -45,7 +45,7 @@ ListView {
// whether we're currently dragging, this way we can keep the popup around during the entire
// drag operation even if the mouse leaves the popup
property bool dragging: false
property bool dragging: Notifications.DragHelper.dragActive
model: {
var urls = notificationItem.urls
......@@ -103,6 +103,9 @@ ListView {
delegate: MouseArea {
id: previewDelegate
property int pressX: -1
property int pressY: -1
// clip is expensive, only clip if the QPixmapItem would leak outside
clip: previewPixmap.height > height
......@@ -120,26 +123,32 @@ ListView {
}
onPressed: {
if (mouse.button === Qt.RightButton) {
if (mouse.button === Qt.LeftButton) {
pressX = mouse.x;
pressY = mouse.y;
} else if (mouse.button === Qt.RightButton) {
// avoid menu button glowing if we didn't actually press it
menuButton.checked = false;
thumbnailer.showContextMenu(mouse.x, mouse.y, modelData, this);
}
}
// cannot drag itself, hence dragging the Pixmap instead
drag.target: previewPixmap
Drag.dragType: Drag.Automatic
Drag.active: previewDelegate.drag.active
Drag.mimeData: ({
"text/uri-list": modelData,
"text/plain": modelData
})
drag.onActiveChanged: {
previewList.dragging = drag.active
onPositionChanged: {
if (pressX !== -1 && pressY !== -1 && Notifications.DragHelper.isDrag(pressX, pressY, mouse.x, mouse.y)) {
Notifications.DragHelper.startDrag(previewDelegate, modelData /*url*/, thumbnailer.pixmap);
pressX = -1;
pressY = -1;
}
}
onReleased: {
pressX = -1;
pressY = -1;
}
onContainsMouseChanged: {
if (!containsMouse) {
pressX = -1;
pressY = -1;
}
}
// first item determins the ListView height
......
......@@ -2,6 +2,7 @@ set(notificationshelper_SRCS
notificationshelper.cpp
notificationshelperplugin.cpp
thumbnailer.cpp
draghelper.cpp
)
add_library(notificationshelperplugin SHARED ${notificationshelper_SRCS})
......
/***************************************************************************
* Copyright (C) 2013 by Eike Hein <hein@kde.org> *
* *
* 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, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
***************************************************************************/
#include "draghelper.h"
#include <QDrag>
#include <QGuiApplication>
#include <QMimeData>
#include <QQuickItem>
#include <QQuickWindow>
#include <QStyleHints>
DragHelper::DragHelper(QObject* parent) : QObject(parent)
{
}
DragHelper::~DragHelper()
{
}
bool DragHelper::dragActive() const
{
return m_dragActive;
}
bool DragHelper::isDrag(int oldX, int oldY, int newX, int newY) const
{
return ((QPoint(oldX, oldY) - QPoint(newX, newY)).manhattanLength() >= qApp->styleHints()->startDragDistance());
}
void DragHelper::startDrag(QQuickItem *item, const QUrl &url, const QPixmap &pixmap)
{
// This allows the caller to return, making sure we don't crash if
// the caller is destroyed mid-drag
QMetaObject::invokeMethod(this, "doDrag", Qt::QueuedConnection,
Q_ARG(QQuickItem*, item), Q_ARG(QUrl, url), Q_ARG(QPixmap, pixmap));
}
void DragHelper::doDrag(QQuickItem *item, const QUrl &url, const QPixmap &pixmap)
{
if (item && item->window() && item->window()->mouseGrabberItem()) {
item->window()->mouseGrabberItem()->ungrabMouse();
}
QDrag *drag = new QDrag(item);
QMimeData *mimeData = new QMimeData();
if (!url.isEmpty()) {
mimeData->setUrls(QList<QUrl>() << url);
}
drag->setMimeData(mimeData);
if (!pixmap.isNull()) {
drag->setPixmap(pixmap);
}
m_dragActive = true;
emit dragActiveChanged();
drag->exec();
m_dragActive = false;
emit dragActiveChanged();
}
/***************************************************************************
* Copyright (C) 2013 by Eike Hein <hein@kde.org> *
* *
* 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, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
***************************************************************************/
#ifndef DRAGHELPER_H
#define DRAGHELPER_H
#include <QObject>
#include <QPixmap>
#include <QUrl>
class QQuickItem;
class DragHelper : public QObject
{
Q_OBJECT
Q_PROPERTY(bool dragActive READ dragActive NOTIFY dragActiveChanged)
public:
DragHelper(QObject *parent = 0);
~DragHelper();
bool dragActive() const;
Q_INVOKABLE bool isDrag(int oldX, int oldY, int newX, int newY) const;
Q_INVOKABLE void startDrag(QQuickItem* item, const QUrl &url = QUrl(), const QPixmap &pixmap = QPixmap());
Q_SIGNALS:
void dragActiveChanged();
private:
Q_INVOKABLE void doDrag(QQuickItem* item, const QUrl &url = QUrl(), const QPixmap &pixmap = QPixmap());
bool m_dragActive = false;
};
#endif
......@@ -19,6 +19,7 @@
#include "notificationshelperplugin.h"
#include "notificationshelper.h"
#include "thumbnailer.h"
#include "draghelper.h"
#include <QtQml>
#include <QProcess>
......@@ -51,6 +52,14 @@ static QObject *urlcheck_singletontype_provider(QQmlEngine *engine, QJSEngine *s
return new UrlHelper();
}
static QObject *draghelper_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
return new DragHelper();
}
void NotificationsHelperPlugin::registerTypes(const char *uri)
{
Q_ASSERT(uri == QLatin1String("org.kde.plasma.private.notifications"));
......@@ -59,6 +68,7 @@ void NotificationsHelperPlugin::registerTypes(const char *uri)
qmlRegisterType<Thumbnailer>(uri, 1, 0, "Thumbnailer");
qmlRegisterSingletonType<UrlHelper>(uri, 1, 0, "UrlHelper", urlcheck_singletontype_provider);
qmlRegisterSingletonType<DragHelper>(uri, 1, 0, "DragHelper", draghelper_singletontype_provider);
}
void NotificationsHelperPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment