Commit 9ef7eab9 authored by Ivan Čukić's avatar Ivan Čukić 👁

Drag and drop assigning windows to activities in the default switcher

Summary:
When dragging a window from the taskbar onto the activity switcher
applet, the switcher will pop up showing all the activities. When the
user drops the task onto an activity, it will be moved to said activity.

Alternatively, if the Control key is pressed, the window will be
assigned to the destination activity as well as the current one (copy).

{F8303555}

Reviewers: #plasma, mart, davidedmundson, ngraham

Reviewed By: #plasma, mart

Subscribers: plasma-devel

Tags: #plasma

Differential Revision: https://phabricator.kde.org/D29548
parent 01776488
......@@ -23,10 +23,13 @@ import QtQuick.Layouts 1.1
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 @@ MouseArea {
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 {
......
......@@ -5,6 +5,7 @@ import org.kde.plasma.core 2.0 as PlasmaCore
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,47 @@ Item {
// }
}
MouseArea {
id: hoverArea
DND.DropArea {
PlasmaComponents.Highlight {
id: dropHighlight
anchors.fill: parent
visible: false
}
anchors.fill: parent
preventStealing: true
enabled: true
anchors.fill : parent
onClicked : root.clicked()
hoverEnabled : true
onEntered : S.showActivityItemActionsBar(root)
onDrop: {
ActivitySwitcher.Backend.drop(event.mimeData, event.modifiers, root.activityId);
}
onDragEnter: {
ActivitySwitcher.Backend.setDropMode(true);
dropHighlight.visible = true;
}
Accessible.name : root.title
Accessible.role : Accessible.Button
Accessible.selected : root.selected
Accessible.onPressAction : root.clicked()
onDragLeave: {
ActivitySwitcher.Backend.setDropMode(false);
dropHighlight.visible = 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
......
......@@ -43,6 +43,9 @@ target_link_libraries (
KF5::KIOCore
KF5::KIOWidgets
KF5::WindowSystem
PW::LibTaskManager
${X11_X11_LIB}
)
......
......@@ -39,6 +39,9 @@
#include <KIO/PreviewJob>
#include <KConfig>
#include <KConfigGroup>
#include <KWindowSystem>
#include <windowtasksmodel.h>
#include <xwindowtasksmodel.h>
// X11
#include <X11/keysym.h>
......@@ -245,6 +248,7 @@ inline void SwitcherBackend::registerShortcut(const QString &actionName,
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 @@ SwitcherBackend::SwitcherBackend(QObject *parent)
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::keybdSwitchedToAnotherActivity()
void SwitcherBackend::showActivitySwitcherIfNeeded()
{
if (!m_lastInvokedAction) {
if (!m_lastInvokedAction || m_dropModeActive) {
return;
}
......@@ -405,7 +416,7 @@ void SwitcherBackend::setShouldShowSwitcher(bool shouldShowSwitcher)
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 @@ void SwitcherBackend::stopActivity(const QString &activity)
{
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<WId> &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();
}
}
......@@ -45,6 +45,7 @@ class SwitcherBackend : public QObject {
Q_OBJECT
Q_PROPERTY(bool shouldShowSwitcher READ shouldShowSwitcher WRITE setShouldShowSwitcher NOTIFY shouldShowSwitcherChanged)
Q_PROPERTY(bool dropEnabled READ dropEnabled CONSTANT)
public:
......@@ -69,6 +70,10 @@ public Q_SLOTS:
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 <typename Handler>
inline void registerShortcut(const QString &actionName, const QString &name,
......@@ -99,6 +104,9 @@ private:
QTimer m_modKeyPollingTimer;
QString m_previousActivity;
bool m_dropModeActive;
QTimer m_dropModeHider;
SortedActivitiesModel *m_runningActivitiesModel = nullptr;
SortedActivitiesModel *m_stoppedActivitiesModel = nullptr;
......
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