mirror of
https://gitgud.io/wackyideas/aerothemeplasma.git
synced 2026-06-19 03:45:50 +00:00
624 lines
20 KiB
QML
624 lines
20 KiB
QML
/*
|
|
SPDX-FileCopyrightText: 2012-2016 Eike Hein <hein@kde.org>
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
import QtQuick
|
|
import QtQuick.Layouts 1.15
|
|
import QtQml 2.15
|
|
|
|
import org.kde.plasma.plasmoid 2.0
|
|
import org.kde.plasma.components 3.0 as PlasmaComponents3
|
|
import org.kde.plasma.core as PlasmaCore
|
|
import org.kde.ksvg 1.0 as KSvg
|
|
import org.kde.plasma.private.mpris as Mpris
|
|
import org.kde.kirigami 2.20 as Kirigami
|
|
import org.kde.kitemmodels as KItemModels
|
|
|
|
import org.kde.taskmanager as TaskManager
|
|
import aeroshell.taskmanager as TaskManagerApplet
|
|
import org.kde.plasma.workspace.dbus as DBus
|
|
import org.kde.plasma.plasma5support as Plasma5Support
|
|
|
|
import org.kde.kquickcontrolsaddons
|
|
import org.kde.kwindowsystem
|
|
import org.kde.notificationmanager as NotificationManager
|
|
|
|
import "code/layoutmetrics.js" as LayoutMetrics
|
|
import "code/tools.js" as TaskTools
|
|
|
|
PlasmoidItem {
|
|
id: tasks
|
|
|
|
// For making a bottom to top layout since qml flow can't do that.
|
|
// We just hang the task manager upside down to achieve that.
|
|
// This mirrors the tasks as well, so we just rotate them again to fix that (see Task.qml).
|
|
rotation: Plasmoid.configuration.reverseMode && Plasmoid.formFactor === PlasmaCore.Types.Vertical ? 180 : 0
|
|
|
|
readonly property bool shouldShrinkToZero: tasksModel.count === 0
|
|
property bool vertical: Plasmoid.formFactor === PlasmaCore.Types.Vertical
|
|
property bool iconsOnly: !Plasmoid.configuration.showLabels //Plasmoid.pluginName === "org.kde.plasma.icontasks"
|
|
|
|
// Used to run separate programs through this plasmoid.
|
|
Plasma5Support.DataSource {
|
|
id: menu_executable
|
|
engine: "executable"
|
|
connectedSources: []
|
|
onNewData: (sourceName, data) => {
|
|
var exitCode = data["exit code"]
|
|
var exitStatus = data["exit status"]
|
|
var stdout = data["stdout"]
|
|
var stderr = data["stderr"]
|
|
exited(sourceName, exitCode, exitStatus, stdout, stderr)
|
|
disconnectSource(sourceName)
|
|
}
|
|
function exec(cmd) {
|
|
if (cmd) {
|
|
connectSource(cmd)
|
|
}
|
|
}
|
|
signal exited(string cmd, int exitCode, int exitStatus, string stdout, string stderr)
|
|
}
|
|
|
|
Plasmoid.contextualActions: [
|
|
PlasmaCore.Action {
|
|
text: i18n("Task Manager")
|
|
icon.name: "ksysguardd"
|
|
onTriggered: {
|
|
menu_executable.exec("kstart ksysguard");
|
|
|
|
}
|
|
}
|
|
]
|
|
|
|
NotificationManager.Notifications {
|
|
id: historyModel
|
|
showExpired: true
|
|
showDismissed: true
|
|
showJobs: true
|
|
groupLimit: 2
|
|
expandUnread: true
|
|
}
|
|
|
|
onIconsOnlyChanged: {
|
|
iconGeometryTimer.start();
|
|
}
|
|
|
|
function findTask(desktopItem) {
|
|
const pinnedTasks = Plasmoid.configuration.launchers;
|
|
for(var i = 0; i < pinnedTasks.length; i++) {
|
|
if(pinnedTasks[i] == desktopItem)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
function pinTask(desktopItem) {
|
|
tasksModel.requestAddLauncher(desktopItem);
|
|
}
|
|
function unpinTask(desktopItem) {
|
|
var pinnedTasks = Plasmoid.configuration.launchers;
|
|
const pinnedTaskIndex = findTask(desktopItem);
|
|
if(pinnedTaskIndex == -1) {
|
|
return false;
|
|
}
|
|
tasksModel.requestRemoveLauncher(desktopItem);
|
|
return true;
|
|
|
|
}
|
|
|
|
property var jumpListItem: null
|
|
property bool pinnedToolTipOpen: false
|
|
property bool toolTipOpen: false
|
|
onJumpListItemChanged: {
|
|
taskList.forceMouseEvent();
|
|
}
|
|
|
|
property QtObject jumpListComponent: Qt.createComponent("TasksMenu.qml");
|
|
property QtObject contextMenuComponent: Qt.createComponent("ContextMenu.qml")
|
|
|
|
property bool needLayoutRefresh: false;
|
|
property var taskClosedWithMouseMiddleButton: []
|
|
property alias taskList: taskList
|
|
property alias animationManager: animationManager
|
|
|
|
property alias taskBackend: backend
|
|
property alias mediaBackend: mpris2Source
|
|
|
|
property int pulseAudioUsers: 0
|
|
|
|
property bool compositionEnabled: {
|
|
if(KWindowSystem.isPlatformX11) {
|
|
return KX11Extras.compositingActive;
|
|
} else return true; // Composition is always enabled in Wayland
|
|
}
|
|
|
|
function acquirePulseAudioMonitoring() {
|
|
++pulseAudioUsers;
|
|
if (!pulseAudio.item) {
|
|
--pulseAudioUsers;
|
|
}
|
|
return pulseAudio.item !== null;
|
|
}
|
|
|
|
function releasePulseAudioMonitoring() {
|
|
if (pulseAudioUsers > 0 && --pulseAudioUsers === 0) {
|
|
clearAudioStreams();
|
|
}
|
|
}
|
|
|
|
function clearAudioStreams() {
|
|
for (var i = 0; i < tasksModel.count; ++i) {
|
|
var task = taskList.itemAtIndex(i);
|
|
if (task) {
|
|
task.audioStreams = [];
|
|
}
|
|
}
|
|
}
|
|
|
|
Item {
|
|
id: animationManager
|
|
property var finishAnimation: {}
|
|
|
|
function addItem(id) {
|
|
if(typeof finishAnimation === "undefined")
|
|
finishAnimation = {};
|
|
finishAnimation[id] = true;
|
|
}
|
|
function getItem(id) {
|
|
if(typeof finishAnimation[id] === "undefined") return false;
|
|
return finishAnimation[id];
|
|
}
|
|
function removeItem(id) {
|
|
if(typeof finishAnimation[id] === "undefined") return;
|
|
delete finishAnimation[id];
|
|
}
|
|
Component.onCompleted: {
|
|
finishAnimation = {};
|
|
}
|
|
}
|
|
|
|
preferredRepresentation: fullRepresentation
|
|
|
|
Plasmoid.constraintHints: Plasmoid.CanFillArea
|
|
|
|
Layout.fillWidth: tasks.vertical ? true : Plasmoid.configuration.fill
|
|
Layout.fillHeight: !tasks.vertical ? true : Plasmoid.configuration.fill
|
|
Layout.minimumWidth: {
|
|
if (shouldShrinkToZero) {
|
|
return Kirigami.Units.iconSizes.small; // For edit mode
|
|
}
|
|
return tasks.vertical ? 0 : LayoutMetrics.preferredMinWidth();
|
|
}
|
|
Layout.minimumHeight: {
|
|
if (shouldShrinkToZero) {
|
|
return Kirigami.Units.iconSizes.small; // For edit mode
|
|
}
|
|
return !tasks.vertical ? 0 : LayoutMetrics.preferredMinHeight();
|
|
}
|
|
|
|
function setRequestedInhibitDnd(value) {
|
|
// This is modifying the value in the panel containment that
|
|
// inhibits accepting drag and drop, so that we don't accidentally
|
|
// drop the task on this panel.
|
|
let item = this;
|
|
while (item.parent) {
|
|
item = item.parent;
|
|
if (item.appletRequestsInhibitDnD !== undefined) {
|
|
item.appletRequestsInhibitDnD = value
|
|
}
|
|
}
|
|
}
|
|
property Item dragSource: null
|
|
property Item dragItem: null
|
|
|
|
signal requestLayout
|
|
|
|
function windowsHovered(winIds: var, hovered: bool): DBus.DBusPendingReply {
|
|
if (!Plasmoid.configuration.highlightWindows) {
|
|
return;
|
|
}
|
|
return DBus.SessionBus.asyncCall({service: "org.kde.KWin.HighlightWindow", path: "/org/kde/KWin/HighlightWindow", iface: "org.kde.KWin.HighlightWindow", member: "highlightWindows", arguments: [hovered ? winIds : []], signature: "(as)"});
|
|
}
|
|
|
|
function cancelHighlightWindows(): DBus.DBusPendingReply {
|
|
return DBus.SessionBus.asyncCall({service: "org.kde.KWin.HighlightWindow", path: "/org/kde/KWin/HighlightWindow", iface: "org.kde.KWin.HighlightWindow", member: "highlightWindows", arguments: [[]], signature: "(as)"});
|
|
}
|
|
|
|
function activateWindowView(winIds: var): DBus.DBusPendingReply {
|
|
if (!effectWatcher.registered) {
|
|
return;
|
|
}
|
|
cancelHighlightWindows();
|
|
return DBus.SessionBus.asyncCall({service: "org.kde.KWin.Effect.WindowView1", path: "/org/kde/KWin/Effect/WindowView1", iface: "org.kde.KWin.Effect.WindowView1", member: "activate", arguments: [winIds.map(s => String(s))], signature: "(as)"});
|
|
}
|
|
|
|
Timer {
|
|
id: syncDelay
|
|
interval: 100
|
|
onTriggered: { tasksModel.syncLaunchers(); }
|
|
}
|
|
onDragItemChanged: {
|
|
if (dragItem == null) {
|
|
syncDelay.start();
|
|
tasks.publishIconGeometries(null, tasks);
|
|
}
|
|
}
|
|
|
|
function publishIconGeometries(taskItems) {
|
|
if (TaskTools.taskManagerInstanceCount >= 2) {
|
|
return;
|
|
}
|
|
for(var i = 0; i < tasksModel.count; ++i) {
|
|
var task = taskList.itemAtIndex(i);
|
|
if (!task.model.IsLauncher && !task.model.IsStartup) {
|
|
tasks.tasksModel.requestPublishDelegateGeometry(tasks.tasksModel.makeModelIndex(task.index),
|
|
backend.globalRect(task), task);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
function taskInLauncherList(launcher) {
|
|
for(var i = 0; i < tasksModel.launcherList.length; i++) {
|
|
if(tasksModel.launcherList[i].includes(launcher)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
// Hack that prevents tasks overlapping each other sometimes. Programs that mess around with the system tray like VLC tend to cause this
|
|
Timer {
|
|
id: layoutDelay
|
|
interval: 1000
|
|
onTriggered: {
|
|
taskList.width += 1;
|
|
taskList.width -= 1;
|
|
}
|
|
}
|
|
|
|
property Item taskThumbnail: ToolTip { }
|
|
|
|
property TaskManager.TasksModel tasksModel: TaskManager.TasksModel {
|
|
id: tasksModel
|
|
|
|
readonly property int logicalLauncherCount: {
|
|
if (Plasmoid.configuration.separateLaunchers) {
|
|
return launcherCount;
|
|
}
|
|
|
|
var startupsWithLaunchers = 0;
|
|
|
|
for(var i = 0; i < tasksModel.count; ++i) {
|
|
var item = taskList.itemAtIndex(i);
|
|
|
|
if (item?.model?.IsStartup && item.model.HasLauncher) {
|
|
++startupsWithLaunchers;
|
|
}
|
|
}
|
|
return launcherCount + startupsWithLaunchers;
|
|
}
|
|
|
|
virtualDesktop: virtualDesktopInfo.currentDesktop
|
|
screenGeometry: Plasmoid.containment.screenGeometry
|
|
activity: activityInfo.currentActivity
|
|
|
|
filterByVirtualDesktop: Plasmoid.configuration.showOnlyCurrentDesktop
|
|
filterByScreen: Plasmoid.configuration.showOnlyCurrentScreen
|
|
filterByActivity: Plasmoid.configuration.showOnlyCurrentActivity
|
|
filterNotMinimized: Plasmoid.configuration.showOnlyMinimized
|
|
|
|
hideActivatedLaunchers: true //tasks.iconsOnly || Plasmoid.configuration.hideLauncherOnStart
|
|
sortMode: sortModeEnumValue(Plasmoid.configuration.sortingStrategy)
|
|
launchInPlace: tasks.iconsOnly && Plasmoid.configuration.sortingStrategy === 1
|
|
separateLaunchers: false
|
|
groupMode: Plasmoid.configuration.groupPopups ? TaskManager.TasksModel.GroupApplications : TaskManager.TasksModel.GroupDisabled
|
|
groupInline: !Plasmoid.configuration.groupPopups && !tasks.iconsOnly
|
|
groupingWindowTasksThreshold: (Plasmoid.configuration.onlyGroupWhenFull && !tasks.iconsOnly
|
|
? LayoutMetrics.optimumCapacity(width, height) + 1 : -1)
|
|
|
|
onLauncherListChanged: {
|
|
Plasmoid.configuration.launchers = launcherList;
|
|
}
|
|
|
|
onGroupingAppIdBlacklistChanged: {
|
|
Plasmoid.configuration.groupingAppIdBlacklist = groupingAppIdBlacklist;
|
|
}
|
|
|
|
onGroupingLauncherUrlBlacklistChanged: {
|
|
Plasmoid.configuration.groupingLauncherUrlBlacklist = groupingLauncherUrlBlacklist;
|
|
}
|
|
|
|
function sortModeEnumValue(index) {
|
|
switch (index) {
|
|
case 0:
|
|
return TaskManager.TasksModel.SortDisabled;
|
|
case 1:
|
|
return TaskManager.TasksModel.SortManual;
|
|
case 2:
|
|
return TaskManager.TasksModel.SortAlpha;
|
|
case 3:
|
|
return TaskManager.TasksModel.SortVirtualDesktop;
|
|
case 4:
|
|
return TaskManager.TasksModel.SortActivity;
|
|
default:
|
|
return TaskManager.TasksModel.SortDisabled;
|
|
}
|
|
}
|
|
|
|
function groupModeEnumValue(index) {
|
|
switch (index) {
|
|
case 0:
|
|
return TaskManager.TasksModel.GroupDisabled;
|
|
case 1:
|
|
return TaskManager.TasksModel.GroupApplications;
|
|
}
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
launcherList = Plasmoid.configuration.launchers;
|
|
groupingAppIdBlacklist = Plasmoid.configuration.groupingAppIdBlacklist;
|
|
groupingLauncherUrlBlacklist = Plasmoid.configuration.groupingLauncherUrlBlacklist;
|
|
}
|
|
}
|
|
|
|
property TaskManagerApplet.Backend backend: TaskManagerApplet.Backend {
|
|
id: backend
|
|
|
|
onAddLauncher: {
|
|
tasks.addLauncher(url);
|
|
}
|
|
}
|
|
|
|
DBus.DBusServiceWatcher {
|
|
id: effectWatcher
|
|
busType: DBus.BusType.Session
|
|
watchedService: "org.kde.KWin.Effect.WindowView1"
|
|
}
|
|
|
|
property Component taskInitComponent: Component {
|
|
Timer {
|
|
id: timer
|
|
|
|
interval: Kirigami.Units.longDuration
|
|
running: true
|
|
|
|
onTriggered: {
|
|
tasksModel.requestPublishDelegateGeometry(parent.modelIndex(), backend.globalRect(parent), parent);
|
|
timer.destroy();
|
|
}
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: Plasmoid
|
|
|
|
function onLocationChanged() {
|
|
if (TaskTools.taskManagerInstanceCount >= 2) {
|
|
return;
|
|
}
|
|
// This is on a timer because the panel may not have
|
|
// settled into position yet when the location prop-
|
|
// erty updates.
|
|
iconGeometryTimer.start();
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: Plasmoid.containment
|
|
|
|
function onScreenGeometryChanged() {
|
|
iconGeometryTimer.start();
|
|
}
|
|
}
|
|
|
|
Mpris.Mpris2Model {
|
|
id: mpris2Source
|
|
}
|
|
|
|
Item {
|
|
anchors.fill: parent
|
|
|
|
TaskManager.VirtualDesktopInfo {
|
|
id: virtualDesktopInfo
|
|
}
|
|
|
|
TaskManager.ActivityInfo {
|
|
id: activityInfo
|
|
readonly property string nullUuid: "00000000-0000-0000-0000-000000000000"
|
|
}
|
|
|
|
Loader {
|
|
id: pulseAudio
|
|
active: pulseAudioUsers > 0
|
|
source: "PulseAudio.qml"
|
|
}
|
|
|
|
Timer {
|
|
id: iconGeometryTimer
|
|
|
|
interval: 500
|
|
repeat: false
|
|
|
|
onTriggered: {
|
|
tasks.publishIconGeometries(taskList.visibleChildren, tasks);
|
|
}
|
|
}
|
|
|
|
Binding {
|
|
target: Plasmoid
|
|
property: "status"
|
|
value: (tasksModel.anyTaskDemandsAttention && Plasmoid.configuration.unhideOnAttention
|
|
? PlasmaCore.Types.NeedsAttentionStatus : PlasmaCore.Types.PassiveStatus)
|
|
restoreMode: Binding.RestoreBinding
|
|
}
|
|
|
|
Connections {
|
|
target: Plasmoid.configuration
|
|
|
|
function onLaunchersChanged() {
|
|
tasksModel.launcherList = Plasmoid.configuration.launchers
|
|
}
|
|
function onGroupingAppIdBlacklistChanged() {
|
|
tasksModel.groupingAppIdBlacklist = Plasmoid.configuration.groupingAppIdBlacklist;
|
|
}
|
|
function onGroupingLauncherUrlBlacklistChanged() {
|
|
tasksModel.groupingLauncherUrlBlacklist = Plasmoid.configuration.groupingLauncherUrlBlacklist;
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: busyIndicator
|
|
PlasmaComponents3.BusyIndicator { visible: false}
|
|
}
|
|
|
|
// Save drag data
|
|
Item {
|
|
id: dragHelper
|
|
|
|
Drag.dragType: Drag.Automatic
|
|
Drag.supportedActions: Qt.CopyAction | Qt.MoveAction | Qt.LinkAction
|
|
Drag.onDragFinished: tasks.dragSource = null;
|
|
}
|
|
|
|
KSvg.Svg {
|
|
id: badgeRing
|
|
imagePath: Qt.resolvedUrl("svgs/badge.svgz")
|
|
}
|
|
KSvg.FrameSvgItem {
|
|
id: taskFrame
|
|
|
|
visible: false;
|
|
|
|
imagePath: "widgets/tasks";
|
|
prefix: TaskTools.taskPrefix("normal", Plasmoid.location)
|
|
}
|
|
|
|
MouseHandler {
|
|
id: mouseHandler
|
|
|
|
anchors.fill: parent
|
|
target: taskList
|
|
onUrlsDropped: (urls) => {
|
|
// If all dropped URLs point to application desktop files, we'll add a launcher for each of them.
|
|
var createLaunchers = urls.every(function (item) {
|
|
return backend.isApplication(item)
|
|
});
|
|
|
|
if (createLaunchers) {
|
|
urls.forEach(function (item) {
|
|
addLauncher(item);
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!hoveredItem) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
TaskList {
|
|
id: taskList
|
|
|
|
anchors.fill: parent
|
|
anchors.leftMargin: 1
|
|
|
|
orientation: {
|
|
if(tasks.vertical) {
|
|
return ListView.Vertical
|
|
}
|
|
return ListView.Horizontal
|
|
}
|
|
|
|
// Is this really needed?
|
|
// It apparently is, this somehow resets MouseArea and makes stuff actually work
|
|
function forceMouseEvent() {
|
|
for(var child in taskList.contentItem.children) {
|
|
var t = taskList.contentItem.children[child];
|
|
if(typeof t !== "undefined") {
|
|
if(t.isLauncher) {
|
|
t.visible = false;
|
|
t.visible = true;
|
|
}
|
|
}
|
|
}
|
|
onAnimatingChanged: {
|
|
if (!animating) {
|
|
tasks.publishIconGeometries(visibleChildren, tasks);
|
|
}
|
|
}
|
|
}
|
|
|
|
delegate: Task {
|
|
tasksRoot: tasks
|
|
}
|
|
model: tasksModel
|
|
}
|
|
}
|
|
|
|
readonly property bool supportsLaunchers: true
|
|
|
|
function hasLauncher(url) {
|
|
return tasksModel.launcherPosition(url) != -1;
|
|
}
|
|
|
|
function addLauncher(url) {
|
|
if (Plasmoid.immutability !== PlasmaCore.Types.SystemImmutable) {
|
|
tasksModel.requestAddLauncher(url);
|
|
}
|
|
}
|
|
|
|
function removeLauncher(url) {
|
|
if (Plasmoid.immutability !== PlasmaCore.Types.SystemImmutable) {
|
|
tasksModel.requestRemoveLauncher(url);
|
|
}
|
|
}
|
|
|
|
// This is called by plasmashell in response to a Meta+number shortcut.
|
|
function activateTaskAtIndex(index) {
|
|
if (typeof index !== "number") {
|
|
return;
|
|
}
|
|
|
|
var task = taskList.itemAtIndex(index);
|
|
if (task) {
|
|
task.leftTapHandler.leftClick();
|
|
}
|
|
}
|
|
|
|
function createJumpList(rootTask, modelIndex, args = {}) {
|
|
const initialArgs = Object.assign(args, {
|
|
visualParent: rootTask,
|
|
modelIndex: modelIndex,
|
|
mpris2Source: mpris2Source,
|
|
backend: backend,
|
|
taskWidth: rootTask.width,
|
|
taskHeight: rootTask.height,
|
|
taskX: rootTask.x,
|
|
taskY: rootTask.y
|
|
});
|
|
return jumpListComponent.createObject(rootTask, initialArgs);
|
|
}
|
|
|
|
function createContextMenu(rootTask, modelIndex, args = {}) {
|
|
const initialArgs = Object.assign(args, {
|
|
visualParent: rootTask,
|
|
modelIndex,
|
|
mpris2Source,
|
|
backend,
|
|
});
|
|
return contextMenuComponent.createObject(rootTask, initialArgs);
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
TaskTools.taskManagerInstanceCount += 1;
|
|
tasks.requestLayout.connect(iconGeometryTimer.restart);
|
|
//tasks.windowsHovered.connect(backend.windowsHovered);
|
|
//tasks.activateWindowView.connect(backend.activateWindowView);
|
|
}
|
|
|
|
Component.onDestruction: {
|
|
TaskTools.taskManagerInstanceCount -= 1;
|
|
}
|
|
}
|