/*SPDX-FileCopyrightText: 2012-2016 Eike Hein <hein@kde.org>SPDX-License-Identifier: GPL-2.0-or-later*/import QtQuickimport QtQuick.Layouts 1.15import QtQml 2.15import org.kde.plasma.plasmoid 2.0import org.kde.plasma.components 3.0 as PlasmaComponents3import org.kde.plasma.core as PlasmaCoreimport org.kde.ksvg 1.0 as KSvgimport org.kde.plasma.private.mpris as Mprisimport org.kde.kirigami 2.20 as Kirigamiimport org.kde.plasma.workspace.trianglemousefilter 1.0import org.kde.taskmanager 0.1 as TaskManagerimport org.kde.plasma.private.taskmanager 0.1 as TaskManagerAppletimport "code/layout.js" as LayoutManagerimport "code/tools.js" as TaskToolsPlasmoidItem {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 : 0readonly property bool shouldShirnkToZero: !LayoutManager.logicalTaskCount()property bool vertical: Plasmoid.formFactor === PlasmaCore.Types.Verticalproperty bool iconsOnly: Plasmoid.pluginName === "org.kde.plasma.icontasks"property var toolTipOpenedByClick: nullproperty QtObject contextMenuComponent: Qt.createComponent("ContextMenu.qml")property QtObject pulseAudioComponent: Qt.createComponent("PulseAudio.qml")property var toolTipAreaItem: nullproperty bool needLayoutRefresh: false;property variant taskClosedWithMouseMiddleButton: []property alias taskList: taskListpreferredRepresentation: fullRepresentationPlasmoid.constraintHints: Plasmoid.CanFillAreaPlasmoid.onUserConfiguringChanged: {if (Plasmoid.userConfiguring && !!tasks.groupDialog) {tasks.groupDialog.visible = false;}}Layout.fillWidth: tasks.vertical ? true : Plasmoid.configuration.fillLayout.fillHeight: !tasks.vertical ? true : Plasmoid.configuration.fillLayout.minimumWidth: {if (shouldShirnkToZero) {return Kirigami.Units.gridUnit; // For edit mode}return tasks.vertical ? 0 : LayoutManager.preferredMinWidth();}Layout.minimumHeight: {if (shouldShirnkToZero) {return Kirigami.Units.gridUnit; // For edit mode}return !tasks.vertical ? 0 : LayoutManager.preferredMinHeight();}//BEGIN TODO: this is not precise enough: launchers are smaller than full tasksLayout.preferredWidth: {if (shouldShirnkToZero) {return 0.01;}if (tasks.vertical) {return Kirigami.Units.gridUnit * 10;}return (LayoutManager.logicalTaskCount() * LayoutManager.preferredMaxWidth()) / LayoutManager.calculateStripes();}Layout.preferredHeight: {if (shouldShirnkToZero) {return 0.01;}if (tasks.vertical) {return (LayoutManager.logicalTaskCount() * LayoutManager.preferredMaxHeight()) / LayoutManager.calculateStripes();}return Kirigami.Units.gridUnit * 2;}//END TODOproperty Item dragSource: nullsignal requestLayoutsignal windowsHovered(variant winIds, bool hovered)signal activateWindowView(variant winIds)onDragSourceChanged: {if (dragSource == null) {tasksModel.syncLaunchers();}}function publishIconGeometries(taskItems) {if (TaskTools.taskManagerInstanceCount >= 2) {return;}for (var i = 0; i < taskItems.length - 1; ++i) {var task = taskItems[i];if (!task.model.IsLauncher && !task.model.IsStartup) {tasks.tasksModel.requestPublishDelegateGeometry(tasks.tasksModel.makeModelIndex(task.index),backend.globalRect(task), task);}}}property TaskManager.TasksModel tasksModel: TaskManager.TasksModel {id: tasksModelreadonly property int logicalLauncherCount: {if (Plasmoid.configuration.separateLaunchers) {return launcherCount;}var startupsWithLaunchers = 0;for (var i = 0; i < taskRepeater.count; ++i) {var item = taskRepeater.itemAt(i);// During destruction required properties such as item.model can go null for a while,// so in paths that can trigger on those moments, they need to be guardedif (item?.model?.IsStartup && item.model.HasLauncher) {++startupsWithLaunchers;}}return launcherCount + startupsWithLaunchers;}virtualDesktop: virtualDesktopInfo.currentDesktopscreenGeometry: Plasmoid.containment.screenGeometryactivity: activityInfo.currentActivityfilterByVirtualDesktop: Plasmoid.configuration.showOnlyCurrentDesktopfilterByScreen: Plasmoid.configuration.showOnlyCurrentScreenfilterByActivity: Plasmoid.configuration.showOnlyCurrentActivityfilterNotMinimized: Plasmoid.configuration.showOnlyMinimizedhideActivatedLaunchers: tasks.iconsOnly || !Plasmoid.configuration.separateLauncherssortMode: sortModeEnumValue(Plasmoid.configuration.sortingStrategy)launchInPlace: tasks.iconsOnly && Plasmoid.configuration.sortingStrategy === 1separateLaunchers: {if (!tasks.iconsOnly && !Plasmoid.configuration.separateLaunchers&& Plasmoid.configuration.sortingStrategy === 1) {return false;}return true;}groupMode: groupModeEnumValue(Plasmoid.configuration.groupingStrategy)groupInline: !Plasmoid.configuration.groupPopups && !tasks.iconsOnlygroupingWindowTasksThreshold: (Plasmoid.configuration.onlyGroupWhenFull && !tasks.iconsOnly? LayoutManager.optimumCapacity(width, height) + 1 : -1)onLauncherListChanged: {layoutTimer.restart();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;// Only hook up view only after the above churn is done.taskRepeater.model = tasksModel;}}property TaskManagerApplet.Backend backend: TaskManagerApplet.Backend {id: backendhighlightWindows: Plasmoid.configuration.highlightWindowsonAddLauncher: {tasks.addLauncher(url);}onWindowViewAvailableChanged: TaskTools.windowViewAvailable = windowViewAvailable;Component.onCompleted: TaskTools.windowViewAvailable = windowViewAvailable;}property Component taskInitComponent: Component {Timer {id: timerinterval: Kirigami.Units.longDurationrunning: trueonTriggered: {tasksModel.requestPublishDelegateGeometry(parent.modelIndex(), backend.globalRect(parent), parent);timer.destroy();}}}Connections {target: Plasmoidfunction 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.containmentfunction onScreenGeometryChanged() {iconGeometryTimer.start();}}Mpris.Mpris2Model {id: mpris2Source}MouseArea {anchors.fill: parenthoverEnabled: trueonExited: {if (needLayoutRefresh) {LayoutManager.layout(taskRepeater)needLayoutRefresh = false;}}TaskManager.VirtualDesktopInfo {id: virtualDesktopInfo}TaskManager.ActivityInfo {id: activityInforeadonly property string nullUuid: "00000000-0000-0000-0000-000000000000"}Loader {id: pulseAudiosourceComponent: pulseAudioComponentactive: pulseAudioComponent.status === Component.Ready}Timer {id: iconGeometryTimerinterval: 500repeat: falseonTriggered: {tasks.publishIconGeometries(taskList.children, tasks);}}Binding {target: Plasmoidproperty: "status"value: (tasksModel.anyTaskDemandsAttention && Plasmoid.configuration.unhideOnAttention? PlasmaCore.Types.NeedsAttentionStatus : PlasmaCore.Types.PassiveStatus)restoreMode: Binding.RestoreBinding}Connections {target: Plasmoid.configurationfunction onLaunchersChanged() {tasksModel.launcherList = Plasmoid.configuration.launchers}function onGroupingAppIdBlacklistChanged() {tasksModel.groupingAppIdBlacklist = Plasmoid.configuration.groupingAppIdBlacklist;}function onGroupingLauncherUrlBlacklistChanged() {tasksModel.groupingLauncherUrlBlacklist = Plasmoid.configuration.groupingLauncherUrlBlacklist;}function onIconSpacingChanged() {taskList.layout();}}Component {id: busyIndicatorPlasmaComponents3.BusyIndicator {}}// Save drag dataItem {id: dragHelperDrag.dragType: Drag.AutomaticDrag.supportedActions: Qt.CopyAction | Qt.MoveAction | Qt.LinkActionDrag.onDragFinished: tasks.dragSource = null;}KSvg.FrameSvgItem {id: taskFramevisible: false;imagePath: "widgets/tasks";prefix: TaskTools.taskPrefix("normal", Plasmoid.location)}MouseHandler {id: mouseHandleranchors.fill: parenttarget: taskListonUrlsDropped: (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;}// Otherwise we'll just start a new instance of the application with the URLs as argument,// as you probably don't expect some of your files to open in the app and others to spawn launchers.tasksModel.requestOpenUrls(hoveredItem.modelIndex(), urls);}}ToolTipDelegate {id: openWindowToolTipDelegatevisible: false}ToolTipDelegate {id: pinnedAppToolTipDelegatevisible: false}TriangleMouseFilter {id: tmffilterTimeOut: 300active: tasks.toolTipAreaItem && tasks.toolTipAreaItem.toolTipOpenblockFirstEnter: falseedge: {switch (Plasmoid.location) {case PlasmaCore.Types.BottomEdge:return Qt.TopEdge;case PlasmaCore.Types.TopEdge:return Qt.BottomEdge;case PlasmaCore.Types.LeftEdge:return Qt.RightEdge;case PlasmaCore.Types.RightEdge:return Qt.LeftEdge;default:return Qt.TopEdge;}}secondaryPoint: {if (tasks.toolTipAreaItem === null) {return Qt.point(0, 0);}const x = tasks.toolTipAreaItem.x;const y = tasks.toolTipAreaItem.y;const height = tasks.toolTipAreaItem.height;const width = tasks.toolTipAreaItem.width;return Qt.point(x+width/2, height);}anchors {left: parent.lefttop: parent.top}height: taskList.implicitHeightwidth: taskList.implicitWidthTaskList {id: taskListanchors {left: parent.lefttop: parent.top}width: tasks.shouldShirnkToZero ? 0 : LayoutManager.layoutWidth()height: tasks.shouldShirnkToZero ? 0 : LayoutManager.layoutHeight()flow: {if (tasks.vertical) {return Plasmoid.configuration.forceStripes ? Grid.LeftToRight : Grid.TopToBottom}return Plasmoid.configuration.forceStripes ? Grid.TopToBottom : Grid.LeftToRight}onAnimatingChanged: {if (!animating) {tasks.publishIconGeometries(children, tasks);}}onWidthChanged: layoutTimer.restart()onHeightChanged: layoutTimer.restart()function layout() {LayoutManager.layout(taskRepeater);}Timer {id: layoutTimerinterval: 0repeat: falseonTriggered: taskList.layout()}Repeater {id: taskRepeaterdelegate: Task {}onItemAdded: taskList.layout()onItemRemoved: {if (tasks.containsMouse && index != taskRepeater.count &&item.model.WinIdList.length > 0 &&taskClosedWithMouseMiddleButton.indexOf(item.winIdList[0]) > -1) {needLayoutRefresh = true;} else {taskList.layout();}taskClosedWithMouseMiddleButton = [];}}}}}readonly property Component groupDialogComponent: Qt.createComponent("GroupDialog.qml")property GroupDialog groupDialog: nullreadonly property bool supportsLaunchers: truefunction 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 = taskRepeater.itemAt(index);if (task) {TaskTools.activateTask(task.modelIndex(), task.model, null, task, Plasmoid, tasks);}}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(layoutTimer.restart);tasks.requestLayout.connect(iconGeometryTimer.restart);tasks.windowsHovered.connect(backend.windowsHovered);tasks.activateWindowView.connect(backend.activateWindowView);}Component.onDestruction: {TaskTools.taskManagerInstanceCount -= 1;}}