1/* 2 SPDX-FileCopyrightText: 2016 Smith AR <audoban@openmailbox.org> 3 SPDX-FileCopyrightText: 2016 Michail Vourlakos <mvourlakos@gmail.com> 4 SPDX-License-Identifier: GPL-2.0-or-later 5*/ 6 7import QtQuick 2.0 8import QtQuick.Layouts 1.1 9import QtGraphicalEffects 1.0 10 11import org.kde.plasma.core 2.0 as PlasmaCore 12import org.kde.plasma.components 2.0 as PlasmaComponents 13import org.kde.plasma.plasmoid 2.0 14 15import org.kde.plasma.private.taskmanager 0.1 as TaskManagerApplet 16 17import org.kde.latte.core 0.2 as LatteCore 18import org.kde.latte.private.tasks 0.1 as LatteTasks 19 20import org.kde.latte.abilities.items 0.1 as AbilityItem 21 22import "animations" as TaskAnimations 23 24AbilityItem.BasicItem { 25 id: taskItem 26 visible: false 27 objectName: "TaskItem" 28 29 isHidden: !visible || isForcedHidden 30 31 isHiddenSpacerForcedShow: taskItem.inAttentionBuiltinAnimation || taskItem.inFastRestoreAnimation 32 isHiddenSpacerAnimated: taskItem.inFastRestoreAnimation 33 || showWindowAnimation.running 34 || root.inActivityChange 35 || taskItem.inRemoveStage 36 || (taskItem.containsMouse && inAttentionBuiltinAnimation && taskItem.parabolicItem.zoom!==taskItem.abilities.parabolic.factor.zoom) 37 38 isMonochromaticForcedContentItem: plasmoid.configuration.forceMonochromaticIcons 39 monochromizedItem: taskIcon.monochromizedItem 40 41 isSeparatorHidden: isSeparator && (lastValidIndex > taskItem.abilities.indexer.lastVisibleItemIndex) 42 isSeparatorInRealLength: isSeparator && root.dragSource 43 44 containsMouse: taskMouseArea.containsMouse || parabolicAreaContainsMouse 45 thinTooltipText: { 46 if (root.showPreviews && !isLauncher) { 47 return ""; 48 } 49 50 return isWindow ? model.display : model.AppName; 51 } 52 53 preserveIndicatorInInitialPosition: inBouncingAnimation || inAttentionBuiltinAnimation || inNewWindowBuiltinAnimation 54 55 parabolicItem.isParabolicEventBlocked: root.dragSource 56 || !hoverEnabled 57 || !taskItem.abilities.myView.isShownFully 58 || inAnimation 59 || (inBlockingAnimation && !(inAttentionBuiltinAnimation || inFastRestoreAnimation)) 60 parabolicItem.isUpdatingOnlySpacers: inAttentionBuiltinAnimation || inBouncingAnimation 61 62 property alias hoverEnabled: taskMouseArea.hoverEnabled 63 property alias pressed: taskMouseArea.pressed 64 65 property bool delayingRemove: ListView.delayRemove 66 //states that exist in windows in a Group of windows 67 property bool hasActive: isActive 68 property bool hasMinimized: (IsGroupParent === true) ? subWindows.hasMinimized : isMinimized 69 property bool hasShown: (IsGroupParent === true) ? subWindows.hasShown : !isMinimized && isWindow 70 property bool inAttention: isDemandingAttention && plasmoid.status === PlasmaCore.Types.NeedsAttentionStatus ? true : false 71 72 /*animations flags*/ 73 property bool inAnimation: true 74 property bool inAddRemoveAnimation: true 75 property bool inAttentionBuiltinAnimation: false 76 property bool inBlockingAnimation: false 77 property bool inBouncingAnimation: false 78 property bool inFastRestoreAnimation: false 79 property bool inNewWindowBuiltinAnimation: false 80 property bool inPopup: false 81 property bool inRemoveStage: false 82 83 property bool isLauncherBuiltinAnimationRunning: false 84 property bool isLauncherAnimationRunning: isLauncherBuiltinAnimationRunning 85 || (taskItem.abilities.indicators.info.providesTaskLauncherAnimation && isIndicatorTaskLauncherAnimationRunning) 86 87 //! after clicking to show/hide preview enter events are trigerred even though the should not 88 property bool showPreviewsIsBlockedFromReleaseEvent: false 89 90 property bool isAbleToShowPreview: true 91 property bool isActive: (IsActive === true) ? true : false 92 property bool isDemandingAttention: (IsDemandingAttention === true) ? true : false 93 property bool isDragged: false 94 property bool isGroupable: (IsGroupable === true) ? true : false 95 property bool isGroupParent: (IsGroupParent === true) ? true : false 96 property bool isForcedHidden: false 97 property bool isLauncher: (IsLauncher === true) ? true : false 98 property bool hasShownLauncher: (taskItem.abilities.launchers.inCurrentActivity(taskItem.launcherUrl) 99 || taskItem.abilities.launchers.inCurrentActivity(taskItem.launcherUrlWithIcon)) 100 && !root.inActivityChange /*update trigger when changing current activity*/ 101 property bool isMinimized: (IsMinimized === true) ? true : false 102 property bool isStartup: (IsStartup === true) ? true : false 103 property bool isWindow: (IsWindow === true) ? true : false 104 105 property bool canPublishGeometries: (isWindow || isStartup || isGroupParent) && visible && width>=taskItem.abilities.metrics.iconSize && height>=taskItem.abilities.metrics.iconSize 106 && !taskItem.delayingRemove 107 && (taskItem.parabolicItem.zoom===1 || taskItem.parabolicItem.zoom===taskItem.abilities.parabolic.factor.zoom) //don't publish during zoom animation 108 109 property bool hoveredFromDragging: (mouseHandler.hoveredItem === taskItem) || (mouseHandler.ignoredItem === taskItem) 110 111 property bool wheelIsBlocked: false 112 property bool hasAddedWaitingLauncher: false 113 114 property int badgeIndicator: 0 //it is used from external apps 115 property int lastValidIndex: -1 //used for the removal animation 116 property int lastButtonClicked: -1; 117 property int pressX: -1 118 property int pressY: -1 119 property int resistanceDelay: 450 120 property int windowsCount: subWindows.windowsCount 121 property int windowsMinimizedCount: subWindows.windowsMinimized 122 123 property string activity: tasksModel.activity 124 125 readonly property var m: model 126 readonly property int pid: model && model.AppPid ? model.AppPid : -1 127 readonly property string appName: model && model.AppName ? model.AppName : "" 128 129 property string modelLauncherUrl: (LauncherUrlWithoutIcon && LauncherUrlWithoutIcon !== null) ? LauncherUrlWithoutIcon : "" 130 property string modelLauncherUrlWithIcon: (LauncherUrl && LauncherUrl !== null) ? LauncherUrl : "" 131 property string launcherUrl: "" 132 property string launcherUrlWithIcon: "" 133 property string launcherName: "" 134 135 readonly property alias hoveredTimer: taskMouseArea.hoveredTimer 136 readonly property alias mouseArea: taskMouseArea 137 readonly property alias subWindows: subWindows 138 139 readonly property alias showWindowAnimation: _showWindowAnimation 140 141 //! Indicator Properties 142 indicator.isTask: true 143 indicator.isLauncher: taskItem.isLauncher || root.disableAllWindowsFunctionality 144 indicator.isStartup: !root.disableAllWindowsFunctionality && taskItem.isStartup 145 indicator.isWindow: !root.disableAllWindowsFunctionality && taskItem.isWindow 146 147 indicator.isActive: !root.disableAllWindowsFunctionality && (taskItem.hasActive 148 || (root.showPreviews 149 && (taskItem.isWindow || taskItem.isGroupParent) 150 && windowsPreviewDlg.activeItem 151 && (windowsPreviewDlg.activeItem === taskItem)) ) 152 153 indicator.isGroup: !root.disableAllWindowsFunctionality && taskItem.isGroupParent 154 indicator.isHovered: taskItem.containsMouse || (windowsPreviewDlg.containsMouse && (toolTipDelegate.parentTask === taskItem)) 155 indicator.isMinimized: !root.disableAllWindowsFunctionality && taskItem.isMinimized 156 indicator.isPressed: taskItem.pressed 157 indicator.inAttention: !root.disableAllWindowsFunctionality && taskItem.inAttention 158 indicator.inRemoving: taskItem.inRemoveStage 159 160 indicator.isSquare: true 161 162 indicator.hasActive: !root.disableAllWindowsFunctionality && taskItem.hasActive 163 indicator.hasMinimized: !root.disableAllWindowsFunctionality && taskItem.hasMinimized 164 indicator.hasShown: !root.disableAllWindowsFunctionality && taskItem.hasShown 165 indicator.windowsCount: !root.disableAllWindowsFunctionality ? taskItem.windowsCount : 0 166 indicator.windowsMinimizedCount: !root.disableAllWindowsFunctionality ? taskItem.windowsMinimizedCount : 0 167 168 indicator.scaleFactor: taskItem.parabolicItem.zoom 169 indicator.panelOpacity: taskItem.abilities.myView.backgroundOpacity 170 indicator.shadowColor: taskItem.abilities.myView.itemShadow.shadowSolidColor 171 172 indicator.progressVisible: taskIcon.progressVisible /*since 0.9.2*/ 173 indicator.progress: taskIcon.progress /*since 0.9.2*/ 174 175 indicator.palette: taskItem.abilities.myView.palette 176 177 indicator.iconBackgroundColor: taskIcon.backgroundColor 178 indicator.iconGlowColor: taskIcon.glowColor 179 //! Indicator Properties 180 181 onModelLauncherUrlChanged: { 182 if (modelLauncherUrl !== ""){ 183 launcherUrl = modelLauncherUrl; 184 185 //!extract the launcherName if possible 186 var nameStarts = launcherUrl.lastIndexOf("/"); 187 if (nameStarts === -1){ 188 nameStarts = launcherUrl.lastIndexOf(":"); 189 } 190 191 var nameEnds = launcherUrl.lastIndexOf(".desktop"); 192 193 if (nameStarts!==-1 && nameEnds!==-1 && nameStarts<nameEnds) { 194 launcherName = launcherUrl.substring(nameStarts+1,nameEnds); 195 } 196 } 197 198 if (taskItem.abilities.launchers.isSeparator(modelLauncherUrl)){ 199 isSeparator = true; 200 } else { 201 isSeparator = false; 202 } 203 } 204 205 onModelLauncherUrlWithIconChanged: { 206 if (modelLauncherUrlWithIcon !== ""){ 207 launcherUrlWithIcon = modelLauncherUrlWithIcon; 208 } 209 } 210 211 onHoveredFromDraggingChanged: { 212 if (hoveredFromDragging) { 213 scrollableList.autoScrollFor(taskItem, true); 214 } 215 } 216 217 ////// Audio streams ////// 218 property Item audioStreamOverlay 219 property var audioStreams: [] 220 readonly property bool hasAudioStream: root.showAudioBadge && audioStreams.length > 0 && !isLauncher 221 readonly property bool playingAudio: hasAudioStream && audioStreams.some(function (item) { 222 return !item.corked 223 }) 224 225 readonly property bool muted: hasAudioStream && audioStreams.every(function (item) { 226 return item.muted 227 }) 228 229 readonly property int volume: { 230 if (!hasAudioStream){ 231 return 0; 232 } 233 234 var maxVolume = 0; 235 for (var i=0; i<audioStreams.length; ++i){ 236 if (audioStreams[i].volume > maxVolume) 237 maxVolume = audioStreams[i].volume; 238 } 239 240 return maxVolume; 241 } 242 243 //! Content Item 244 contentItem: TaskIcon{ 245 id:taskIcon 246 } 247 ////// 248 249 property QtObject contextMenu: null 250 251 signal checkWindowsStates(); 252 253 SubWindows{ 254 id: subWindows 255 256 property int previousCount: 0 257 258 onWindowsCountChanged: { 259 if (root.disableAllWindowsFunctionality) { 260 return; 261 } 262 263 if ((windowsCount >= 2) 264 && (windowsCount > previousCount) 265 && !(taskItem.containsMouse) 266 && !root.dragSource ){ 267 taskItem.taskGroupedWindowAdded(); 268 } else if ((windowsCount >= 1) 269 && (windowsCount < previousCount) 270 && !root.dragSource 271 && !taskItem.delayingRemove){ 272 //sometimes this is triggered in dragging with no reason 273 taskItem.taskGroupedWindowRemoved(); 274 } 275 276 if (windowsCount>=1) { 277 taskItem.slotPublishGeometries(); 278 } 279 280 //! workaround in order to update correctly the previousCount 281 //! windowsCount can not return to zero because is such case 282 //! the window task is removed and the launcher is added from 283 //! libtaskmanager 284 if (windowsCount>=1) { 285 previousCount = windowsCount; 286 } 287 } 288 } 289 290 TaskMouseArea { 291 id: taskMouseArea 292 } 293 294 Timer { 295 id: publishGeometryTimer 296 interval: 800 297 repeat: false 298 299 onTriggered: { 300 slotPublishGeometries(); 301 302 if (taskItem.abilities.debug.timersEnabled) { 303 console.log("plasmoid timer: publishGeometryTimer called..."); 304 } 305 } 306 } 307 308 ////// Values Changes ///// 309 //restore scales when there is no zoom factor for that item or 310 //the mouse is out of the ListView 311 // onItemIndexChanged: { 312 // } 313 314 onAppNameChanged: updateAudioStreams() 315 onPidChanged: updateAudioStreams() 316 onHasAudioStreamChanged: updateAudioStreams() 317 318 onCanPublishGeometriesChanged: { 319 if (canPublishGeometries) { 320 slotPublishGeometries(); 321 publishGeometryTimer.start(); 322 } 323 } 324 325 onItemIndexChanged: { 326 if (itemIndex>=0) { 327 lastValidTimer.start(); 328 } 329 } 330 331 onIsDraggedChanged: { 332 if (isDragged){ 333 root.dragSource = taskItem; 334 dragHelper.startDrag(taskItem, model.MimeType, model.MimeData, 335 model.LauncherUrlWithoutIcon, model.decoration); 336 pressX = -1; 337 pressY = -1; 338 } 339 } 340 341 onIsMinimizedChanged: { 342 checkWindowsStates(); 343 } 344 345 onIsActiveChanged: { 346 checkWindowsStates(); 347 if (isActive) { 348 scrollableList.focusOn(taskItem); 349 } 350 } 351 352 onIsSeparatorChanged: { 353 if (isSeparator) { 354 if (tasksExtendedManager.isLauncherToBeMoved(launcherUrl) && itemIndex>=0) { 355 tasksExtendedManager.moveLauncherToCorrectPos(launcherUrl, itemIndex); 356 } 357 } 358 } 359 360 onLauncherUrlChanged: updateBadge(); 361 362 onShortcutRequestedActivate: { 363 if (taskItem.isGroupParent) { 364 taskItem.activateNextTask(); 365 } else { 366 taskItem.activateTask(); 367 } 368 } 369 370 onShortcutRequestedNewInstance: { 371 tasksModel.requestNewInstance(taskItem.modelIndex()); 372 } 373 374 ////// End of Values Changes and Signals ///// 375 376 377 //! A timer is needed in order to handle also touchpads that probably 378 //! send too many signals very fast. This way the signals per sec are limited. 379 //! The user needs to have a steady normal scroll in order to not 380 //! notice a annoying delay 381 Timer{ 382 id: scrollDelayer 383 384 interval: 400 385 386 onTriggered: taskItem.wheelIsBlocked = false; 387 } 388 389 ///////////////// End Of Mouse Area Events /////////////////// 390 391 ///// Handlers for Signals ///// 392 function animationStarted(){ 393 // console.log("Animation started: " + index); 394 inAnimation = true; 395 } 396 397 function animationEnded(){ 398 // console.log("Animation ended: " + index); 399 inAnimation = false; 400 } 401 402 function handlerDraggingFinished(){ 403 isDragged = false; 404 } 405 ///// End of Handlers ////// 406 407 408 409 ///// Helper functions ///// 410 function activateNextTask() { 411 subWindows.activateNextTask(); 412 } 413 414 function activateLauncher() { 415 if (LatteCore.WindowSystem.compositingActive) { 416 taskItem.taskLauncherActivated(); 417 hasAddedWaitingLauncher = true; 418 tasksExtendedManager.addWaitingLauncher(taskItem.launcherUrl); 419 } 420 421 if (root.disableAllWindowsFunctionality) { 422 tasksModel.requestNewInstance(modelIndex()); 423 } else { 424 tasksModel.requestActivate(modelIndex()); 425 } 426 } 427 428 function activateTask() { 429 if( taskItem.isLauncher || root.disableAllWindowsFunctionality){ 430 activateLauncher(); 431 } else{ 432 if (model.IsGroupParent) { 433 var canPresentWindowsIsSupported = LatteCore.WindowSystem.compositingActive && (root.plasmaGreaterThan522 ? backend.canPresentWindows : backend.canPresentWindows()); 434 if (canPresentWindowsIsSupported) { 435 root.presentWindows(root.plasma515 ? model.WinIdList: model.LegacyWinIdList ); 436 } 437 } else { 438 if (windowsPreviewDlg.visible) { 439 forceHidePreview(8.3); 440 } 441 442 if (isMinimized) { 443 var i = modelIndex(); 444 tasksModel.requestToggleMinimized(i); 445 tasksModel.requestActivate(i); 446 } else if (isActive) { 447 tasksModel.requestToggleMinimized(modelIndex()); 448 } else { 449 tasksModel.requestActivate(modelIndex()); 450 } 451 } 452 } 453 } 454 455 function forceHidePreview(debugtext) { 456 showPreviewsIsBlockedFromReleaseEvent = true; 457 hoveredTimer.stop(); 458 459 root.forcePreviewsHiding(debugtext); 460 } 461 462 function showPreviewWindow() { 463 if (root.disableAllWindowsFunctionality || !isAbleToShowPreview) { 464 return; 465 } 466 467 if(windowsPreviewDlg.activeItem !== taskItem){ 468 if (!taskItem.abilities.myView.isReady 469 || (taskItem.abilities.myView.isReady && taskItem.abilities.myView.isShownFully)) { 470 taskItem.preparePreviewWindow(false); 471 windowsPreviewDlg.show(taskItem); 472 } 473 } 474 } 475 476 function hidePreviewWindow() { 477 if(windowsPreviewDlg.activeItem === taskItem){ 478 windowsPreviewDlg.hide("14.1"); 479 } 480 } 481 482 function preparePreviewWindow(hideClose){ 483 windowsPreviewDlg.visualParent = tooltipVisualParent; 484 toolTipDelegate.parentTask = taskItem; 485 toolTipDelegate.rootIndex = tasksModel.makeModelIndex(itemIndex, -1); 486 487 toolTipDelegate.hideCloseButtons = hideClose; 488 489 toolTipDelegate.appName = Qt.binding(function() { 490 return model.AppName; 491 }); 492 493 if (!isLauncher) { 494 toolTipDelegate.pidParent = Qt.binding(function() { 495 return model.AppPid; 496 }); 497 } else { 498 toolTipDelegate.pidParent = -1; 499 } 500 501 toolTipDelegate.windows = Qt.binding(function() { 502 return root.plasma515 ? model.WinIdList : model.LegacyWinIdList ; 503 }); 504 toolTipDelegate.isGroup = Qt.binding(function() { 505 return model.IsGroupParent == true; 506 }); 507 toolTipDelegate.icon = Qt.binding(function() { 508 return model.decoration; 509 }); 510 toolTipDelegate.launcherUrl = Qt.binding(function() { 511 return model.LauncherUrlWithoutIcon; 512 }); 513 toolTipDelegate.isLauncher = Qt.binding(function() { 514 return model.IsLauncher == true; 515 }); 516 toolTipDelegate.isMinimizedParent = Qt.binding(function() { 517 return model.IsMinimized == true; 518 }); 519 toolTipDelegate.displayParent = Qt.binding(function() { 520 return model.display; 521 }); 522 toolTipDelegate.genericName = Qt.binding(function() { 523 return model.GenericName; 524 }); 525 toolTipDelegate.virtualDesktopParent = Qt.binding(function() { 526 return (model.VirtualDesktops !== undefined && model.VirtualDesktops.length > 0) ? model.VirtualDesktops : [0]; 527 }); 528 toolTipDelegate.isOnAllVirtualDesktopsParent = Qt.binding(function() { 529 return model.IsOnAllVirtualDesktops == true; 530 }); 531 toolTipDelegate.activitiesParent = Qt.binding(function() { 532 return model.Activities; 533 }); 534 } 535 536 ///window previews/// 537 function generateSubText(task) { 538 var subTextEntries = new Array(); 539 540 if (!plasmoid.configuration.showOnlyCurrentDesktop 541 && virtualDesktopInfo.numberOfDesktops > 1 542 && model.IsOnAllVirtualDesktops !== true 543 && model.VirtualDesktop != -1 544 && model.VirtualDesktop != undefined) { 545 subTextEntries.push(i18n("On %1", virtualDesktopInfo.desktopNames[model.VirtualDesktop - 1])); 546 } 547 548 if (model.Activities == undefined) { 549 return subTextEntries.join("\n"); 550 } 551 552 if (model.Activities.length == 0 && activityInfo.numberOfRunningActivities > 1) { 553 subTextEntries.push(i18nc("Which virtual desktop a window is currently on", 554 "Available on all activities")); 555 } else if (model.Activities.length > 0) { 556 var activityNames = new Array(); 557 558 for (var i = 0; i < model.Activities.length; i++) { 559 var activity = model.Activities[i]; 560 561 if (plasmoid.configuration.showOnlyCurrentActivity) { 562 if (activity != activityInfo.currentActivity) { 563 activityNames.push(activityInfo.activityName(model.Activities[i])); 564 } 565 } else if (activity != activityInfo.currentActivity) { 566 activityNames.push(activityInfo.activityName(model.Activities[i])); 567 } 568 } 569 570 if (plasmoid.configuration.showOnlyCurrentActivity) { 571 if (activityNames.length > 0) { 572 subTextEntries.push(i18nc("Activities a window is currently on (apart from the current one)", 573 "Also available on %1", activityNames.join(", "))); 574 } 575 } else if (activityNames.length > 0) { 576 subTextEntries.push(i18nc("Which activities a window is currently on", 577 "Available on %1", activityNames.join(", "))); 578 } 579 } 580 581 return subTextEntries.join("\n"); 582 } 583 ///window previews//// 584 585 function modelIndex(){ 586 return tasksModel.makeModelIndex(index); 587 } 588 589 function showContextMenu(args) { 590 if (isSeparator && !root.inEditMode) 591 return; 592 593 if (!root.contextMenu) { 594 contextMenu = root.createContextMenu(taskItem, modelIndex(), args); 595 contextMenu.show(); 596 } else { 597 //! make sure that context menu isnt deleted multiple times and creates a crash 598 //! bug case: 397635 599 var cMenu = root.contextMenu; 600 root.contextMenu = null; 601 cMenu.destroy(); 602 } 603 } 604 605 function modifierAccepted(mouse){ 606 if (mouse.modifiers & root.modifierQt){ 607 if ((mouse.button === Qt.LeftButton && root.modifierClick === LatteTasks.Types.LeftClick) 608 || (mouse.button === Qt.MiddleButton && root.modifierClick === LatteTasks.Types.MiddleClick) 609 || (mouse.button === Qt.RightButton && root.modifierClick === LatteTasks.Types.RightClick)) 610 return true; 611 } 612 613 return false; 614 } 615 616 function setBlockingAnimation(value){ 617 inBlockingAnimation = value; 618 } 619 620 function slotShowPreviewForTasks(group) { 621 if (group === taskItem && !windowsPreviewDlg.visible) { 622 preparePreviewWindow(true); 623 windowsPreviewDlg.show(taskItem); 624 } 625 } 626 627 function slotPublishGeometries() { 628 //! this way we make sure that layouts that are in different activities that the current layout 629 //! don't publish their geometries 630 if ( canPublishGeometries && (!taskItem.abilities.myView.isReady || (taskItem.abilities.myView.isReady && taskItem.abilities.myView.inCurrentLayout()))) { 631 var globalChoords = backend.globalRect(taskItem.parabolicItem.contentItemContainer); 632 var limits = backend.globalRect(scrollableList); 633 634 //! Limit the published geometries boundaries at scrolling area boundaries 635 var adjX = Math.min(limits.x+limits.width, Math.max(limits.x, globalChoords.x)); 636 var adjY = Math.min(limits.y+limits.height, Math.max(limits.y, globalChoords.y)); 637 638 var length = taskItem.abilities.metrics.iconSize * taskItem.parabolicItem.zoom; 639 var thickness = length; 640 641 //! Magic Lamp effect doesn't like coordinates outside the screen and 642 //! width,heights of zero value... So we now normalize the geometries 643 //! sent in order to avoid such circumstances 644 if (root.vertical) { 645 if (adjY !== globalChoords.y) { 646 if (((globalChoords.y+globalChoords.height) < limits.y) || (globalChoords.y)>(limits.y+limits.height)) { 647 //! totally out of boundaries 648 length = 4; 649 } else { 650 //! semi-part out of boundaries 651 length = Math.max(4, Math.abs(adjY - globalChoords.y)); 652 } 653 654 globalChoords.height = length; 655 } 656 } else { 657 if (adjX !== globalChoords.x) { 658 if (((globalChoords.x+globalChoords.width) < limits.x) || (globalChoords.x)>(limits.x+limits.width)) { 659 //! totally out of boundaries 660 length = 4; 661 } else { 662 //! semi-part out of boundaries 663 length = Math.max(4, Math.abs(adjX - globalChoords.x)); 664 } 665 666 globalChoords.width = length; 667 } 668 } 669 670 globalChoords.x = adjX; 671 globalChoords.y = adjY; 672 673 if (taskItem.abilities.myView.isHidden) { 674 if (root.location === PlasmaCore.Types.BottomEdge) { 675 globalChoords.y = taskItem.abilities.myView.screenGeometry.y + taskItem.abilities.myView.screenGeometry.height-1; 676 globalChoords.height = 1; 677 } else if (root.location === PlasmaCore.Types.TopEdge) { 678 globalChoords.y = taskItem.abilities.myView.screenGeometry.y+1; 679 globalChoords.height = 1; 680 } else if (root.location === PlasmaCore.Types.LeftEdge) { 681 globalChoords.x = taskItem.abilities.myView.screenGeometry.x+1; 682 globalChoords.width = 1; 683 } else if (root.location === PlasmaCore.Types.RightEdge) { 684 globalChoords.x = taskItem.abilities.myView.screenGeometry.x + taskItem.abilities.myView.screenGeometry.width - 1; 685 globalChoords.width = 1; 686 } 687 } 688 689 tasksModel.requestPublishDelegateGeometry(taskItem.modelIndex(), globalChoords, taskItem); 690 } 691 } 692 693 function slotWaitingLauncherRemoved(launch) { 694 if ((isWindow || isStartup || isLauncher) && !visible && launch === launcherUrl) { 695 if (!taskItem.abilities.indicators.info.providesTaskLauncherAnimation) { 696 //! this is needed only from in-built launcher animation to restore zoom smoothly 697 taskItem.parabolicItem.zoom = 1; 698 } 699 visible = true; 700 } 701 } 702 703 704 function updateAudioStreams() { 705 if (root.dragSource !== null) { 706 audioStreams = []; 707 return; 708 } 709 710 var pa = pulseAudio.item; 711 if (!pa) { 712 audioStreams = []; 713 return; 714 } 715 716 var streams = pa.streamsForPid(taskItem.pid); 717 if (streams.length) { 718 pa.registerPidMatch(taskItem.appName); 719 } else { 720 // We only want to fall back to appName matching if we never managed to map 721 // a PID to an audio stream window. Otherwise if you have two instances of 722 // an application, one playing and the other not, it will look up appName 723 // for the non-playing instance and erroneously show an indicator on both. 724 if (!pa.hasPidMatch(taskItem.appName)) { 725 streams = pa.streamsForAppName(taskItem.appName); 726 } 727 } 728 729 // fix a binding loop concerning audiostreams, the audiostreams 730 // should be updated only when they have changed 731 var changed = false; 732 733 if (streams.length !== audioStreams.length) { 734 changed = true; 735 } else { 736 for(var i=0; i<streams.length; ++i) { 737 if (streams[i] !== audioStreams[i]) { 738 changed = true; 739 break; 740 } 741 } 742 } 743 744 if (changed) { 745 taskItem.audioStreams = streams; 746 } 747 } 748 749 function onLauncherChanged(launcher) { 750 if ((root.showWindowsOnlyFromLaunchers || root.disableAllWindowsFunctionality) && launcher === launcherUrl) { 751 updateVisibilityBasedOnLaunchers() 752 } 753 } 754 755 function updateVisibilityBasedOnLaunchers(){ 756 var launcherExists = !(((tasksModel.launcherPosition(taskItem.launcherUrl) == -1) 757 && (tasksModel.launcherPosition(taskItem.launcherUrlWithIcon) == -1) ) 758 || !taskItem.abilities.launchers.inCurrentActivity(taskItem.launcherUrl)); 759 760 if (root.showWindowsOnlyFromLaunchers || root.disableAllWindowsFunctionality) { 761 var hideWindow = !launcherExists && (taskItem.isWindow || root.disableAllWindowsFunctionality); 762 763 if (hideWindow) { 764 isForcedHidden = true; 765 taskRealRemovalAnimation.start(); 766 } else if (launcherExists && taskItem.isWindow && !taskItem.isVisible) { 767 showWindowAnimation.showWindow(); 768 isForcedHidden = false; 769 } 770 } else { 771 var showWindow = !launcherExists && taskItem.isWindow; 772 773 if (showWindow) { 774 showWindowAnimation.showWindow(); 775 isForcedHidden = false; 776 } 777 } 778 } 779 780 function toggleMuted() { 781 if (muted) { 782 taskItem.audioStreams.forEach(function (item) { item.unmute(); }); 783 } else { 784 taskItem.audioStreams.forEach(function (item) { item.mute(); }); 785 } 786 } 787 788 function increaseVolume() { 789 taskItem.audioStreams.forEach(function (item) { item.increaseVolume(); }); 790 } 791 792 function decreaseVolume() { 793 taskItem.audioStreams.forEach(function (item) { item.decreaseVolume(); }); 794 } 795 796 function updateBadge() { 797 var badger = root.getBadger(launcherUrl); 798 799 if (badger) { 800 badgeIndicator = parseInt(badger.value); 801 } else { 802 badgeIndicator = 0; 803 } 804 } 805 806 Connections { 807 target: pulseAudio.item 808 ignoreUnknownSignals: true // Plasma-PA might not be available 809 onStreamsChanged: taskItem.updateAudioStreams() 810 } 811 812 //fix bug #478, when changing form factor sometimes the tasks are not positioned 813 //correctly, in such case we make a fast reinitialization for the sizes 814 Connections { 815 target: plasmoid 816 onFormFactorChanged:{ 817 taskItem.inAddRemoveAnimation = false; 818 } 819 } 820 821 Connections { 822 target: root 823 //trying to fix #440, showing the audio icon indicator to irrelevant tasks 824 //after dragging an existent task with audio 825 onDragSourceChanged: taskItem.updateAudioStreams() 826 onShowAudioBadgeChanged: taskItem.updateAudioStreams() 827 828 onDisableAllWindowsFunctionalityChanged: { 829 if (!root.inEditMode) { 830 return; 831 } 832 833 taskItem.updateVisibilityBasedOnLaunchers(); 834 } 835 836 onShowWindowsOnlyFromLaunchersChanged: { 837 if (!root.inEditMode) { 838 return; 839 } 840 841 taskItem.updateVisibilityBasedOnLaunchers(); 842 } 843 844 onInActivityChangeChanged: { 845 if ((root.showWindowsOnlyFromLaunchers || root.disableAllWindowsFunctionality) && !root.inActivityChange) { 846 taskItem.updateVisibilityBasedOnLaunchers(); 847 } 848 } 849 } 850 851 Connections { 852 target: scrollableList 853 onAnimationsFinishedChanged: { 854 if (scrollableList.animationsFinished) { 855 taskItem.slotPublishGeometries(); 856 } 857 } 858 } 859 860 Connections { 861 target: taskItem.abilities.myView 862 onIsHiddenChanged: { 863 if (taskItem.abilities.myView.isHidden) { 864 taskItem.slotPublishGeometries(); 865 } 866 } 867 868 onIsShownFullyChanged: { 869 if (taskItem.abilities.myView.isShownFully) { 870 taskItem.slotPublishGeometries(); 871 } 872 } 873 } 874 875 ///// End of Helper functions //// 876 877 Component.onCompleted: { 878 parabolicItem.opacity = 0; 879 880 root.draggingFinished.connect(handlerDraggingFinished); 881 root.publishTasksGeometries.connect(slotPublishGeometries); 882 root.showPreviewForTasks.connect(slotShowPreviewForTasks); 883 884 taskItem.abilities.launchers.launcherChanged.connect(onLauncherChanged); 885 taskItem.abilities.launchers.launcherRemoved.connect(onLauncherChanged); 886 887 //startup without launcher 888 var hideStartup = ((!hasShownLauncher || !taskItem.abilities.launchers.inCurrentActivity(taskItem.launcherUrl)) 889 && taskItem.isStartup); 890 891 if (!LatteCore.WindowSystem.compositingActive) { 892 visible = true; 893 } else if ( (isWindow || isStartup || isLauncher) && tasksExtendedManager.waitingLauncherExists(launcherUrl)) { 894 tasksExtendedManager.waitingLauncherRemoved.connect(slotWaitingLauncherRemoved); 895 visible = false; 896 } else if (hideStartup){ 897 visible = false; 898 } else { 899 visible = true; 900 } 901 902 showWindowAnimation.showWindow(); 903 updateAudioStreams(); 904 } 905 906 Component.onDestruction: { 907 root.draggingFinished.disconnect(handlerDraggingFinished); 908 root.publishTasksGeometries.disconnect(slotPublishGeometries); 909 root.showPreviewForTasks.disconnect(slotShowPreviewForTasks); 910 911 taskItem.abilities.launchers.launcherChanged.disconnect(onLauncherChanged); 912 taskItem.abilities.launchers.launcherRemoved.disconnect(onLauncherChanged); 913 914 tasksExtendedManager.waitingLauncherRemoved.disconnect(slotWaitingLauncherRemoved); 915 916 taskItem.parabolicItem.sendEndOfNeedBothAxisAnimation(); 917 } 918 919 /////Animations 920 TaskAnimations.ShowWindowAnimation{ id: _showWindowAnimation } 921 922 // when changing activities and desktops the index of the tasks 923 // is updated immediately to -1, this timer protects this indexing 924 // change in order to provide a beautiful removal tasks animation 925 Timer { 926 id: lastValidTimer 927 interval: 100 ///the interval does not follow the animations timing 928 repeat: false 929 930 onTriggered: { 931 if (taskItem.itemIndex >= 0){ 932 taskItem.lastValidIndex = taskItem.itemIndex; 933 } 934 935 if (taskItem.abilities.debug.timersEnabled) { 936 console.log("plasmoid timer: lastValidTimer called..."); 937 } 938 } 939 } 940 941 ///Item's Removal Animation 942 ListView.onRemove: TaskAnimations.RealRemovalAnimation{ id: taskRealRemovalAnimation } 943 944 onIsLauncherAnimationRunningChanged: { 945 if (!isLauncherAnimationRunning && taskRealRemovalAnimation.paused) { 946 taskRealRemovalAnimation.resume(); 947 } 948 } 949}// main Item 950 951