diff --git a/applets/showActivityManager/package/contents/ui/main.qml b/applets/showActivityManager/package/contents/ui/main.qml --- a/applets/showActivityManager/package/contents/ui/main.qml +++ b/applets/showActivityManager/package/contents/ui/main.qml @@ -23,10 +23,13 @@ import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.draganddrop 2.0 as DND + +import org.kde.plasma.activityswitcher 1.0 as ActivitySwitcher import org.kde.activities 0.1 as Activities -MouseArea { +DND.DropArea { id: root property string activeSource: "Status" height: units.iconSizes.large @@ -51,10 +54,21 @@ Plasmoid.preferredRepresentation: Plasmoid.fullRepresentation - onClicked: { - var service = dataSource.serviceForSource(activeSource) - var operation = service.operationDescription("toggleActivityManager") - service.startOperationCall(operation) + MouseArea { + anchors.fill: parent + onClicked: { + var service = dataSource.serviceForSource(activeSource) + var operation = service.operationDescription("toggleActivityManager") + service.startOperationCall(operation) + } + } + + onDragEnter: { + ActivitySwitcher.Backend.setDropMode(true); + } + + onDragLeave: { + ActivitySwitcher.Backend.setDropMode(false); } PlasmaCore.DataSource { diff --git a/desktoppackage/contents/activitymanager/ActivityItem.qml b/desktoppackage/contents/activitymanager/ActivityItem.qml --- a/desktoppackage/contents/activitymanager/ActivityItem.qml +++ b/desktoppackage/contents/activitymanager/ActivityItem.qml @@ -5,6 +5,7 @@ import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddonsComponents +import org.kde.draganddrop 2.0 as DND import org.kde.plasma.activityswitcher 1.0 as ActivitySwitcher @@ -225,18 +226,39 @@ // } } - MouseArea { - id: hoverArea + DND.DropArea { + anchors.fill: parent + + preventStealing: true + enabled: true + + onDrop: { + ActivitySwitcher.Backend.drop(event.mimeData, event.modifiers, root.activityId); + } - anchors.fill : parent - onClicked : root.clicked() - hoverEnabled : true - onEntered : S.showActivityItemActionsBar(root) + onDragEnter: { + ActivitySwitcher.Backend.setDropMode(true); + } - Accessible.name : root.title - Accessible.role : Accessible.Button - Accessible.selected : root.selected - Accessible.onPressAction : root.clicked() + onDragLeave: { + ActivitySwitcher.Backend.setDropMode(false); + } + + visible: ActivitySwitcher.Backend.dropEnabled + + MouseArea { + id: hoverArea + + anchors.fill : parent + onClicked : root.clicked() + hoverEnabled : true + onEntered : S.showActivityItemActionsBar(root) + + Accessible.name : root.title + Accessible.role : Accessible.Button + Accessible.selected : root.selected + Accessible.onPressAction : root.clicked() + } } // Controls diff --git a/imports/activitymanager/CMakeLists.txt b/imports/activitymanager/CMakeLists.txt --- a/imports/activitymanager/CMakeLists.txt +++ b/imports/activitymanager/CMakeLists.txt @@ -43,6 +43,9 @@ KF5::KIOCore KF5::KIOWidgets KF5::WindowSystem + + PW::LibTaskManager + ${X11_X11_LIB} ) diff --git a/imports/activitymanager/switcherbackend.h b/imports/activitymanager/switcherbackend.h --- a/imports/activitymanager/switcherbackend.h +++ b/imports/activitymanager/switcherbackend.h @@ -45,6 +45,7 @@ Q_OBJECT Q_PROPERTY(bool shouldShowSwitcher READ shouldShowSwitcher WRITE setShouldShowSwitcher NOTIFY shouldShowSwitcherChanged) + Q_PROPERTY(bool dropEnabled READ dropEnabled CONSTANT) public: @@ -69,6 +70,10 @@ void setCurrentActivity(const QString &activity); void stopActivity(const QString &activity); + void setDropMode(bool value); + void drop(QMimeData* mimeData, int modifiers, const QVariant &activityId); + bool dropEnabled() const; + private: template inline void registerShortcut(const QString &actionName, const QString &name, @@ -99,6 +104,9 @@ QTimer m_modKeyPollingTimer; QString m_previousActivity; + bool m_dropModeActive; + QTimer m_dropModeHider; + SortedActivitiesModel *m_runningActivitiesModel = nullptr; SortedActivitiesModel *m_stoppedActivitiesModel = nullptr; diff --git a/imports/activitymanager/switcherbackend.cpp b/imports/activitymanager/switcherbackend.cpp --- a/imports/activitymanager/switcherbackend.cpp +++ b/imports/activitymanager/switcherbackend.cpp @@ -39,6 +39,9 @@ #include #include #include +#include +#include +#include // X11 #include @@ -245,6 +248,7 @@ SwitcherBackend::SwitcherBackend(QObject *parent) : QObject(parent) , m_shouldShowSwitcher(false) + , m_dropModeActive(false) , m_runningActivitiesModel(new SortedActivitiesModel({KActivities::Info::Running, KActivities::Info::Stopping}, this)) , m_stoppedActivitiesModel(new SortedActivitiesModel({KActivities::Info::Stopped, KActivities::Info::Starting}, this)) { @@ -261,8 +265,15 @@ connect(this, &SwitcherBackend::shouldShowSwitcherChanged, m_runningActivitiesModel, &SortedActivitiesModel::setInhibitUpdates); + m_modKeyPollingTimer.setInterval(100); + m_modKeyPollingTimer.setSingleShot(true); connect(&m_modKeyPollingTimer, &QTimer::timeout, this, &SwitcherBackend::showActivitySwitcherIfNeeded); + + m_dropModeHider.setInterval(500); + connect(&m_dropModeHider, &QTimer::timeout, + this, [this] { setShouldShowSwitcher(false); }); + connect(&m_activities, &KActivities::Controller::currentActivityChanged, this, &SwitcherBackend::onCurrentActivityChanged); m_previousActivity = m_activities.currentActivity(); @@ -324,7 +335,7 @@ void SwitcherBackend::showActivitySwitcherIfNeeded() { - if (!m_lastInvokedAction) { + if (!m_lastInvokedAction || m_dropModeActive) { return; } @@ -405,7 +416,7 @@ if (m_shouldShowSwitcher) { // TODO: We really should NOT do this by polling - m_modKeyPollingTimer.start(100); + m_modKeyPollingTimer.start(); } else { m_modKeyPollingTimer.stop(); @@ -435,3 +446,74 @@ { m_activities.stopActivity(activity); } + +bool SwitcherBackend::dropEnabled() const +{ +#if HAVE_X11 + return true; +#else + return false; +#endif +} + +void SwitcherBackend::drop(QMimeData* mimeData, int modifiers, const QVariant &activityId) +{ + setDropMode(false); + +#if HAVE_X11 + if (KWindowSystem::isPlatformX11()) { + bool ok = false; + const QList &ids = TaskManager::XWindowTasksModel::winIdsFromMimeData(mimeData, &ok); + + if (!ok) { + return; + } + + const QString newActivity = activityId.toString(); + const QStringList runningActivities = m_activities.runningActivities(); + + if (!runningActivities.contains(newActivity)) { + return; + } + + for (const auto &id : ids) { + QStringList activities = KWindowInfo(id, NET::Properties(), NET::WM2Activities).activities(); + + if (modifiers & Qt::ControlModifier) { + // Add to the activity instead of moving. + // This is a hack because the task manager reports that + // is supports only the 'Move' DND action. + if (!activities.contains(newActivity)) { + activities << newActivity; + } + + } else { + // Move to this activity + // if on only one activity, set it to only the new activity + // if on >1 activity, remove it from the current activity and add it to the new activity + + const QString currentActivity = m_activities.currentActivity(); + activities.removeAll(currentActivity); + activities << newActivity; + } + + KWindowSystem::setOnActivities(id, activities); + } + } +#endif +} + +void SwitcherBackend::setDropMode(bool value) +{ + if (m_dropModeActive == value) return; + + m_dropModeActive = value; + if (value) { + setShouldShowSwitcher(true); + m_dropModeHider.stop(); + } else { + m_dropModeHider.start(); + } +} + +