diff --git a/applets/taskmanager/CMakeLists.txt b/applets/taskmanager/CMakeLists.txt
--- a/applets/taskmanager/CMakeLists.txt
+++ b/applets/taskmanager/CMakeLists.txt
@@ -26,6 +26,7 @@
KF5::I18n
KF5::KIOCore
KF5::KIOWidgets
+ KF5::KIOFileWidgets # KFilePlacesModel
KF5::Plasma
KF5::Service
KF5::WindowSystem)
diff --git a/applets/taskmanager/package/contents/config/main.xml b/applets/taskmanager/package/contents/config/main.xml
--- a/applets/taskmanager/package/contents/config/main.xml
+++ b/applets/taskmanager/package/contents/config/main.xml
@@ -75,6 +75,10 @@
+
+
+
+
diff --git a/applets/taskmanager/package/contents/ui/ContextMenu.qml b/applets/taskmanager/package/contents/ui/ContextMenu.qml
--- a/applets/taskmanager/package/contents/ui/ContextMenu.qml
+++ b/applets/taskmanager/package/contents/ui/ContextMenu.qml
@@ -28,7 +28,11 @@
PlasmaComponents.ContextMenu {
id: menu
+ property QtObject backend
property QtObject mpris2Source
+ property var placesPreferences: ({})
+
+ property bool showAllPlaces
placement: {
if (plasmoid.location == PlasmaCore.Types.LeftEdge) {
@@ -52,6 +56,13 @@
}
}
+ Component.onCompleted: {
+ // Cannot have "Connections" as child of PlasmaCoponents.ContextMenu.
+ backend.showAllPlaces.connect(function() {
+ visualParent.showContextMenu({showAllPlaces: true});
+ });
+ }
+
function show() {
loadDynamicLaunchActions(visualParent.m.LauncherUrlWithoutIcon);
backend.ungrabMouse(visualParent);
@@ -73,29 +84,23 @@
}
function loadDynamicLaunchActions(launcherUrl) {
- var actionList = backend.jumpListActions(launcherUrl, menu);
-
- for (var i = 0; i < actionList.length; ++i) {
- var item = newMenuItem(menu);
- item.action = actionList[i];
- menu.addMenuItem(item, virtualDesktopsMenuItem);
- }
-
- if (actionList.length > 0) {
- menu.addMenuItem(newSeparator(menu), virtualDesktopsMenuItem);
- }
-
- var actionList = backend.recentDocumentActions(launcherUrl, menu);
-
- for (var i = 0; i < actionList.length; ++i) {
- var item = newMenuItem(menu);
- item.action = actionList[i];
- menu.addMenuItem(item, virtualDesktopsMenuItem);
- }
+ var lists = [
+ backend.jumpListActions(launcherUrl, menu),
+ backend.placesActions(launcherUrl, showAllPlaces, placesPreferences, menu),
+ backend.recentDocumentActions(launcherUrl, menu)
+ ]
+
+ lists.forEach(function (list) {
+ for (var i = 0; i < list.length; ++i) {
+ var item = newMenuItem(menu);
+ item.action = list[i];
+ menu.addMenuItem(item, virtualDesktopsMenuItem);
+ }
- if (actionList.length > 0) {
- menu.addMenuItem(newSeparator(menu), virtualDesktopsMenuItem);
- }
+ if (list.length > 0) {
+ menu.addMenuItem(newSeparator(menu), virtualDesktopsMenuItem);
+ }
+ });
// Add Media Player control actions
var sourceName = mpris2Source.sourceNameForLauncherUrl(launcherUrl);
diff --git a/applets/taskmanager/package/contents/ui/Task.qml b/applets/taskmanager/package/contents/ui/Task.qml
--- a/applets/taskmanager/package/contents/ui/Task.qml
+++ b/applets/taskmanager/package/contents/ui/Task.qml
@@ -100,11 +100,7 @@
pressX = mouse.x;
pressY = mouse.y;
} else if (mouse.button == Qt.RightButton) {
- if (plasmoid.configuration.showToolTips) {
- toolTip.hideToolTip();
- }
-
- tasks.createContextMenu(task).show();
+ showContextMenu();
}
}
@@ -169,6 +165,14 @@
: tasksModel.makeModelIndex(index));
}
+ function showContextMenu(args) {
+ if (plasmoid.configuration.showToolTips) {
+ toolTip.hideToolTip();
+ }
+
+ tasks.createContextMenu(task, args).show();
+ }
+
Component {
id: taskInitComponent
diff --git a/applets/taskmanager/package/contents/ui/main.qml b/applets/taskmanager/package/contents/ui/main.qml
--- a/applets/taskmanager/package/contents/ui/main.qml
+++ b/applets/taskmanager/package/contents/ui/main.qml
@@ -57,6 +57,21 @@
signal windowsHovered(variant winIds, bool hovered)
signal presentWindows(variant winIds)
+ readonly property var placesPreferences: {
+ var preferences = {}
+
+ var preferencesConfig = plasmoid.configuration.placesPreferences
+ if (preferencesConfig) {
+ try {
+ preferences = JSON.parse(preferencesConfig)
+ } catch (e) {
+ console.warn("Failed to parse Task Manager places preferencesConfig", e)
+ }
+ }
+
+ return preferences
+ }
+
onWidthChanged: {
taskList.width = LayoutManager.layoutWidth();
@@ -176,6 +191,17 @@
onAddLauncher: {
tasks.addLauncher(url);
}
+
+ onTrackPlaceLaunch: {
+ var stringUrl = url.toString()
+ if (!stringUrl) {
+ return
+ }
+
+ var preferences = placesPreferences
+ preferences[stringUrl] = (preferences[stringUrl] || 0) + 1
+ plasmoid.configuration.placesPreferences = JSON.stringify(preferences)
+ }
}
PlasmaCore.DataSource {
@@ -404,11 +430,13 @@
dragSource = null;
}
- function createContextMenu(task) {
- var menu = tasks.contextMenuComponent.createObject(task);
- menu.visualParent = task;
- menu.mpris2Source = mpris2Source;
- return menu;
+ function createContextMenu(task, args) {
+ var initialArgs = args || {}
+ initialArgs.visualParent = task;
+ initialArgs.mpris2Source = mpris2Source;
+ initialArgs.placesPreferences = tasks.placesPreferences;
+ initialArgs.backend = backend
+ return tasks.contextMenuComponent.createObject(task, initialArgs);
}
Component.onCompleted: {
diff --git a/applets/taskmanager/plugin/backend.h b/applets/taskmanager/plugin/backend.h
--- a/applets/taskmanager/plugin/backend.h
+++ b/applets/taskmanager/plugin/backend.h
@@ -66,6 +66,7 @@
void setHighlightWindows(bool highlight);
Q_INVOKABLE QVariantList jumpListActions(const QUrl &launcherUrl, QObject *parent);
+ Q_INVOKABLE QVariantList placesActions(const QUrl &launcherUrl, bool showAllPlaces, const QJsonObject &preferences, QObject *parent);
Q_INVOKABLE QVariantList recentDocumentActions(const QUrl &launcherUrl, QObject *parent);
Q_INVOKABLE void setActionGroup(QAction *action) const;
@@ -91,6 +92,9 @@
void highlightWindowsChanged() const;
void addLauncher(const QUrl &url) const;
+ void trackPlaceLaunch(const QUrl &url) const;
+ void showAllPlaces();
+
private Q_SLOTS:
void toolTipWindowChanged(QQuickWindow *window);
void handleJumpListAction() const;
diff --git a/applets/taskmanager/plugin/backend.cpp b/applets/taskmanager/plugin/backend.cpp
--- a/applets/taskmanager/plugin/backend.cpp
+++ b/applets/taskmanager/plugin/backend.cpp
@@ -22,6 +22,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -32,14 +33,18 @@
#include
#include
#include
+#include
#include
#include
+#include
#include
#include
#include
#include
+#include
+
namespace KAStats = KActivities::Stats;
using namespace KAStats;
@@ -157,6 +162,77 @@
return actions;
}
+QVariantList Backend::placesActions(const QUrl &launcherUrl, bool showAllPlaces, const QJsonObject &preferences, QObject *parent)
+{
+ QVariantList actions;
+
+ if (!parent || !launcherUrl.isValid() || !launcherUrl.isLocalFile()
+ || !KDesktopFile::isDesktopFile(launcherUrl.toLocalFile())) {
+ return actions;
+ }
+
+ KDesktopFile desktopFile(launcherUrl.toLocalFile());
+
+ // Since we can't have dynamic jump list actions, at least add the user's "Places" for file managers.
+ const QStringList &categories = desktopFile.desktopGroup().readXdgListEntry(QStringLiteral("Categories"));
+ if (!categories.contains(QLatin1String("FileManager"))) {
+ return actions;
+ }
+
+ QScopedPointer placesModel(new KFilePlacesModel());
+ for (int i = 0; i < placesModel->rowCount(); ++i) {
+ QModelIndex idx = placesModel->index(i, 0);
+
+ if (placesModel->data(idx, KFilePlacesModel::HiddenRole).toBool()) {
+ continue;
+ }
+
+ const QString &title = placesModel->data(idx, Qt::DisplayRole).toString();
+ const QIcon &icon = placesModel->data(idx, Qt::DecorationRole).value();
+ const QUrl &url = placesModel->data(idx, KFilePlacesModel::UrlRole).toUrl();
+
+ QAction *action = new QAction(parent);
+ action->setText(title);
+ action->setIcon(icon);
+ action->setProperty("preference", preferences.value(url.toString()).toInt());
+
+ connect(action, &QAction::triggered, this, [this, action, url, launcherUrl] {
+ KService::Ptr service = KService::serviceByDesktopPath(launcherUrl.toLocalFile());
+ if (!service) {
+ return;
+ }
+
+ KRun::runService(*service, {url}, QApplication::activeWindow());
+ emit trackPlaceLaunch(url);
+ });
+
+ actions << QVariant::fromValue(action);
+ }
+
+ if (!showAllPlaces && actions.count() > 7) {
+ const int totalActionCount = actions.count();
+
+ QVariantList preferenceSortedActions = actions;
+ std::sort(preferenceSortedActions.begin(), preferenceSortedActions.end(), [](const QVariant &a, const QVariant &b) {
+ auto *actionA = a.value();
+ auto *actionB = b.value();
+ return actionA->property("preference").toInt() > actionB->property("preference").toInt();
+ });
+
+ while (actions.count() > 5) {
+ actions.removeOne(preferenceSortedActions.takeLast());
+ }
+
+
+ QAction *action = new QAction(parent);
+ action->setText(i18ncp("Show all user Places", "%1 More Place", "%1 More Places", totalActionCount - actions.count()));
+ connect(action, &QAction::triggered, this, &Backend::showAllPlaces);
+ actions << QVariant::fromValue(action);
+ }
+
+ return actions;
+}
+
QVariantList Backend::recentDocumentActions(const QUrl &launcherUrl, QObject *parent)
{
QVariantList actions;
diff --git a/containments/panel/contents/code/LayoutManager.js b/containments/panel/contents/code/LayoutManager.js
--- a/containments/panel/contents/code/LayoutManager.js
+++ b/containments/panel/contents/code/LayoutManager.js
@@ -29,30 +29,25 @@
//array, a cell for encoded item order
var itemsArray = configString.split(";");
- //map applet id->order in panel
- var idsOrder = new Object();
- //map order in panel -> applet pointer
- var appletsOrder = new Object();
+ var applets = plasmoid.applets
+ applets.sort(function (a, b) {
+ var aIdx = itemsArray.indexOf(String(a.id))
+ var bIdx = itemsArray.indexOf(String(b.id))
+
+ // applets that aren't listed in the order are sorted last
+ if (aIdx === -1 && bIdx !== -1) {
+ return 1
+ } else if (aIdx !== -1 && bIdx === -1) {
+ return -1
+ }
- for (var i = 0; i < itemsArray.length; i++) {
- //property name: applet id
- //property value: order
- idsOrder[itemsArray[i]] = i;
- }
+ return aIdx - bIdx
+ })
- for (var i = 0; i < plasmoid.applets.length; ++i) {
- if (idsOrder[plasmoid.applets[i].id] !== undefined) {
- appletsOrder[idsOrder[plasmoid.applets[i].id]] = plasmoid.applets[i];
- //ones that weren't saved in AppletOrder go to the end
- } else {
- appletsOrder["unordered"+i] = plasmoid.applets[i];
- }
- }
+ applets.forEach(function (applet) {
+ root.addApplet(applet, -1, -1)
+ })
- //finally, restore the applets in the correct order
- for (var i in appletsOrder) {
- root.addApplet(appletsOrder[i], -1, -1)
- }
//rewrite, so if in the orders there were now invalid ids or if some were missing creates a correct list instead
save();
}