Warning, /plasma/plasma-desktop/applets/taskmanager/package/contents/ui/main.qml is written in an unsupported language. File is not indexed.
0001 /* 0002 SPDX-FileCopyrightText: 2012-2016 Eike Hein <hein@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 import QtQuick 0008 import QtQuick.Layouts 1.15 0009 import QtQml 2.15 0010 0011 import org.kde.plasma.plasmoid 2.0 0012 import org.kde.plasma.components 3.0 as PlasmaComponents3 0013 import org.kde.plasma.core as PlasmaCore 0014 import org.kde.ksvg 1.0 as KSvg 0015 import org.kde.plasma.private.mpris as Mpris 0016 import org.kde.kirigami 2.20 as Kirigami 0017 0018 import org.kde.plasma.workspace.trianglemousefilter 1.0 0019 0020 import org.kde.taskmanager 0.1 as TaskManager 0021 import org.kde.plasma.private.taskmanager 0.1 as TaskManagerApplet 0022 0023 import "code/layout.js" as LayoutManager 0024 import "code/tools.js" as TaskTools 0025 0026 PlasmoidItem { 0027 id: tasks 0028 0029 // For making a bottom to top layout since qml flow can't do that. 0030 // We just hang the task manager upside down to achieve that. 0031 // This mirrors the tasks as well, so we just rotate them again to fix that (see Task.qml). 0032 rotation: Plasmoid.configuration.reverseMode && Plasmoid.formFactor === PlasmaCore.Types.Vertical ? 180 : 0 0033 0034 readonly property bool shouldShirnkToZero: !LayoutManager.logicalTaskCount() 0035 property bool vertical: Plasmoid.formFactor === PlasmaCore.Types.Vertical 0036 property bool iconsOnly: Plasmoid.pluginName === "org.kde.plasma.icontasks" 0037 0038 property var toolTipOpenedByClick: null 0039 0040 property QtObject contextMenuComponent: Qt.createComponent("ContextMenu.qml") 0041 property QtObject pulseAudioComponent: Qt.createComponent("PulseAudio.qml") 0042 0043 property var toolTipAreaItem: null 0044 0045 property bool needLayoutRefresh: false; 0046 property variant taskClosedWithMouseMiddleButton: [] 0047 property alias taskList: taskList 0048 0049 preferredRepresentation: fullRepresentation 0050 0051 Plasmoid.constraintHints: Plasmoid.CanFillArea 0052 0053 Plasmoid.onUserConfiguringChanged: { 0054 if (Plasmoid.userConfiguring && !!tasks.groupDialog) { 0055 tasks.groupDialog.visible = false; 0056 } 0057 } 0058 0059 Layout.fillWidth: tasks.vertical ? true : Plasmoid.configuration.fill 0060 Layout.fillHeight: !tasks.vertical ? true : Plasmoid.configuration.fill 0061 Layout.minimumWidth: { 0062 if (shouldShirnkToZero) { 0063 return Kirigami.Units.gridUnit; // For edit mode 0064 } 0065 return tasks.vertical ? 0 : LayoutManager.preferredMinWidth(); 0066 } 0067 Layout.minimumHeight: { 0068 if (shouldShirnkToZero) { 0069 return Kirigami.Units.gridUnit; // For edit mode 0070 } 0071 return !tasks.vertical ? 0 : LayoutManager.preferredMinHeight(); 0072 } 0073 0074 //BEGIN TODO: this is not precise enough: launchers are smaller than full tasks 0075 Layout.preferredWidth: { 0076 if (shouldShirnkToZero) { 0077 return 0.01; 0078 } 0079 if (tasks.vertical) { 0080 return Kirigami.Units.gridUnit * 10; 0081 } 0082 return (LayoutManager.logicalTaskCount() * LayoutManager.preferredMaxWidth()) / LayoutManager.calculateStripes(); 0083 } 0084 Layout.preferredHeight: { 0085 if (shouldShirnkToZero) { 0086 return 0.01; 0087 } 0088 if (tasks.vertical) { 0089 return (LayoutManager.logicalTaskCount() * LayoutManager.preferredMaxHeight()) / LayoutManager.calculateStripes(); 0090 } 0091 return Kirigami.Units.gridUnit * 2; 0092 } 0093 //END TODO 0094 0095 property Item dragSource: null 0096 0097 signal requestLayout 0098 signal windowsHovered(variant winIds, bool hovered) 0099 signal activateWindowView(variant winIds) 0100 0101 onDragSourceChanged: { 0102 if (dragSource == null) { 0103 tasksModel.syncLaunchers(); 0104 } 0105 } 0106 0107 function publishIconGeometries(taskItems) { 0108 if (TaskTools.taskManagerInstanceCount >= 2) { 0109 return; 0110 } 0111 for (var i = 0; i < taskItems.length - 1; ++i) { 0112 var task = taskItems[i]; 0113 0114 if (!task.model.IsLauncher && !task.model.IsStartup) { 0115 tasks.tasksModel.requestPublishDelegateGeometry(tasks.tasksModel.makeModelIndex(task.index), 0116 backend.globalRect(task), task); 0117 } 0118 } 0119 } 0120 0121 property TaskManager.TasksModel tasksModel: TaskManager.TasksModel { 0122 id: tasksModel 0123 0124 readonly property int logicalLauncherCount: { 0125 if (Plasmoid.configuration.separateLaunchers) { 0126 return launcherCount; 0127 } 0128 0129 var startupsWithLaunchers = 0; 0130 0131 for (var i = 0; i < taskRepeater.count; ++i) { 0132 var item = taskRepeater.itemAt(i); 0133 0134 // During destruction required properties such as item.model can go null for a while, 0135 // so in paths that can trigger on those moments, they need to be guarded 0136 if (item?.model?.IsStartup && item.model.HasLauncher) { 0137 ++startupsWithLaunchers; 0138 } 0139 } 0140 0141 return launcherCount + startupsWithLaunchers; 0142 } 0143 0144 virtualDesktop: virtualDesktopInfo.currentDesktop 0145 screenGeometry: Plasmoid.containment.screenGeometry 0146 activity: activityInfo.currentActivity 0147 0148 filterByVirtualDesktop: Plasmoid.configuration.showOnlyCurrentDesktop 0149 filterByScreen: Plasmoid.configuration.showOnlyCurrentScreen 0150 filterByActivity: Plasmoid.configuration.showOnlyCurrentActivity 0151 filterNotMinimized: Plasmoid.configuration.showOnlyMinimized 0152 0153 hideActivatedLaunchers: tasks.iconsOnly || !Plasmoid.configuration.separateLaunchers 0154 sortMode: sortModeEnumValue(Plasmoid.configuration.sortingStrategy) 0155 launchInPlace: tasks.iconsOnly && Plasmoid.configuration.sortingStrategy === 1 0156 separateLaunchers: { 0157 if (!tasks.iconsOnly && !Plasmoid.configuration.separateLaunchers 0158 && Plasmoid.configuration.sortingStrategy === 1) { 0159 return false; 0160 } 0161 0162 return true; 0163 } 0164 0165 groupMode: groupModeEnumValue(Plasmoid.configuration.groupingStrategy) 0166 groupInline: !Plasmoid.configuration.groupPopups && !tasks.iconsOnly 0167 groupingWindowTasksThreshold: (Plasmoid.configuration.onlyGroupWhenFull && !tasks.iconsOnly 0168 ? LayoutManager.optimumCapacity(width, height) + 1 : -1) 0169 0170 onLauncherListChanged: { 0171 layoutTimer.restart(); 0172 Plasmoid.configuration.launchers = launcherList; 0173 } 0174 0175 onGroupingAppIdBlacklistChanged: { 0176 Plasmoid.configuration.groupingAppIdBlacklist = groupingAppIdBlacklist; 0177 } 0178 0179 onGroupingLauncherUrlBlacklistChanged: { 0180 Plasmoid.configuration.groupingLauncherUrlBlacklist = groupingLauncherUrlBlacklist; 0181 } 0182 0183 function sortModeEnumValue(index) { 0184 switch (index) { 0185 case 0: 0186 return TaskManager.TasksModel.SortDisabled; 0187 case 1: 0188 return TaskManager.TasksModel.SortManual; 0189 case 2: 0190 return TaskManager.TasksModel.SortAlpha; 0191 case 3: 0192 return TaskManager.TasksModel.SortVirtualDesktop; 0193 case 4: 0194 return TaskManager.TasksModel.SortActivity; 0195 default: 0196 return TaskManager.TasksModel.SortDisabled; 0197 } 0198 } 0199 0200 function groupModeEnumValue(index) { 0201 switch (index) { 0202 case 0: 0203 return TaskManager.TasksModel.GroupDisabled; 0204 case 1: 0205 return TaskManager.TasksModel.GroupApplications; 0206 } 0207 } 0208 0209 Component.onCompleted: { 0210 launcherList = Plasmoid.configuration.launchers; 0211 groupingAppIdBlacklist = Plasmoid.configuration.groupingAppIdBlacklist; 0212 groupingLauncherUrlBlacklist = Plasmoid.configuration.groupingLauncherUrlBlacklist; 0213 0214 // Only hook up view only after the above churn is done. 0215 taskRepeater.model = tasksModel; 0216 } 0217 } 0218 0219 property TaskManagerApplet.Backend backend: TaskManagerApplet.Backend { 0220 id: backend 0221 highlightWindows: Plasmoid.configuration.highlightWindows 0222 0223 onAddLauncher: { 0224 tasks.addLauncher(url); 0225 } 0226 0227 onWindowViewAvailableChanged: TaskTools.windowViewAvailable = windowViewAvailable; 0228 0229 Component.onCompleted: TaskTools.windowViewAvailable = windowViewAvailable; 0230 } 0231 0232 property Component taskInitComponent: Component { 0233 Timer { 0234 id: timer 0235 0236 interval: Kirigami.Units.longDuration 0237 running: true 0238 0239 onTriggered: { 0240 tasksModel.requestPublishDelegateGeometry(parent.modelIndex(), backend.globalRect(parent), parent); 0241 timer.destroy(); 0242 } 0243 } 0244 } 0245 0246 Connections { 0247 target: Plasmoid 0248 0249 function onLocationChanged() { 0250 if (TaskTools.taskManagerInstanceCount >= 2) { 0251 return; 0252 } 0253 // This is on a timer because the panel may not have 0254 // settled into position yet when the location prop- 0255 // erty updates. 0256 iconGeometryTimer.start(); 0257 } 0258 } 0259 0260 Connections { 0261 target: Plasmoid.containment 0262 0263 function onScreenGeometryChanged() { 0264 iconGeometryTimer.start(); 0265 } 0266 } 0267 0268 Mpris.Mpris2Model { 0269 id: mpris2Source 0270 } 0271 0272 MouseArea { 0273 anchors.fill: parent 0274 0275 hoverEnabled: true 0276 onExited: { 0277 if (needLayoutRefresh) { 0278 LayoutManager.layout(taskRepeater) 0279 needLayoutRefresh = false; 0280 } 0281 } 0282 0283 TaskManager.VirtualDesktopInfo { 0284 id: virtualDesktopInfo 0285 } 0286 0287 TaskManager.ActivityInfo { 0288 id: activityInfo 0289 readonly property string nullUuid: "00000000-0000-0000-0000-000000000000" 0290 } 0291 0292 Loader { 0293 id: pulseAudio 0294 sourceComponent: pulseAudioComponent 0295 active: pulseAudioComponent.status === Component.Ready 0296 } 0297 0298 Timer { 0299 id: iconGeometryTimer 0300 0301 interval: 500 0302 repeat: false 0303 0304 onTriggered: { 0305 tasks.publishIconGeometries(taskList.children, tasks); 0306 } 0307 } 0308 0309 Binding { 0310 target: Plasmoid 0311 property: "status" 0312 value: (tasksModel.anyTaskDemandsAttention && Plasmoid.configuration.unhideOnAttention 0313 ? PlasmaCore.Types.NeedsAttentionStatus : PlasmaCore.Types.PassiveStatus) 0314 restoreMode: Binding.RestoreBinding 0315 } 0316 0317 Connections { 0318 target: Plasmoid.configuration 0319 0320 function onLaunchersChanged() { 0321 tasksModel.launcherList = Plasmoid.configuration.launchers 0322 } 0323 function onGroupingAppIdBlacklistChanged() { 0324 tasksModel.groupingAppIdBlacklist = Plasmoid.configuration.groupingAppIdBlacklist; 0325 } 0326 function onGroupingLauncherUrlBlacklistChanged() { 0327 tasksModel.groupingLauncherUrlBlacklist = Plasmoid.configuration.groupingLauncherUrlBlacklist; 0328 } 0329 function onIconSpacingChanged() { 0330 taskList.layout(); 0331 } 0332 } 0333 0334 Component { 0335 id: busyIndicator 0336 PlasmaComponents3.BusyIndicator {} 0337 } 0338 0339 // Save drag data 0340 Item { 0341 id: dragHelper 0342 0343 Drag.dragType: Drag.Automatic 0344 Drag.supportedActions: Qt.CopyAction | Qt.MoveAction | Qt.LinkAction 0345 Drag.onDragFinished: tasks.dragSource = null; 0346 } 0347 0348 KSvg.FrameSvgItem { 0349 id: taskFrame 0350 0351 visible: false; 0352 0353 imagePath: "widgets/tasks"; 0354 prefix: TaskTools.taskPrefix("normal", Plasmoid.location) 0355 } 0356 0357 MouseHandler { 0358 id: mouseHandler 0359 0360 anchors.fill: parent 0361 0362 target: taskList 0363 0364 onUrlsDropped: (urls) => { 0365 // If all dropped URLs point to application desktop files, we'll add a launcher for each of them. 0366 var createLaunchers = urls.every(function (item) { 0367 return backend.isApplication(item) 0368 }); 0369 0370 if (createLaunchers) { 0371 urls.forEach(function (item) { 0372 addLauncher(item); 0373 }); 0374 return; 0375 } 0376 0377 if (!hoveredItem) { 0378 return; 0379 } 0380 0381 // Otherwise we'll just start a new instance of the application with the URLs as argument, 0382 // as you probably don't expect some of your files to open in the app and others to spawn launchers. 0383 tasksModel.requestOpenUrls(hoveredItem.modelIndex(), urls); 0384 } 0385 } 0386 0387 ToolTipDelegate { 0388 id: openWindowToolTipDelegate 0389 visible: false 0390 } 0391 0392 ToolTipDelegate { 0393 id: pinnedAppToolTipDelegate 0394 visible: false 0395 } 0396 0397 TriangleMouseFilter { 0398 id: tmf 0399 filterTimeOut: 300 0400 active: tasks.toolTipAreaItem && tasks.toolTipAreaItem.toolTipOpen 0401 blockFirstEnter: false 0402 0403 edge: { 0404 switch (Plasmoid.location) { 0405 case PlasmaCore.Types.BottomEdge: 0406 return Qt.TopEdge; 0407 case PlasmaCore.Types.TopEdge: 0408 return Qt.BottomEdge; 0409 case PlasmaCore.Types.LeftEdge: 0410 return Qt.RightEdge; 0411 case PlasmaCore.Types.RightEdge: 0412 return Qt.LeftEdge; 0413 default: 0414 return Qt.TopEdge; 0415 } 0416 } 0417 0418 secondaryPoint: { 0419 if (tasks.toolTipAreaItem === null) { 0420 return Qt.point(0, 0); 0421 } 0422 const x = tasks.toolTipAreaItem.x; 0423 const y = tasks.toolTipAreaItem.y; 0424 const height = tasks.toolTipAreaItem.height; 0425 const width = tasks.toolTipAreaItem.width; 0426 return Qt.point(x+width/2, height); 0427 } 0428 0429 anchors { 0430 left: parent.left 0431 top: parent.top 0432 } 0433 0434 height: taskList.implicitHeight 0435 width: taskList.implicitWidth 0436 0437 TaskList { 0438 id: taskList 0439 0440 anchors { 0441 left: parent.left 0442 top: parent.top 0443 } 0444 width: tasks.shouldShirnkToZero ? 0 : LayoutManager.layoutWidth() 0445 height: tasks.shouldShirnkToZero ? 0 : LayoutManager.layoutHeight() 0446 0447 flow: { 0448 if (tasks.vertical) { 0449 return Plasmoid.configuration.forceStripes ? Grid.LeftToRight : Grid.TopToBottom 0450 } 0451 return Plasmoid.configuration.forceStripes ? Grid.TopToBottom : Grid.LeftToRight 0452 } 0453 0454 onAnimatingChanged: { 0455 if (!animating) { 0456 tasks.publishIconGeometries(children, tasks); 0457 } 0458 } 0459 onWidthChanged: layoutTimer.restart() 0460 onHeightChanged: layoutTimer.restart() 0461 0462 function layout() { 0463 LayoutManager.layout(taskRepeater); 0464 } 0465 0466 Timer { 0467 id: layoutTimer 0468 0469 interval: 0 0470 repeat: false 0471 0472 onTriggered: taskList.layout() 0473 } 0474 0475 Repeater { 0476 id: taskRepeater 0477 0478 delegate: Task {} 0479 onItemAdded: taskList.layout() 0480 onItemRemoved: { 0481 if (tasks.containsMouse && index != taskRepeater.count && 0482 item.model.WinIdList.length > 0 && 0483 taskClosedWithMouseMiddleButton.indexOf(item.winIdList[0]) > -1) { 0484 needLayoutRefresh = true; 0485 } else { 0486 taskList.layout(); 0487 } 0488 taskClosedWithMouseMiddleButton = []; 0489 } 0490 } 0491 } 0492 } 0493 } 0494 0495 readonly property Component groupDialogComponent: Qt.createComponent("GroupDialog.qml") 0496 property GroupDialog groupDialog: null 0497 0498 readonly property bool supportsLaunchers: true 0499 0500 function hasLauncher(url) { 0501 return tasksModel.launcherPosition(url) != -1; 0502 } 0503 0504 function addLauncher(url) { 0505 if (Plasmoid.immutability !== PlasmaCore.Types.SystemImmutable) { 0506 tasksModel.requestAddLauncher(url); 0507 } 0508 } 0509 0510 function removeLauncher(url) { 0511 if (Plasmoid.immutability !== PlasmaCore.Types.SystemImmutable) { 0512 tasksModel.requestRemoveLauncher(url); 0513 } 0514 } 0515 0516 // This is called by plasmashell in response to a Meta+number shortcut. 0517 function activateTaskAtIndex(index) { 0518 if (typeof index !== "number") { 0519 return; 0520 } 0521 0522 var task = taskRepeater.itemAt(index); 0523 if (task) { 0524 TaskTools.activateTask(task.modelIndex(), task.model, null, task, Plasmoid, tasks); 0525 } 0526 } 0527 0528 function createContextMenu(rootTask, modelIndex, args = {}) { 0529 const initialArgs = Object.assign(args, { 0530 visualParent: rootTask, 0531 modelIndex, 0532 mpris2Source, 0533 backend, 0534 }); 0535 return contextMenuComponent.createObject(rootTask, initialArgs); 0536 } 0537 0538 Component.onCompleted: { 0539 TaskTools.taskManagerInstanceCount += 1; 0540 tasks.requestLayout.connect(layoutTimer.restart); 0541 tasks.requestLayout.connect(iconGeometryTimer.restart); 0542 tasks.windowsHovered.connect(backend.windowsHovered); 0543 tasks.activateWindowView.connect(backend.activateWindowView); 0544 } 0545 0546 Component.onDestruction: { 0547 TaskTools.taskManagerInstanceCount -= 1; 0548 } 0549 }