diff --git a/desktoppackage/contents/activitymanager/ActivityItem.qml b/desktoppackage/contents/activitymanager/ActivityItem.qml index d6479952d..7736af195 100644 --- a/desktoppackage/contents/activitymanager/ActivityItem.qml +++ b/desktoppackage/contents/activitymanager/ActivityItem.qml @@ -1,365 +1,412 @@ +/* + * Copyright (C) 2014-2020 Ivan Cukic + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * or (at your option) any later version, as published by the Free + * Software Foundation + * + * 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. + */ + import QtQuick 2.0 import org.kde.plasma.components 2.0 as PlasmaComponents 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 import org.kde.activities 0.1 as Activities import org.kde.activities.settings 0.1 import "static.js" as S Item { id: root property int innerPadding : units.largeSpacing property bool current : false property bool selected : false property bool stoppable : true property alias title : title.text property alias icon : icon.source property alias hasWindows : hasWindowsIndicator.visible z : current ? 10 : selected ? 5 : 0 property string activityId : "" property string background : "" onBackgroundChanged: if (background[0] !== '#') { // We have a proper wallpaper, hurroo! backgroundColor.visible = false; } else { // We have only a color backgroundColor.color = background; backgroundColor.visible = true; } signal clicked width : 200 // height : width * 1 / units.displayAspectRatio // Marco removed displayAspectRatio height : width * 9.0 / 16.0 Item { anchors { fill: parent } // Background until we get something real Rectangle { id: backgroundColor anchors.fill: parent // This is intentional - while waiting for the wallpaper, // we are showing a semi-transparent black background color: "black" opacity: root.selected ? .8 : .5 } Image { id: backgroundWallpaper anchors.fill: parent visible: !backgroundColor.visible source: "image://wallpaperthumbnail/" + background sourceSize: Qt.size(width, height) } // Title and the icon Rectangle { id: shade width: parent.height height: parent.width anchors.centerIn: parent rotation: 90 gradient: Gradient { GradientStop { position: 1.0; color: "black" } GradientStop { position: 0.0; color: "transparent" } } opacity : root.selected ? 0.5 : 1.0 } Rectangle { - id: highlight + id: currentActivityHighlight visible: root.current border.width: root.current ? units.smallSpacing : 0 border.color: theme.highlightColor + z: 10 + anchors { fill: parent // Hide the rounding error on the bottom of the rectangle bottomMargin: -1 } color: "transparent" - - // z: 1 } Item { id: titleBar anchors { top : parent.top left : parent.left right : parent.right leftMargin : 2 * units.smallSpacing + 2 topMargin : 2 * units.smallSpacing } Text { id: title color : "white" elide : Text.ElideRight visible : shade.visible font.bold : true anchors { top : parent.top left : parent.left right : icon.left } } Text { id: description color : "white" elide : Text.ElideRight text : model.description opacity : .6 anchors { top : title.bottom left : parent.left right : icon.left } } PlasmaCore.IconItem { id: icon width : units.iconSizes.medium height : width anchors { right : parent.right rightMargin : 2 * units.smallSpacing } } } Column { id: statsBar height: childrenRect.height + units.smallSpacing anchors { bottom : controlBar.top left : parent.left right : parent.right leftMargin : 2 * units.smallSpacing + 2 rightMargin : 2 * units.smallSpacing bottomMargin : units.smallSpacing } PlasmaCore.IconItem { id : hasWindowsIndicator source : "window-duplicate" width : 16 height : width opacity : .6 visible : false } Text { id: lastUsedDate color : "white" elide : Text.ElideRight opacity : .6 text: root.current ? i18nd("plasma_shell_org.kde.plasma.desktop", "Currently being used") : model.lastTimeUsedString } + } + + Rectangle { + id: dropHighlight + visible: moveDropAction.isHovered || copyDropAction.isHovered + + onVisibleChanged: { + ActivitySwitcher.Backend.setDropMode(visible); + if (visible) { + root.state = "dropAreasShown"; + } else { + root.state = "plain"; + } + } - // Text { - // id: stats - // - // color : "white" - // elide : Text.ElideRight - // opacity : .6 - // - // text: "6 documents, 2 applications" - // visible: false - // - // anchors { - // top : lastUsedDate.bottom - // left : parent.left - // right : parent.right - // } - // } + anchors { + fill: parent + topMargin: icon.height + 3 * units.smallSpacing + } + + opacity: .75 + color: theme.backgroundColor } - DND.DropArea { - PlasmaComponents.Highlight { - id: dropHighlight - anchors.fill: parent - visible: false + TaskDropArea { + id: moveDropAction + + anchors { + right: parent.horizontalCenter + left: parent.left + top: parent.top + bottom: parent.bottom } - anchors.fill: parent + topPadding: icon.height + 3 * units.smallSpacing + actionVisible: dropHighlight.visible - preventStealing: true - enabled: true + actionTitle: i18nd("plasma_shell_org.kde.plasma.desktop", "Move to\nthis activity") - onDrop: { - ActivitySwitcher.Backend.drop(event.mimeData, event.modifiers, root.activityId); + onTaskDropped: { + ActivitySwitcher.Backend.dropMove(mimeData, root.activityId); } - onDragEnter: { - ActivitySwitcher.Backend.setDropMode(true); - dropHighlight.visible = true; + onClicked: { + root.clicked(); } - onDragLeave: { - ActivitySwitcher.Backend.setDropMode(false); - dropHighlight.visible = false; + onEntered: { + S.showActivityItemActionsBar(root); } visible: ActivitySwitcher.Backend.dropEnabled + } + + TaskDropArea { + id: copyDropAction + + topPadding: icon.height + 3 * units.smallSpacing + actionVisible: dropHighlight.visible + + anchors { + right: parent.right + left: parent.horizontalCenter + top: parent.top + bottom: parent.bottom + } - MouseArea { - id: hoverArea + actionTitle: i18nd("plasma_shell_org.kde.plasma.desktop", "Show also\nin this activity") - anchors.fill : parent - onClicked : root.clicked() - hoverEnabled : true - onEntered : S.showActivityItemActionsBar(root) + onTaskDropped: { + ActivitySwitcher.Backend.dropCopy(mimeData, root.activityId); + } - Accessible.name : root.title - Accessible.role : Accessible.Button - Accessible.selected : root.selected - Accessible.onPressAction : root.clicked() + onClicked: { + root.clicked(); } + + onEntered: { + S.showActivityItemActionsBar(root); + } + + visible: ActivitySwitcher.Backend.dropEnabled + } // Controls Item { id: controlBar height: root.state == "showingControls" ? (configButton.height + 4 * units.smallSpacing) : 0 Behavior on height { NumberAnimation { duration: units.longDuration } } Behavior on opacity { NumberAnimation { duration: units.shortDuration } } clip: true anchors { bottom : parent.bottom left : parent.left right : parent.right } Rectangle { anchors { fill: parent margins: - 2 * units.smallSpacing } opacity: .75 color: theme.backgroundColor } PlasmaComponents.Button { id: configButton iconSource: "configure" tooltip: i18nd("plasma_shell_org.kde.plasma.desktop", "Configure") onClicked: ActivitySettings.configureActivity(root.activityId); anchors { left : parent.left top : parent.top leftMargin : 2 * units.smallSpacing + 2 topMargin : 2 * units.smallSpacing } } PlasmaComponents.Button { id: stopButton iconSource: "process-stop" tooltip: i18nd("plasma_shell_org.kde.plasma.desktop", "Stop activity") onClicked: ActivitySwitcher.Backend.stopActivity(activityId); anchors { right : parent.right top : parent.top rightMargin : 2 * units.smallSpacing + 2 topMargin : 2 * units.smallSpacing } } } } states: [ State { name: "plain" PropertyChanges { target: shade; visible: true } PropertyChanges { target: controlBar; opacity: 0 } }, State { name: "showingControls" PropertyChanges { target: shade; visible: true } PropertyChanges { target: controlBar; opacity: 1 } + }, + State { + name: "dropAreasShown" + // PropertyChanges { target: shade; visible: false } + PropertyChanges { target: statsBar; visible: false } + PropertyChanges { target: controlBar; opacity: 0 } } ] transitions: [ Transition { NumberAnimation { properties : "opacity" duration : units.shortDuration } } ] } diff --git a/desktoppackage/contents/activitymanager/TaskDropArea.qml b/desktoppackage/contents/activitymanager/TaskDropArea.qml new file mode 100644 index 000000000..ad60b5a1f --- /dev/null +++ b/desktoppackage/contents/activitymanager/TaskDropArea.qml @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2020 Ivan Cukic + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * or (at your option) any later version, as published by the Free + * Software Foundation + * + * 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. + */ + +import QtQuick 2.0 + +import org.kde.plasma.components 2.0 as PlasmaComponents + +import org.kde.draganddrop 2.0 as DND + +DND.DropArea { + id: root + + signal taskDropped(variant mimeData, variant modifiers) + signal clicked() + signal entered() + + property int topPadding: 0 + property string activityName: "" + property bool selected: false + property string actionTitle: "" + property bool isHovered: false + property bool actionVisible: false + + PlasmaComponents.Highlight { + id: dropHighlight + anchors { + fill: parent + // topMargin: icon.height + 3 * units.smallSpacing + topMargin: root.topPadding + } + visible: root.isHovered + z: -1 + } + + Text { + id: dropAreaLeftText + anchors { + fill: dropHighlight + leftMargin: units.largeSpacing + rightMargin: units.largeSpacing + } + + color: theme.textColor + visible: root.actionVisible + + text: root.actionTitle + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + maximumLineCount: 3 + } + + anchors { + left: parent.left + right: parent.horizontalCenter + top: parent.top + bottom: parent.bottom + } + + preventStealing: true + enabled: true + + onDrop: { + root.taskDropped(event.mimeData, event.modifiers); + } + + onDragEnter: { + root.isHovered = true; + } + + onDragLeave: { + root.isHovered = false; + } + + MouseArea { + anchors.fill : parent + onClicked : root.clicked() + hoverEnabled : true + onEntered : root.entered() + + Accessible.name : root.activityName + Accessible.role : Accessible.Button + Accessible.selected : root.selected + Accessible.onPressAction : root.clicked() + } +} diff --git a/imports/activitymanager/switcherbackend.cpp b/imports/activitymanager/switcherbackend.cpp index f57f36f96..ea8a91f69 100644 --- a/imports/activitymanager/switcherbackend.cpp +++ b/imports/activitymanager/switcherbackend.cpp @@ -1,519 +1,529 @@ /* * Copyright (C) 2014. 2015 Ivan Cukic * Copyright (C) 2009 Martin Gräßlin * Copyright (C) 2003 Lubos Lunak * Copyright (C) 1999, 2000 Matthias Ettrich * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, * or (at your option) any later version, as published by the Free * Software Foundation * * 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. */ // Self #include "switcherbackend.h" // Qt #include #include #include #include // Qml and QtQuick #include #include // KDE #include #include #include #include #include #include #include #include // X11 #include #include #include #define ACTION_NAME_NEXT_ACTIVITY "next activity" #define ACTION_NAME_PREVIOUS_ACTIVITY "previous activity" namespace { bool isPlatformX11() { static const bool isX11 = QX11Info::isPlatformX11(); return isX11; } // Taken from kwin/tabbox/tabbox.cpp Display* x11_display() { static Display *s_display = nullptr; if (!s_display) { s_display = QX11Info::display(); } return s_display; } bool x11_areKeySymXsDepressed(bool bAll, const uint keySyms[], int nKeySyms) { char keymap[32]; XQueryKeymap(x11_display(), keymap); for (int iKeySym = 0; iKeySym < nKeySyms; iKeySym++) { uint keySymX = keySyms[ iKeySym ]; uchar keyCodeX = XKeysymToKeycode(x11_display(), keySymX); int i = keyCodeX / 8; char mask = 1 << (keyCodeX - (i * 8)); // Abort if bad index value, if (i < 0 || i >= 32) return false; // If ALL keys passed need to be depressed, if (bAll) { if ((keymap[i] & mask) == 0) return false; } else { // If we are looking for ANY key press, and this key is depressed, if (keymap[i] & mask) return true; } } // If we were looking for ANY key press, then none was found, return false, // If we were looking for ALL key presses, then all were found, return true. return bAll; } bool x11_areModKeysDepressed(const QKeySequence& seq) { uint rgKeySyms[10]; int nKeySyms = 0; if (seq.isEmpty()) { return false; } int mod = seq[seq.count()-1] & Qt::KeyboardModifierMask; if (mod & Qt::SHIFT) { rgKeySyms[nKeySyms++] = XK_Shift_L; rgKeySyms[nKeySyms++] = XK_Shift_R; } if (mod & Qt::CTRL) { rgKeySyms[nKeySyms++] = XK_Control_L; rgKeySyms[nKeySyms++] = XK_Control_R; } if (mod & Qt::ALT) { rgKeySyms[nKeySyms++] = XK_Alt_L; rgKeySyms[nKeySyms++] = XK_Alt_R; } if (mod & Qt::META) { // It would take some code to determine whether the Win key // is associated with Super or Meta, so check for both. // See bug #140023 for details. rgKeySyms[nKeySyms++] = XK_Super_L; rgKeySyms[nKeySyms++] = XK_Super_R; rgKeySyms[nKeySyms++] = XK_Meta_L; rgKeySyms[nKeySyms++] = XK_Meta_R; } return x11_areKeySymXsDepressed(false, rgKeySyms, nKeySyms); } bool x11_isReverseTab(const QKeySequence &prevAction) { if (prevAction == QKeySequence(Qt::ShiftModifier | Qt::Key_Tab)) { return x11_areModKeysDepressed(Qt::SHIFT); } else { return false; } } class ThumbnailImageResponse: public QQuickImageResponse { public: ThumbnailImageResponse(const QString &id, const QSize &requestedSize); QQuickTextureFactory *textureFactory() const override; void run(); private: QString m_id; QSize m_requestedSize; QQuickTextureFactory *m_texture = nullptr; }; ThumbnailImageResponse::ThumbnailImageResponse(const QString &id, const QSize &requestedSize) : m_id(id) , m_requestedSize(requestedSize) , m_texture(nullptr) { int width = m_requestedSize.width(); int height = m_requestedSize.height(); if (width <= 0) { width = 320; } if (height <= 0) { height = 240; } if (m_id.isEmpty()) { emit finished(); return; } const auto file = QUrl::fromUserInput(m_id); KFileItemList list; list.append(KFileItem(file, QString(), 0)); auto job = KIO::filePreview(list, QSize(width, height)); job->setScaleType(KIO::PreviewJob::Scaled); job->setIgnoreMaximumSize(true); connect(job, &KIO::PreviewJob::gotPreview, this, [this,file] (const KFileItem& item, const QPixmap& pixmap) { Q_UNUSED(item); auto image = pixmap.toImage(); m_texture = QQuickTextureFactory::textureFactoryForImage(image); emit finished(); }, Qt::QueuedConnection); connect(job, &KIO::PreviewJob::failed, this, [this,job] (const KFileItem& item) { Q_UNUSED(item); qWarning() << "SwitcherBackend: FAILED to get the thumbnail" << job->errorString() << job->detailedErrorStrings(); emit finished(); }); } QQuickTextureFactory *ThumbnailImageResponse::textureFactory() const { return m_texture; } class ThumbnailImageProvider: public QQuickAsyncImageProvider { public: QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override { return new ThumbnailImageResponse(id, requestedSize); } }; } // local namespace template inline void SwitcherBackend::registerShortcut(const QString &actionName, const QString &text, const QKeySequence &shortcut, Handler &&handler) { auto action = new QAction(this); m_actionShortcut[actionName] = shortcut; action->setObjectName(actionName); action->setText(text); KGlobalAccel::self()->setShortcut(action, { shortcut }); using KActivities::Controller; connect(action, &QAction::triggered, this, std::forward(handler)); } 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)) { registerShortcut(ACTION_NAME_NEXT_ACTIVITY, i18n("Walk through activities"), Qt::META + Qt::Key_Tab, &SwitcherBackend::keybdSwitchToNextActivity); registerShortcut(ACTION_NAME_PREVIOUS_ACTIVITY, i18n("Walk through activities (Reverse)"), Qt::META + Qt::SHIFT + Qt::Key_Tab, &SwitcherBackend::keybdSwitchToPreviousActivity); 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(); } SwitcherBackend::~SwitcherBackend() { } QObject *SwitcherBackend::instance(QQmlEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(scriptEngine) engine->addImageProvider("wallpaperthumbnail", new ThumbnailImageProvider()); return new SwitcherBackend(); } void SwitcherBackend::keybdSwitchToNextActivity() { if (isPlatformX11()) { // If we are on X11, we have all needed features for meta+tab // to work properly if (x11_isReverseTab(m_actionShortcut[ACTION_NAME_PREVIOUS_ACTIVITY])) { switchToActivity(Previous); } else { switchToActivity(Next); } } else { // If we are on wayland, just switch to the next activity switchToActivity(Next); } } void SwitcherBackend::keybdSwitchToPreviousActivity() { switchToActivity(Previous); } void SwitcherBackend::switchToActivity(Direction direction) { const auto activityToSet = m_runningActivitiesModel->relativeActivity(direction == Next ? 1 : -1); if (activityToSet.isEmpty()) return; QTimer::singleShot(0, this, [this,activityToSet] () { setCurrentActivity(activityToSet); }); keybdSwitchedToAnotherActivity(); } void SwitcherBackend::keybdSwitchedToAnotherActivity() { m_lastInvokedAction = dynamic_cast(sender()); QTimer::singleShot(90, this, &SwitcherBackend::showActivitySwitcherIfNeeded); } void SwitcherBackend::showActivitySwitcherIfNeeded() { if (!m_lastInvokedAction || m_dropModeActive) { return; } auto actionName = m_lastInvokedAction->objectName(); if (!m_actionShortcut.contains(actionName)) { return; } if (isPlatformX11()) { if (!x11_areModKeysDepressed(m_actionShortcut[actionName])) { m_lastInvokedAction = nullptr; setShouldShowSwitcher(false); return; } setShouldShowSwitcher(true); } else { // We are not showing the switcher on wayland // TODO: This is a regression on wayland setShouldShowSwitcher(false); } } void SwitcherBackend::init() { // nothing } void SwitcherBackend::onCurrentActivityChanged(const QString &id) { if (m_shouldShowSwitcher) { // If we are showing the switcher because the user is // pressing Meta+Tab, we are not ready to commit the // activity change to memory return; } if (m_previousActivity == id) return; // Safe, we have a long-lived Consumer object KActivities::Info activity(id); emit showSwitchNotification(id, activity.name(), activity.icon()); KConfig config("kactivitymanagerd-switcher"); KConfigGroup times(&config, "LastUsed"); const auto now = QDateTime::currentDateTime().toTime_t(); // Updating the time for the activity we just switched to // in the case we do not power off properly, and on the next // start, kamd switches to another activity for some reason times.writeEntry(id, now); if (!m_previousActivity.isEmpty()) { // When leaving an activity, say goodbye and fondly remember // the last time we saw it times.writeEntry(m_previousActivity, now); } times.sync(); m_previousActivity = id; } bool SwitcherBackend::shouldShowSwitcher() const { return m_shouldShowSwitcher; } void SwitcherBackend::setShouldShowSwitcher(bool shouldShowSwitcher) { if (m_shouldShowSwitcher == shouldShowSwitcher) return; m_shouldShowSwitcher = shouldShowSwitcher; if (m_shouldShowSwitcher) { // TODO: We really should NOT do this by polling m_modKeyPollingTimer.start(); } else { m_modKeyPollingTimer.stop(); // We might have an unprocessed onCurrentActivityChanged onCurrentActivityChanged(m_activities.currentActivity()); } emit shouldShowSwitcherChanged(m_shouldShowSwitcher); } QAbstractItemModel *SwitcherBackend::runningActivitiesModel() const { return m_runningActivitiesModel; } QAbstractItemModel *SwitcherBackend::stoppedActivitiesModel() const { return m_stoppedActivitiesModel; } void SwitcherBackend::setCurrentActivity(const QString &activity) { m_activities.setCurrentActivity(activity); } 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::dropCopy(QMimeData* mimeData, const QVariant &activityId) +{ + drop(mimeData, Qt::ControlModifier, activityId); +} + +void SwitcherBackend::dropMove(QMimeData* mimeData, const QVariant &activityId) +{ + drop(mimeData, 0, activityId); +} + 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(); } } diff --git a/imports/activitymanager/switcherbackend.h b/imports/activitymanager/switcherbackend.h index e141fe1b3..183f5d9ad 100644 --- a/imports/activitymanager/switcherbackend.h +++ b/imports/activitymanager/switcherbackend.h @@ -1,115 +1,117 @@ /* * Copyright (C) 2014 Ivan Cukic * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, * or (at your option) any later version, as published by the Free * Software Foundation * * 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 SWITCHER_BACKEND_H #define SWITCHER_BACKEND_H // Qt #include #include #include #include #include #include // KDE #include #include // Local #include "sortedactivitiesmodel.h" class QAction; class QQmlEngine; class QJSEngine; namespace KIO { class PreviewJob; } class SwitcherBackend : public QObject { Q_OBJECT Q_PROPERTY(bool shouldShowSwitcher READ shouldShowSwitcher WRITE setShouldShowSwitcher NOTIFY shouldShowSwitcherChanged) Q_PROPERTY(bool dropEnabled READ dropEnabled CONSTANT) public: explicit SwitcherBackend(QObject *parent = nullptr); ~SwitcherBackend() override; static QObject *instance(QQmlEngine *engine, QJSEngine *scriptEngine); Q_SIGNALS: void showSwitchNotification(const QString &id, const QString &name, const QString &icon); void shouldShowSwitcherChanged(bool value); public Q_SLOTS: void init(); bool shouldShowSwitcher() const; void setShouldShowSwitcher(bool shouldShowSwitcher); QAbstractItemModel *runningActivitiesModel() const; QAbstractItemModel *stoppedActivitiesModel() const; void setCurrentActivity(const QString &activity); void stopActivity(const QString &activity); void setDropMode(bool value); void drop(QMimeData* mimeData, int modifiers, const QVariant &activityId); + void dropCopy(QMimeData* mimeData, const QVariant &activityId); + void dropMove(QMimeData* mimeData, const QVariant &activityId); bool dropEnabled() const; private: template inline void registerShortcut(const QString &actionName, const QString &name, const QKeySequence &shortcut, Handler &&handler); enum Direction { Next, Previous }; void switchToActivity(Direction i); private Q_SLOTS: void keybdSwitchToNextActivity(); void keybdSwitchToPreviousActivity(); void keybdSwitchedToAnotherActivity(); void showActivitySwitcherIfNeeded(); void onCurrentActivityChanged(const QString &id); private: QHash m_actionShortcut; QAction *m_lastInvokedAction = nullptr; KActivities::Controller m_activities; bool m_shouldShowSwitcher; QTimer m_modKeyPollingTimer; QString m_previousActivity; bool m_dropModeActive; QTimer m_dropModeHider; SortedActivitiesModel *m_runningActivitiesModel = nullptr; SortedActivitiesModel *m_stoppedActivitiesModel = nullptr; }; #endif // SWITCHER_BACKEND_H