Merge branch 'mart/basicDeleteUndo'

REVIEW:120885
This commit is contained in:
Marco Martin 2014-10-31 17:48:41 +01:00
commit 78ab028d52
10 changed files with 130 additions and 26 deletions

View File

@ -58,6 +58,7 @@ find_package(KF5KIO ${KF5_DEP_VERSION} REQUIRED)
find_package(KF5Service ${KF5_DEP_VERSION} REQUIRED)
find_package(KF5WindowSystem ${KF5_DEP_VERSION} REQUIRED)
find_package(KF5XmlGui ${KF5_DEP_VERSION} REQUIRED)
find_package(KF5Notifications ${KF5_DEP_VERSION} REQUIRED)
find_package(KF5DocTools ${KF5_DEP_VERSION})
set_package_properties(KF5DocTools PROPERTIES DESCRIPTION "Tools to generate documentation"

View File

@ -125,6 +125,7 @@ PRIVATE
KF5::Declarative #runtimePlatform
KF5::XmlGui #KActionCollection
KF5::GlobalAccel #Applet::setGlobalShortcut
KF5::Notifications
${PLASMA_EXTRA_LIBS}
)
@ -201,6 +202,7 @@ install(FILES
install(FILES data/operations/dataengineservice.operations DESTINATION ${PLASMA_DATA_INSTALL_DIR}/services)
install(FILES data/operations/plasmoidservice.operations DESTINATION ${PLASMA_DATA_INSTALL_DIR}/services)
install(FILES data/operations/storage.operations DESTINATION ${PLASMA_DATA_INSTALL_DIR}/services)
install(FILES data/notifications/plasmashell.notifyrc DESTINATION ${KNOTIFYRC_INSTALL_DIR})
install(TARGETS KF5Plasma EXPORT KF5PlasmaTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS})

View File

@ -104,6 +104,9 @@ Applet::Applet(const QString &packagePath, uint appletId)
Applet::~Applet()
{
if (d->transient) {
d->resetConfigurationObject();
}
//let people know that i will die
emit appletDeleted(this);
@ -255,6 +258,7 @@ void Applet::destroy()
}
d->transient = true;
emit destroyedChanged(true);
//FIXME: an animation on leave if !isContainment() would be good again .. which should be handled by the containment class
d->cleanUpAndDelete();
}

View File

@ -383,6 +383,13 @@ Q_SIGNALS:
*/
void statusChanged(Plasma::Types::ItemStatus status);
/**
* Emitted when the applet has been scheduled for destruction
* or the destruction has been undone
* @since 5.4
*/
void destroyedChanged(bool destroyed);
//CONFIGURATION
/**
* Emitted when an applet has changed values in its configuration

View File

@ -0,0 +1,9 @@
[Global]
IconName=plasma
Comment=Plasma Workspace
Name=Plasma
[Event/plasmoidDeleted]
Name=Widget deleted
Comment=A widget has been deleted
Action=Popup

View File

@ -27,6 +27,7 @@
#include <QFile>
#include <qstandardpaths.h>
#include <QTimer>
#include <QDebug>
#include <QMessageBox>
@ -61,7 +62,8 @@ AppletPrivate::AppletPrivate(KService::Ptr service, const KPluginInfo *info, int
actions(AppletPrivate::defaultActions(applet)),
activationAction(0),
itemStatus(Types::UnknownStatus),
modificationsTimer(0),
modificationsTimer(Q_NULLPTR),
deleteNotificationTimer(Q_NULLPTR),
hasConfigurationInterface(false),
failed(false),
transient(false),
@ -89,6 +91,10 @@ AppletPrivate::~AppletPrivate()
KGlobalAccel::self()->removeAllShortcuts(activationAction);
}
if (deleteNotification) {
deleteNotification->close();
}
delete script;
script = 0;
delete package;
@ -220,28 +226,83 @@ void AppletPrivate::askDestroy()
return; //don't double delete
}
if (q->isContainment()) {
QMessageBox *box = new QMessageBox(QMessageBox::Warning, i18nc("@title:window %1 is the name of the containment", "Remove %1", q->title()), i18nc("%1 is the name of the containment", "Do you really want to remove this %1?", q->title()), QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No));
box->setWindowFlags((Qt::WindowFlags)(box->windowFlags() | Qt::WA_DeleteOnClose));
box->open();
if (transient) {
cleanUpAndDelete();
} else {
//There is no confirmation anymore for panels removal:
//this needs users feedback
transient = true;
emit q->destroyedChanged(true);
//no parent, but it won't leak, since it will be closed both in case of timeout
//or direct action
deleteNotification = new KNotification("plasmoidDeleted", KNotification::Persistent, 0);
QStringList actions;
deleteNotification->setIconName(q->icon());
Plasma::Containment *asContainment = qobject_cast<Plasma::Containment *>(q);
QObject::connect(q, &Applet::immutabilityChanged, [=] () {
box->close();
});
QObject::connect(q, &QObject::destroyed, [=] () {
box->close();
});
QObject::connect(box->button(QMessageBox::Yes), &QAbstractButton::clicked,
[ = ]() {
transient = true;
cleanUpAndDelete();
});
if (!q->isContainment()) {
deleteNotification->setText(i18n("The widget \"%1\" has been removed.", q->title()));
} else if (asContainment && (asContainment->containmentType() == Types::PanelContainment || asContainment->containmentType() == Types::CustomPanelContainment)) {
deleteNotification->setText(i18n("A Panel has been removed."));
//This will never happen with our current shell, but could with a custom one
} else {
deleteNotification->setText(i18n("A Desktop has been removed."));
}
return;
actions.append(i18n("Undo"));
deleteNotification->setActions(actions);
QObject::connect(deleteNotification.data(), &KNotification::action1Activated,
[=]() {
transient = false;
emit q->destroyedChanged(false);
if (!q->isContainment() && q->containment()) {
//make sure the applets are sorted by id
auto position = std::lower_bound(q->containment()->d->applets.begin(), q->containment()->d->applets.end(), q, [](Plasma::Applet *a1, Plasma::Applet *a2) {
return a1->id() < a2->id();
});
q->containment()->d->applets.insert(position, q);
emit q->containment()->appletAdded(q);
}
if (deleteNotification) {
deleteNotification->close();
}
if (deleteNotificationTimer) {
delete deleteNotificationTimer;
deleteNotificationTimer = 0;
}
});
QObject::connect(deleteNotification.data(), &KNotification::closed,
[=]() {
//If the timer still exists, it means the undo action was NOT triggered
if (deleteNotificationTimer) {
transient = true;
emit q->destroyedChanged(true);
cleanUpAndDelete();
}
});
deleteNotification->sendEvent();
if (!deleteNotificationTimer) {
deleteNotificationTimer = new QTimer(q);
//really delete after a minute
deleteNotificationTimer->setInterval(60 * 1000);
deleteNotificationTimer->setSingleShot(true);
QObject::connect(deleteNotificationTimer, &QTimer::timeout,
[=]() {
if (deleteNotification) {
deleteNotification->close();
}
transient = true;
emit q->destroyedChanged(true);
cleanUpAndDelete();
});
deleteNotificationTimer->start();
}
if (!q->isContainment() && q->containment()) {
q->containment()->d->applets.removeAll(q);
emit q->containment()->appletRemoved(q);
}
}
transient = true;
cleanUpAndDelete();
}
void AppletPrivate::globalShortcutChanged()

View File

@ -29,6 +29,7 @@
#include <kconfigskeleton.h>
#include <kservice.h>
#include <kplugininfo.h>
#include <KNotification>
#include "plasma/applet.h"
@ -104,6 +105,9 @@ public:
QBasicTimer constraintsTimer;
QBasicTimer *modificationsTimer;
QPointer <KNotification> deleteNotification;
QTimer *deleteNotificationTimer;
// a great green field of booleans :)
bool hasConfigurationInterface : 1;
bool failed : 1;

View File

@ -59,7 +59,8 @@ AppletInterface::AppletInterface(DeclarativeAppletScript *script, const QVariant
m_appletScriptEngine(script),
m_backgroundHints(Plasma::Types::StandardBackground),
m_busy(false),
m_hideOnDeactivate(true)
m_hideOnDeactivate(true),
m_positionBeforeRemoval(QPointF(-1, -1))
{
qmlRegisterType<QAction>();
@ -72,6 +73,12 @@ AppletInterface::AppletInterface(DeclarativeAppletScript *script, const QVariant
connect(applet(), &Plasma::Applet::statusChanged,
this, &AppletInterface::statusChanged);
connect(applet(), &Plasma::Applet::destroyedChanged,
this, [=] () {
setVisible(!applet()->destroyed());
});
connect(applet(), &Plasma::Applet::activated,
this, &AppletInterface::activated);
@ -124,6 +131,11 @@ AppletInterface::AppletInterface(Plasma::Applet *a, const QVariantList &args, QQ
connect(applet(), &Plasma::Applet::statusChanged,
this, &AppletInterface::statusChanged);
connect(applet(), &Plasma::Applet::destroyedChanged,
[=] () {
setVisible(!applet()->destroyed());
});
connect(appletScript(), &DeclarativeAppletScript::formFactorChanged,
this, &AppletInterface::formFactorChanged);
connect(appletScript(), &DeclarativeAppletScript::locationChanged,

View File

@ -369,7 +369,10 @@ private:
Plasma::Types::BackgroundHints m_backgroundHints;
bool m_busy : 1;
bool m_hideOnDeactivate : 1;
friend class ContainmentInterface;
//This is used by ContainmentInterface
QPointF m_positionBeforeRemoval;
};
QML_DECLARE_TYPEINFO(AppletInterface, QML_HAS_ATTACHED_PROPERTIES)

View File

@ -554,8 +554,8 @@ void ContainmentInterface::appletAddedForward(Plasma::Applet *applet)
return;
}
QObject *appletGraphicObject = applet->property("_plasma_graphicObject").value<QObject *>();
QObject *contGraphicObject = m_containment->property("_plasma_graphicObject").value<QObject *>();
AppletInterface *appletGraphicObject = applet->property("_plasma_graphicObject").value<AppletInterface *>();
AppletInterface *contGraphicObject = m_containment->property("_plasma_graphicObject").value<AppletInterface *>();
// qDebug() << "Applet added on containment:" << m_containment->title() << contGraphicObject
// << "Applet: " << applet << applet->title() << appletGraphicObject;
@ -572,14 +572,15 @@ void ContainmentInterface::appletAddedForward(Plasma::Applet *applet)
}
m_appletInterfaces << appletGraphicObject;
emit appletAdded(appletGraphicObject, -1, -1);
emit appletAdded(appletGraphicObject, appletGraphicObject->m_positionBeforeRemoval.x(), appletGraphicObject->m_positionBeforeRemoval.y());
emit appletsChanged();
}
void ContainmentInterface::appletRemovedForward(Plasma::Applet *applet)
{
QObject *appletGraphicObject = applet->property("_plasma_graphicObject").value<QObject *>();
AppletInterface *appletGraphicObject = applet->property("_plasma_graphicObject").value<AppletInterface *>();
m_appletInterfaces.removeAll(appletGraphicObject);
appletGraphicObject->m_positionBeforeRemoval = appletGraphicObject->mapToItem(this, QPointF());
emit appletRemoved(appletGraphicObject);
emit appletsChanged();
}
@ -719,7 +720,7 @@ void ContainmentInterface::mousePressEvent(QMouseEvent *event)
Plasma::Applet *applet = 0;
foreach (QObject *appletObject, m_appletInterfaces) {
if (AppletInterface *ai = qobject_cast<AppletInterface *>(appletObject)) {
if (ai->contains(ai->mapFromItem(this, event->posF()))) {
if (ai->isVisible() && ai->contains(ai->mapFromItem(this, event->posF()))) {
applet = ai->applet();
emit ai->contextualActionsAboutToShow();
break;