diff --git a/containment/package/contents/ui/applet/AppletItem.qml b/containment/package/contents/ui/applet/AppletItem.qml index 3d4832b5..f2139c8b 100644 --- a/containment/package/contents/ui/applet/AppletItem.qml +++ b/containment/package/contents/ui/applet/AppletItem.qml @@ -1,868 +1,882 @@ /* * Copyright 2016 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import QtQuick 2.1 import QtQuick.Layouts 1.1 import QtGraphicalEffects 1.0 import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.kquickcontrolsaddons 2.0 import org.kde.latte 0.2 as Latte import "colorizer" as Colorizer import "communicator" as Communicator +import "indicator" as Indicator Item { id: appletItem visible: false width: isInternalViewSplitter && !root.editMode ? 0 : computeWidth height: isInternalViewSplitter && !root.editMode ? 0 : computeHeight property bool animationsEnabled: true property bool animationWasSent: false //protection flag for animation broadcasting property bool canBeHovered: true property bool canShowAppletNumberBadge: !isSeparator && !isHidden && !isLattePlasmoid && !isSpacer && !isInternalViewSplitter property bool inFillCalculations: false //temp record, is used in calculations for fillWidth,fillHeight applets property bool needsFillSpace: { //fill flag, it is used in calculations for fillWidth,fillHeight applets if (!applet || !applet.Layout || (applet && applet.pluginName === "org.kde.plasma.panelspacer")) return false; if (((root.isHorizontal && applet.Layout.fillWidth===true) || (root.isVertical && applet.Layout.fillHeight===true)) && (applet.status !== PlasmaCore.Types.HiddenStatus)) return true; else return false; } property bool userBlocksColorizing: false property bool appletBlocksColorizing: !communicator.latteSideColoringEnabled property bool appletBlocksParabolicEffect: communicator.parabolicEffectLocked property bool lockZoom: false property bool indicatorNeedsIconColors: false + property bool indicatorProvidesFrontLayer: false property bool isActive: (isExpanded && applet.pluginName !== root.plasmoidName && applet.pluginName !== "org.kde.activeWindowControl" && applet.pluginName !== "org.kde.plasma.appmenu") property bool isExpanded: applet && applet.status >= PlasmaCore.Types.NeedsAttentionStatus && applet.status !== PlasmaCore.Types.HiddenStatus property bool isHidden: applet && applet.status === PlasmaCore.Types.HiddenStatus ? true : false property bool isInternalViewSplitter: (internalSplitterId > 0) property bool isLattePlasmoid: latteApplet !== null property bool isZoomed: false property bool isSeparator: applet && (applet.pluginName === "audoban.applet.separator" || applet.pluginName === "org.kde.latte.separator") property bool isSpacer: applet && (applet.pluginName === "org.kde.latte.spacer") property bool isSystray: applet && (applet.pluginName === "org.kde.plasma.systemtray" || applet.pluginName === "org.nomad.systemtray" ) property bool firstChildOfStartLayout: (index === layoutsContainer.startLayout.beginIndex) property bool lastChildOfEndLayout: ((index === layoutsContainer.endLayout.beginIndex+layoutsContainer.endLayout.count-1)&&(layoutsContainer.endLayout.count>1)) //applet is in starting edge /*property bool startEdge: index < layoutsContainer.endLayout.beginIndex ? (index === 0)&&(layoutsContainer.mainLayout.count > 1) : (index === layoutsContainer.endLayout.beginIndex)&&(layoutsContainer.endLayout.count > 1)*/ property bool startEdge: (index === layoutsContainer.startLayout.beginIndex) || (index === layoutsContainer.mainLayout.beginIndex) || (index === layoutsContainer.endLayout.beginIndex) //applet is in ending edge property bool endEdge: plasmoid.configuration.panelPosition !== Latte.Types.Justify ? (index === layoutsContainer.mainLayout.beginIndex + layoutsContainer.mainLayout.count - 1)&&(layoutsContainer.mainLayout.count>1) : (((index === layoutsContainer.startLayout.beginIndex+layoutsContainer.startLayout.count-2)&&(layoutsContainer.startLayout.count>2)) ||((index === layoutsContainer.mainLayout.beginIndex+layoutsContainer.mainLayout.count-2)&&(layoutsContainer.mainLayout.count>2)) ||((index === layoutsContainer.endLayout.beginIndex+layoutsContainer.endLayout.count-1)&&(layoutsContainer.endLayout.count>1))) property int animationTime: appliedDurationTime * (1.2 *units.shortDuration) property int hoveredIndex: layoutsContainer.hoveredIndex property int index: -1 property int maxWidth: root.isHorizontal ? root.height : root.width property int maxHeight: root.isHorizontal ? root.height : root.width property int internalSplitterId: 0 property int previousIndex: -1 property int sizeForFill: -1 //it is used in calculations for fillWidth,fillHeight applets property int spacersMaxSize: Math.max(0,Math.ceil(0.55 * root.iconSize) - root.lengthMargins) property int status: applet ? applet.status : -1 property real computeWidth: root.isVertical ? wrapper.width : hiddenSpacerLeft.width+wrapper.width+hiddenSpacerRight.width property real computeHeight: root.isVertical ? hiddenSpacerLeft.height + wrapper.height + hiddenSpacerRight.height : wrapper.height property string title: isInternalViewSplitter ? "Now Dock Splitter" : "" property Item applet: null property Item latteApplet: applet && (applet.pluginName === root.plasmoidName) ? (applet.children[0] ? applet.children[0] : null) : null property Item latteStyleApplet: applet && ((applet.pluginName === "org.kde.latte.spacer") || (applet.pluginName === "org.kde.latte.separator")) ? (applet.children[0] ? applet.children[0] : null) : null property Item appletWrapper: applet && ((applet.pluginName === root.plasmoidName) || isSystray) ? wrapper : wrapper.wrapperContainer property Item tooltipVisualParent: titleTooltipParent property Item communicatorAlias: communicator property Item wrapperAlias: wrapper property alias containsMouse: appletMouseArea.containsMouse property alias pressed: appletMouseArea.pressed /*onComputeHeightChanged: { if(index==0) console.log(computeHeight); }*/ /// BEGIN functions function activateAppletForNeutralAreas(mouse){ //if the event is at the active indicator or spacers area then try to expand the applet, //unfortunately for other applets there is no other way to activate them yet //for example the icon-only applets var choords = mapToItem(appletItem.appletWrapper, mouse.x, mouse.y); if (choords.x<0 || choords.y<0 || choords.x>=appletItem.appletWrapper.width || choords.y>=appletItem.appletWrapper.height) { latteView.toggleAppletExpanded(applet.id); } } function checkIndex(){ index = -1; for(var i=0; i maxSize || applet.Layout.minimumWidth > maxOfMinSize)) || (applet && root.isVertical && (applet.height > maxSize || applet.Layout.minimumHeight > maxOfMinSize))) && (applet && applet.pluginName !== "org.kde.plasma.panelspacer" && (applet.pluginName !== "org.kde.latte.spacer")) && !communicator.canShowOverlaiedLatteIcon) || isSystray || appletItem.needsFillSpace) { appletItem.canBeHovered = false; } else{ appletItem.canBeHovered = true; } } } //! pos in global root positioning function containsPos(pos) { var relPos = root.mapToItem(appletItem,pos.x, pos.y); if (relPos.x>=0 && relPos.x<=width && relPos.y>=0 && relPos.y<=height) return true; return false; } ///END functions //BEGIN connections onAppletChanged: { if (!applet) { destroy(); } } onHoveredIndexChanged:{ if ( (Math.abs(hoveredIndex-index) > 1) && (hoveredIndex !== -1) ) { wrapper.zoomScale = 1; } if (Math.abs(hoveredIndex-index) >= 1) { hiddenSpacerLeft.nScale = 0; hiddenSpacerRight.nScale = 0; } } onIndexChanged: { if (appletItem.latteApplet) { root.latteAppletPos = index; } if (isHidden) { parabolicManager.setHidden(previousIndex, index); } if (isSeparator) { parabolicManager.setSeparator(previousIndex, index); } if (index>-1) { previousIndex = index; } } onIsExpandedChanged: { if (isExpanded) { root.hideTooltipLabel(); } } onIsHiddenChanged: { if (isHidden) { parabolicManager.setHidden(-1, index); } else { parabolicManager.setHidden(index, -1); } } onIsSeparatorChanged: { if (isSeparator) { parabolicManager.setSeparator(-1, index); } else { parabolicManager.setSeparator(index, -1); } } onLatteAppletChanged: { if(appletItem.latteApplet){ root.latteApplet = appletItem.latteApplet; root.latteAppletContainer = appletItem; root.latteAppletPos = index; appletItem.latteApplet.latteView = root; appletItem.latteApplet.forceHidePanel = true; appletItem.latteApplet.signalAnimationsNeedBothAxis.connect(slotAnimationsNeedBothAxis); appletItem.latteApplet.signalAnimationsNeedLength.connect(slotAnimationsNeedLength); appletItem.latteApplet.signalAnimationsNeedThickness.connect(slotAnimationsNeedThickness); appletItem.latteApplet.signalActionsBlockHiding.connect(slotActionsBlockHiding); appletItem.latteApplet.signalPreviewsShown.connect(slotPreviewsShown); appletItem.latteApplet.clearZoomSignal.connect(titleTooltipDialog.hide); } } onNeedsFillSpaceChanged: checkCanBeHovered(); Component.onCompleted: { checkIndex(); root.updateIndexes.connect(checkIndex); root.clearZoomSignal.connect(clearZoom); } Component.onDestruction: { if (isSeparator){ parabolicManager.setSeparator(previousIndex, -1); } if (isHidden) parabolicManager.setHidden(previousIndex, -1); root.updateIndexes.disconnect(checkIndex); root.clearZoomSignal.disconnect(clearZoom); if (appletItem.latteApplet) { appletItem.latteApplet.signalAnimationsNeedBothAxis.disconnect(slotAnimationsNeedBothAxis); appletItem.latteApplet.signalAnimationsNeedLength.disconnect(slotAnimationsNeedLength); appletItem.latteApplet.signalAnimationsNeedThickness.disconnect(slotAnimationsNeedThickness); appletItem.latteApplet.signalActionsBlockHiding.disconnect(slotActionsBlockHiding); appletItem.latteApplet.signalPreviewsShown.disconnect(slotPreviewsShown); appletItem.latteApplet.clearZoomSignal.disconnect(titleTooltipDialog.hide); } } Connections{ target: root /* onGlobalDirectRenderChanged:{ if (root.globalDirectRender && restoreAnimation.running) { // console.log("CLEAR APPLET SCALE !!!!"); //restoreAnimation.stop(); //wrapper.zoomScale = 1; } }*/ onLatteAppletHoveredIndexChanged: { if ( (root.zoomFactor>1) && (root.latteAppletHoveredIndex >= 0) ){ var distance = 2; //for Tasks plasmoid distance of 2 is not always safe there are //cases that needs to be 3, when an internal separator there is //between the hovered task and the current applet if (root.hasInternalSeparator) { if (index < root.latteAppletPos) { var firstTaskIndex = root.latteApplet.parabolicManager.availableHigherIndex(0); distance = firstTaskIndex+2; } else if (index > root.latteAppletPos) { var lastTaskIndex = root.latteApplet.parabolicManager.availableLowerIndex(root.tasksCount-1); distance = root.tasksCount-1-lastTaskIndex+2; } } if(Math.abs(index-root.latteAppletPos+root.latteAppletHoveredIndex)>=Math.max(2,distance)) { appletItem.clearZoom(); } } } onSignalActivateEntryAtIndex: { if (parabolicManager.pseudoIndexBelongsToLatteApplet(entryIndex) && appletItem.isLattePlasmoid) { latteApplet.activateTaskAtIndex(entryIndex - latteApplet.tasksBaseIndex); } else if (root.unifiedGlobalShortcuts && (entryIndex === parabolicManager.pseudoAppletIndex(appletItem.index))) { latteView.toggleAppletExpanded(applet.id); } } onSignalNewInstanceForEntryAtIndex: { if (parabolicManager.pseudoIndexBelongsToLatteApplet(entryIndex) && appletItem.isLattePlasmoid) { latteApplet.newInstanceForTaskAtIndex(entryIndex - latteApplet.tasksBaseIndex); } else if (root.unifiedGlobalShortcuts && (entryIndex === parabolicManager.pseudoAppletIndex(appletItem.index))) { latteView.toggleAppletExpanded(applet.id); } } } Connections{ target: layoutsContainer onHoveredIndexChanged:{ //for applets it is safe to consider that a distance of 2 //is enough to clearZoom if ( (root.zoomFactor>1) && (layoutsContainer.hoveredIndex>=0) && (Math.abs(index-layoutsContainer.hoveredIndex)>=2)) appletItem.clearZoom(); if ((restoreAnimation.running) && (layoutsContainer.hoveredIndex !== -1)) { restoreAnimation.stop(); } } } Connections{ target: root onLatteAppletHoveredIndexChanged: { if ((restoreAnimation.running) && (root.latteAppletHoveredIndex !== -1)) { restoreAnimation.stop(); } } } ///END connections //! It is used for any communication needed with the underlying applet Communicator.Engine{ id: communicator //set up the overlayed appletItems and properties for when a overlaiedIconItem must be presented to the user //because the plasma widgets specific implementation breaks the Latte experience onOverlayLatteIconIsActiveChanged:{ if (!overlayLatteIconIsActive && applet.opacity===0) { applet.opacity = 1; wrapper.disableScaleWidth = false; wrapper.disableScaleHeight = false; wrapper.updateLayoutWidth(); wrapper.updateLayoutHeight(); } else if (overlayLatteIconIsActive && applet.opacity>0) { applet.opacity = 0; if (applet.pluginName === "org.kde.plasma.folder") { applet.parent = wrapper.containerForOverlayIcon; applet.anchors.fill = wrapper.containerForOverlayIcon; } wrapper.disableScaleWidth = false; wrapper.disableScaleHeight = false; wrapper.updateLayoutWidth(); wrapper.updateLayoutHeight(); } } } /* Rectangle{ anchors.fill: parent color: "transparent" border.color: "green" border.width: 1 }*/ MouseArea{ id: appletMouseAreaBottom anchors.fill: parent propagateComposedEvents: true visible: (!appletMouseArea.visible || !appletMouseArea.enabled) && !root.editMode property bool pressed: false onPressed: { appletItem.activateAppletForNeutralAreas(mouse); pressed = true; mouse.accepted = false; } onReleased: { pressed = false; mouse.accepted = false; } } //! Main Applet Shown Area Flow{ id: appletFlow width: appletItem.computeWidth height: appletItem.computeHeight // a hidden spacer for the first element to add stability // IMPORTANT: hidden spacers must be tested on vertical !!! HiddenSpacer{id: hiddenSpacerLeft} Item { width: wrapper.width height: wrapper.height + Indicator.Manager{ + id: indicatorManager + } + //! Indicator Back Layer - IndicatorLoader{ + Indicator.Loader{ id: indicatorBackLayer + manager: indicatorManager } ItemWrapper{ id: wrapper TitleTooltipParent{ id: titleTooltipParent } } //! The Applet Colorizer Colorizer.Applet { id: appletColorizer anchors.fill: parent opacity: mustBeShown ? 1 : 0 readonly property bool mustBeShown: colorizerManager.mustBeShown && !appletItem.userBlocksColorizing && !appletItem.appletBlocksColorizing && !appletItem.isInternalViewSplitter Behavior on opacity { NumberAnimation { duration: 1.2 * root.animationTime easing.type: Easing.OutCubic } } } + //! Indicator Front Layer + Indicator.Loader{ + id: indicatorFrontLayer + manager: indicatorManager + isBackLayer: false + } + //! Applet Shortcut Visual Badge ShortcutBadge{ anchors.fill: wrapper } } // a hidden spacer on the right for the last item to add stability HiddenSpacer{id: hiddenSpacerRight; rightSpacer: true} }// Flow with hidden spacers inside //Busy Indicator PlasmaComponents.BusyIndicator { z: 1000 visible: applet && applet.busy running: visible anchors.centerIn: parent width: Math.min(parent.width, parent.height) height: width } //! The Launchers Area Indicator Rectangle{ anchors.fill: parent radius: root.iconSize/10 property color tempColor: "#aa222222" color: tempColor border.width: 1 border.color: "#ff656565" opacity: latteApplet && root.addLaunchersMessage ? 1 : 0 Behavior on opacity{ NumberAnimation { duration: 2*root.durationTime*appletItem.animationTime } } PlasmaExtras.Heading { width: parent.width height: parent.height text: i18n("Tasks Area") horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter wrapMode: Text.WordWrap elide: Text.ElideRight fontSizeMode: Text.Fit color: colorizerManager.applyTheme.textColor rotation: { if (root.isHorizontal) return 0; else if (plasmoid.location === PlasmaCore.Types.LeftEdge) return -90; else if (plasmoid.location === PlasmaCore.Types.RightEdge) return 90; } } } MouseArea{ id: appletMouseArea anchors.fill: parent enabled: applet && !latteApplet && canBeHovered && !lockZoom && !communicator.parabolicEffectLocked hoverEnabled: latteApplet ? false : true propagateComposedEvents: true //! a way must be found in order for this be enabled //! only to support springloading for plasma 5.10 //! also on this is based the tooltips behavior by enabling it //! plasma tooltips are disabled visible: applet && !appletItem.latteApplet && !lockZoom && !communicator.parabolicEffectLocked && canBeHovered && !appletItem.isSeparator property bool blockWheel: false property bool pressed: false onClicked: { pressed = false; mouse.accepted = false; } onEntered: { if (containsMouse && !appletItem.lockZoom && !communicator.parabolicEffectLocked && appletItem.canBeHovered){ root.stopCheckRestoreZoomTimer(); } if (restoreAnimation.running) { restoreAnimation.stop(); } if (!(isSeparator || isSpacer)) { root.showTooltipLabel(appletItem, applet.title); } //console.log("entered applet:" + layoutsContainer.hoveredIndex); if (layoutsContainer.hoveredIndex === -1 && root.latteAppletHoveredIndex===-1) { root.startDirectRenderDelayerDuringEntering(); } if (!(root.latteViewIsHidden || root.inSlidingIn || root.inSlidingOut)){ layoutsContainer.hoveredIndex = index; } if (lockZoom || communicator.parabolicEffectLocked || !canBeHovered) { return; } if (root.isHalfShown || (root.latteApplet && (root.latteApplet.noTasksInAnimation>0 || root.latteApplet.contextMenu))) { return; } if (root.isHorizontal){ layoutsContainer.currentSpot = mouseX; wrapper.calculateScales(mouseX); } else{ layoutsContainer.currentSpot = mouseY; wrapper.calculateScales(mouseY); } } onExited:{ if (communicator.appletIconItemIsShown()) { communicator.setAppletIconItemActive(false); } root.hideTooltipLabel(); if (root.zoomFactor>1){ root.startCheckRestoreZoomTimer(); } } onPositionChanged: { // if(!pressed){ if (lockZoom || !canBeHovered) { mouse.accepted = false; return; } if (root.isHalfShown || (root.latteApplet && (root.latteApplet.noTasksInAnimation>0 || root.latteApplet.contextMenu))) { return; } if (layoutsContainer.hoveredIndex === -1 && root.latteAppletHoveredIndex===-1) { root.startDirectRenderDelayerDuringEntering(); } if (!(root.latteViewIsHidden || root.inSlidingIn || root.inSlidingOut)){ layoutsContainer.hoveredIndex = index; } if (!root.globalDirectRender && !root.directRenderDelayerIsRunning) { root.setGlobalDirectRender(true); } if( ((wrapper.zoomScale == 1 || wrapper.zoomScale === root.zoomFactor) && !root.globalDirectRender) || root.globalDirectRender) { if (root.isHorizontal){ var step = Math.abs(layoutsContainer.currentSpot-mouse.x); if (step >= root.animationStep){ layoutsContainer.currentSpot = mouse.x; wrapper.calculateScales(mouse.x); } } else{ var step = Math.abs(layoutsContainer.currentSpot-mouse.y); if (step >= root.animationStep){ layoutsContainer.currentSpot = mouse.y; wrapper.calculateScales(mouse.y); } } } mouse.accepted = false; } onPressed: { appletItem.activateAppletForNeutralAreas(mouse); pressed = true; //! this is needed for some applets is order to be activated/deactivated correctly //! such case is the "Application Menu". (bug #928) mouse.accepted = false; } onReleased: { pressed = false; mouse.accepted = false; } onWheel: { if (isSeparator || !root.mouseWheelActions || blockWheel || (root.latteViewIsHidden || root.inSlidingIn || root.inSlidingOut)){ wheel.accepted = false; return; } var angle = wheel.angleDelta.y / 8; blockWheel = true; scrollDelayer.start(); if (angle > 12) { //positive direction if (!isExpanded) { latteView.toggleAppletExpanded(applet.id); } } else if (angle < -12) { //negative direction if (isExpanded) { latteView.toggleAppletExpanded(applet.id); } } } //! A timer is needed in order to handle also touchpads that probably //! send too many signals very fast. This way the signals per sec are limited. //! The user needs to have a steady normal scroll in order to not //! notice a annoying delay Timer{ id: scrollDelayer interval: 700 onTriggered: appletMouseArea.blockWheel = false; } } //BEGIN states states: [ State { name: "left" when: (plasmoid.location === PlasmaCore.Types.LeftEdge) AnchorChanges { target: appletFlow anchors{ top:undefined; bottom:undefined; left:parent.left; right:undefined;} } }, State { name: "right" when: (plasmoid.location === PlasmaCore.Types.RightEdge) AnchorChanges { target: appletFlow anchors{ top:undefined; bottom:undefined; left:undefined; right:parent.right;} } }, State { name: "bottom" when: (plasmoid.location === PlasmaCore.Types.BottomEdge) AnchorChanges { target: appletFlow anchors{ top:undefined; bottom:parent.bottom; left:undefined; right:undefined;} } }, State { name: "top" when: (plasmoid.location === PlasmaCore.Types.TopEdge) AnchorChanges { target: appletFlow anchors{ top:parent.top; bottom:undefined; left:undefined; right:undefined;} } } ] //END states //BEGIN animations ///////Restore Zoom Animation///// ParallelAnimation{ id: restoreAnimation PropertyAnimation { target: wrapper property: "zoomScale" to: 1 duration: 4 * appletItem.animationTime easing.type: Easing.InCubic } PropertyAnimation { target: hiddenSpacerLeft property: "nScale" to: 0 duration: 4 * appletItem.animationTime easing.type: Easing.InCubic } PropertyAnimation { target: hiddenSpacerRight property: "nScale" to: 0 duration: 4 * appletItem.animationTime easing.type: Easing.InCubic } } /////Clicked Animation///// SequentialAnimation{ id: clickedAnimation alwaysRunToEnd: true running: (appletMouseArea.pressed || appletMouseAreaBottom.pressed) && (root.durationTime > 0) onStopped: { appletMouseArea.pressed = false; appletMouseAreaBottom.pressed = false; } ParallelAnimation{ PropertyAnimation { target: wrapper.clickedEffect property: "brightness" to: -0.35 duration: units.longDuration easing.type: Easing.OutQuad } } ParallelAnimation{ PropertyAnimation { target: wrapper.clickedEffect property: "brightness" to: 0 duration: units.longDuration easing.type: Easing.OutQuad } } } //END animations } diff --git a/containment/package/contents/ui/applet/indicator/Loader.qml b/containment/package/contents/ui/applet/indicator/Loader.qml new file mode 100644 index 00000000..61f07f47 --- /dev/null +++ b/containment/package/contents/ui/applet/indicator/Loader.qml @@ -0,0 +1,84 @@ +/* +* Copyright 2019 Michail Vourlakos +* +* This file is part of Latte-Dock +* +* Latte-Dock is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* Latte-Dock is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +import QtQuick 2.7 + +import org.kde.latte 0.2 as Latte + +Loader{ + id: indicatorLoader + anchors.fill: parent + + active: manager && manager.active && (isBackLayer || (!isBackLayer && indicatorProvidesFrontLayer)) + sourceComponent: manager.sourceComponent + + property bool isBackLayer: true + property Item manager + + //! Used when the indicators require more thickness in the view mask + //! e.g. when the Latte indicators are glowing in reverse order + Binding { + target: visibilityManager + property: "indicatorsExtraThickMask" + value: { + if (indicatorLoader.isBackLayer + && indicatorLoader.active + && indicatorLoader.item + && indicatorLoader.item.hasOwnProperty("extraMaskThickness")) { + return indicatorLoader.item.extraMaskThickness; + } + + return 0; + } + } + + //! Used when the indicators need icon colors in orde to be painted + //! properly, for example the Unity indicator + Binding { + target: appletItem + property: "indicatorNeedsIconColors" + value: { + if (indicatorLoader.isBackLayer + && indicatorLoader.active + && indicatorLoader.item + && indicatorLoader.item.hasOwnProperty("needsIconColors")) { + return indicatorLoader.item.needsIconColors; + } + + return false; + } + } + + //! Used when the indicators property also a front layer + //! to be drawn above the icon + Binding { + target: appletItem + property: "indicatorProvidesFrontLayer" + value: { + if (indicatorLoader.isBackLayer + && indicatorLoader.active + && indicatorLoader.item + && indicatorLoader.item.hasOwnProperty("providesFrontLayer")) { + return indicatorLoader.item.providesFrontLayer; + } + + return false; + } + } +} diff --git a/containment/package/contents/ui/applet/IndicatorLoader.qml b/containment/package/contents/ui/applet/indicator/Manager.qml similarity index 72% rename from containment/package/contents/ui/applet/IndicatorLoader.qml rename to containment/package/contents/ui/applet/indicator/Manager.qml index f52c1e2e..e42a25d5 100644 --- a/containment/package/contents/ui/applet/IndicatorLoader.qml +++ b/containment/package/contents/ui/applet/indicator/Manager.qml @@ -1,136 +1,104 @@ /* * Copyright 2019 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import QtQuick 2.7 import org.kde.latte 0.2 as Latte -Loader{ - id: indicatorLoader +Item{ + id: indicatorManager anchors.fill: parent - active: (root.indicatorsEnabled && appletItem.communicatorAlias.activeIndicatorEnabled && root.indicatorsForApplets) - || (!root.indicatorsForApplets && appletItem.communicatorAlias.overlayLatteIconIsActive) + readonly property bool active: (root.indicatorsEnabled && appletItem.communicatorAlias.activeIndicatorEnabled && root.indicatorsForApplets) + || (!root.indicatorsForApplets && appletItem.communicatorAlias.overlayLatteIconIsActive) - sourceComponent: { + readonly property Component sourceComponent: { if (!root.indicatorsForApplets && appletItem.communicatorAlias.overlayLatteIconIsActive) { return plasmaStyleIndicator; } switch (root.indicatorStyle) { case Latte.Types.LatteIndicator: return latteStyleIndicator; case Latte.Types.PlasmaIndicator: return plasmaStyleIndicator; case Latte.Types.UnityIndicator: return unityStyleIndicator; default: return latteStyleIndicator; }; } /* Indicators Properties in order use them*/ readonly property bool isTask: false readonly property bool isApplet: true readonly property bool isLauncher: false readonly property bool isStartup: false readonly property bool isWindow: false readonly property bool isActive: appletItem.isActive readonly property bool isGroup: false readonly property bool isMinimized: false readonly property bool inAttention: false readonly property bool inRemoving: false readonly property bool hasActive: isActive readonly property bool hasMinimized: false readonly property bool hasShown: false readonly property int windowsCount: 0 readonly property int windowsMinimizedCount: 0 readonly property int currentIconSize: root.iconSize readonly property int maxIconSize: root.maxIconSize readonly property int durationTime: root.durationTime readonly property real scaleFactor: appletItem.wrapperAlias.zoomScale readonly property color shadowColor: root.appShadowColorSolid readonly property bool dotsOnActive: root.dotsOnActive readonly property bool multiColorEnabled: root.threeColorsWindows readonly property bool reversedEnabled: root.reverseLinesPosition readonly property int activeIndicatorType: root.activeIndicatorType readonly property bool usePlasmaTabsStyle: !root.indicatorsForApplets //!glow options readonly property bool glowEnabled: root.showGlow readonly property int glowOption: root.glowOption readonly property real glowOpacity: root.glowOpacity readonly property bool glow3D: root.glow3D //!icon colors property color backgroundColor: appletItem.wrapperAlias.overlayIconLoader.backgroundColor property color glowColor: appletItem.wrapperAlias.overlayIconLoader.glowColor Component { id: latteStyleIndicator Latte.LatteIndicator{} } Component { id: plasmaStyleIndicator Latte.PlasmaIndicator{} } Component{ id:unityStyleIndicator Latte.UnityIndicator{} } - - //! Used when the indicators require more thickness in the view mask - //! e.g. when the Latte indicators are glowing in reverse order - Binding { - target: visibilityManager - property: "indicatorsExtraThickMask" - value: { - if (indicatorLoader.active - && indicatorLoader.item - && indicatorLoader.item.hasOwnProperty("extraMaskThickness")) { - return indicatorLoader.item.extraMaskThickness; - } - - return 0; - } - } - - //! Used when the indicators need icon colors in orde to be painted - //! properly, for example the Unity indicator - Binding { - target: appletItem - property: "indicatorNeedsIconColors" - value: { - if (indicatorLoader.active - && indicatorLoader.item - && indicatorLoader.item.hasOwnProperty("needsIconColors")) { - return indicatorLoader.item.needsIconColors; - } - - return false; - } - } } diff --git a/liblatte2/qml/indicators/UnityIndicator.qml b/liblatte2/qml/indicators/UnityIndicator.qml index 9a9b213b..0bfcd010 100644 --- a/liblatte2/qml/indicators/UnityIndicator.qml +++ b/liblatte2/qml/indicators/UnityIndicator.qml @@ -1,300 +1,322 @@ /* * Copyright 2019 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import QtQuick 2.0 import QtGraphicalEffects 1.0 import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.latte 0.2 as Latte import "../code/ColorizerTools.js" as ColorizerTools Item{ id: indicatorItem + property Item parentItem: parent.manager property Item rootItem: parent readonly property bool needsIconColors: true + readonly property bool providesFrontLayer: true readonly property int thickness: plasmoid.formFactor === PlasmaCore.Types.Vertical ? width : height - readonly property int freedThickness: (thickness - rectangleItem.size) / 2 - readonly property int shownWindows: rootItem.windowsCount - rootItem.windowsMinimizedCount - readonly property int maxDrawnMinimizedWindows: shownWindows > 0 ? Math.min(rootItem.windowsMinimizedCount,2) : 3 + readonly property int shownWindows: parentItem.windowsCount - parentItem.windowsMinimizedCount + readonly property int maxDrawnMinimizedWindows: shownWindows > 0 ? Math.min(parentItem.windowsMinimizedCount,2) : 3 readonly property real backColorBrightness: ColorizerTools.colorBrightness(theme.backgroundColor) readonly property color backgroundColor: backColorBrightness < 127 ? theme.backgroundColor : theme.textColor - Item{ - id: rectangleItem - width: rootItem.isTask ? Math.min(parent.width, parent.height) : parent.width - height: rootItem.isTask ? width : parent.height - anchors.centerIn: parent + //! Background Layer + Loader{ + id: backLayer + anchors.fill: parent + active: rootItem.isBackLayer + + sourceComponent: Item{ + Item{ + id: rectangleItem + width: parentItem.isTask ? Math.min(parent.width, parent.height) : parent.width + height: parentItem.isTask ? width : parent.height + anchors.centerIn: parent + + property bool isActive: parentItem.isActive || (parentItem.isWindow && parentItem.hasActive) + readonly property int size: Math.min(parent.width, parent.height) + + Rectangle { + id: unityRect + anchors.fill: parent + visible: parentItem.isActive || (parentItem.isWindow && parentItem.hasShown) + + radius: parentItem.currentIconSize / 12 + color: parentItem.backgroundColor + clip: true + } - property bool isActive: rootItem.isActive || (rootItem.isWindow && rootItem.hasActive) - readonly property int size: Math.min(parent.width, parent.height) + RadialGradient{ + id: glowGradient + anchors.verticalCenter: parent.top + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width - unityRect.anchors.margins * 2 - 1 + height: (width * 0.85) - unityRect.anchors.margins * 2 - 1 + visible: false + + gradient: Gradient { + GradientStop { position: 0.0; + color: { + if (parentItem.isMinimized) { + return "#aafcfcfc"; + } + + return parentItem.glowColor; + } + } + GradientStop { position: 0.6; color: "transparent" } + } + } - Rectangle { - id: unityRect - anchors.fill: parent - visible: rootItem.isActive || (rootItem.isWindow && rootItem.hasShown) + Item { + id: gradientMask + anchors.fill: glowGradient - radius: rootItem.currentIconSize / 12 - color: rootItem.backgroundColor - clip: true - } + Rectangle { + anchors.top: gradientMask.verticalCenter + anchors.topMargin: unityRect.anchors.margins + width: glowGradient.width + height: glowGradient.height / 2 + radius: unityRect.radius + } - RadialGradient{ - id: glowGradient - anchors.verticalCenter: parent.top - anchors.horizontalCenter: parent.horizontalCenter - width: parent.width - unityRect.anchors.margins * 2 - 1 - height: (width * 0.85) - unityRect.anchors.margins * 2 - 1 - visible: false - - gradient: Gradient { - GradientStop { position: 0.0; - color: { - if (rootItem.isMinimized) { - return "#aafcfcfc"; - } + visible: false + } + + OpacityMask { + anchors.fill: glowGradient + source: glowGradient + maskSource: gradientMask + visible: unityRect.visible || borderRectangle.visible + } - return rootItem.glowColor; + Rectangle { + id: borderRectangle + anchors.fill: parent + visible: (parentItem.isTask && parentItem.isWindow) || (parentItem.isApplet && parentItem.isActive) + color: "transparent" + border.width: 1 + border.color: "#303030" + radius: unityRect.radius + clip: true + + Rectangle { + anchors.fill: parent + anchors.margins: parent.border.width + radius: unityRect.radius + color: "transparent" + border.width: 1 + border.color: "#25dedede" } } - GradientStop { position: 0.6; color: "transparent" } } } + } - Item { - id: gradientMask - anchors.fill: glowGradient - Rectangle { - anchors.top: gradientMask.verticalCenter - anchors.topMargin: unityRect.anchors.margins - width: glowGradient.width - height: glowGradient.height / 2 - radius: unityRect.radius - } + //! Foreground Layer to draw Triangles + Loader{ + id: frontLayer + anchors.fill: parent + active: !rootItem.isBackLayer - visible: false - } + sourceComponent: Item { + anchors.fill: parent - OpacityMask { - anchors.fill: glowGradient - source: glowGradient - maskSource: gradientMask - visible: unityRect.visible || borderRectangle.visible - } + Row { + id: upperIndicators + spacing: 2 + readonly property bool alwaysActive: true + readonly property bool reversed: true - Rectangle { - id: borderRectangle - anchors.fill: parent - visible: (rootItem.isTask && rootItem.isWindow) || (rootItem.isApplet && rootItem.isActive) - color: "transparent" - border.width: 1 - border.color: "#303030" - radius: unityRect.radius - clip: true - - Rectangle { - anchors.fill: parent - anchors.margins: parent.border.width - radius: unityRect.radius - color: "transparent" - border.width: 1 - border.color: "#25dedede" + Repeater { + model: parentItem.isActive || parentItem.hasActive ? 1 : 0 + delegate: triangleComponent + } } - } - } - Row { - id: upperIndicators - spacing: 2 - readonly property bool alwaysActive: true - readonly property bool reversed: true + Grid { + id: lowerIndicators + rows: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? 1 : Math.min(3, parentItem.windowsCount) + columns: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? Math.min(3, parentItem.windowsCount) : 1 + rowSpacing: 2 + columnSpacing: 2 - Repeater { - model: rootItem.isActive || rootItem.hasActive ? 1 : 0 - delegate: triangleComponent - } - } + readonly property bool alwaysActive: false + readonly property bool reversed: false - Grid { - id: lowerIndicators - rows: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? 1 : Math.min(3, rootItem.windowsCount) - columns: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? Math.min(3, rootItem.windowsCount) : 1 - rowSpacing: 2 - columnSpacing: 2 + Repeater { + model: Math.min(3, parentItem.windowsCount) + delegate: triangleComponent + } + } - readonly property bool alwaysActive: false - readonly property bool reversed: false - Repeater { - model: Math.min(3, rootItem.windowsCount) - delegate: triangleComponent - } - } + //! Triangle Indicator Component + Component { + id: triangleComponent + Canvas { + id: canvas + width: parentItem.currentIconSize / 6 + height: width + + rotation: { + if (!parent.reversed) { + if (plasmoid.location === PlasmaCore.Types.BottomEdge) { + return 0; + } else if (plasmoid.location === PlasmaCore.Types.LeftEdge) { + return 90; + } else if (plasmoid.location === PlasmaCore.Types.TopEdge) { + return 180; + } else if (plasmoid.location === PlasmaCore.Types.RightEdge) { + return 270; + } + } else { + if (plasmoid.location === PlasmaCore.Types.BottomEdge) { + return 180; + } else if (plasmoid.location === PlasmaCore.Types.LeftEdge) { + return 270; + } else if (plasmoid.location === PlasmaCore.Types.TopEdge) { + return 0; + } else if (plasmoid.location === PlasmaCore.Types.RightEdge) { + return 90; + } + } + return 0; + } - //! Triangle Indicator Component - Component { - id: triangleComponent - Canvas { - id: canvas - width: rootItem.currentIconSize / 6 - height: width + property color drawColor: theme.buttonFocusColor; + property bool fillTriangle: { + if (!parent.alwaysActive && parentItem.windowsMinimizedCount!==0 + && ((index < maxDrawnMinimizedWindows) + || (parentItem.windowsCount === parentItem.windowsMinimizedCount))) { + return false; + } - rotation: { - if (!parent.reversed) { - if (plasmoid.location === PlasmaCore.Types.BottomEdge) { - return 0; - } else if (plasmoid.location === PlasmaCore.Types.LeftEdge) { - return 90; - } else if (plasmoid.location === PlasmaCore.Types.TopEdge) { - return 180; - } else if (plasmoid.location === PlasmaCore.Types.RightEdge) { - return 270; + return true; } - } else { - if (plasmoid.location === PlasmaCore.Types.BottomEdge) { - return 180; - } else if (plasmoid.location === PlasmaCore.Types.LeftEdge) { - return 270; - } else if (plasmoid.location === PlasmaCore.Types.TopEdge) { - return 0; - } else if (plasmoid.location === PlasmaCore.Types.RightEdge) { - return 90; + + readonly property int lineWidth: 2 + + onFillTriangleChanged: requestPaint(); + onDrawColorChanged: requestPaint(); + + onPaint: { + var ctx = getContext('2d'); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.strokeStyle = indicatorItem.backgroundColor; + ctx.lineWidth = lineWidth; + + ctx.beginPath(); + ctx.moveTo(0, canvas.height); + ctx.lineTo(canvas.width/2, 0); + ctx.lineTo(canvas.width, canvas.height); + ctx.lineTo(0, canvas.height); + ctx.closePath(); + ctx.stroke(); + + ctx.strokeStyle = drawColor; + ctx.fillStyle = fillTriangle ? drawColor : indicatorItem.backgroundColor; + + ctx.beginPath(); + ctx.moveTo(lineWidth, canvas.height - lineWidth); + ctx.lineTo(canvas.width/2, lineWidth); + ctx.lineTo(canvas.width - lineWidth, canvas.height - lineWidth); + ctx.lineTo(lineWidth, canvas.height - lineWidth); + ctx.closePath(); + ctx.stroke(); + ctx.fill(); } } - - return 0; } - property color drawColor: theme.buttonFocusColor; - property bool fillTriangle: { - if (!parent.alwaysActive && rootItem.windowsMinimizedCount!==0 - && ((index < maxDrawnMinimizedWindows) - || (rootItem.windowsCount === rootItem.windowsMinimizedCount))) { - return false; - } - - return true; - } + //! States + states: [ + State { + name: "bottom" + when: (plasmoid.location === PlasmaCore.Types.BottomEdge) - readonly property int lineWidth: 2 - - onFillTriangleChanged: requestPaint(); - onDrawColorChanged: requestPaint(); - - onPaint: { - var ctx = getContext('2d'); - ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.strokeStyle = indicatorItem.backgroundColor; - ctx.lineWidth = lineWidth; - - ctx.beginPath(); - ctx.moveTo(0, canvas.height); - ctx.lineTo(canvas.width/2, 0); - ctx.lineTo(canvas.width, canvas.height); - ctx.lineTo(0, canvas.height); - ctx.closePath(); - ctx.stroke(); - - ctx.strokeStyle = drawColor; - ctx.fillStyle = fillTriangle ? drawColor : indicatorItem.backgroundColor; - - ctx.beginPath(); - ctx.moveTo(lineWidth, canvas.height - lineWidth); - ctx.lineTo(canvas.width/2, lineWidth); - ctx.lineTo(canvas.width - lineWidth, canvas.height - lineWidth); - ctx.lineTo(lineWidth, canvas.height - lineWidth); - ctx.closePath(); - ctx.stroke(); - ctx.fill(); - } + AnchorChanges { + target: lowerIndicators + anchors{ top:undefined; bottom:parent.bottom; left:undefined; right:undefined; + horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} + } + AnchorChanges { + target: upperIndicators + anchors{ top:parent.top; bottom:undefined; left:undefined; right:undefined; + horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} + } + }, + State { + name: "top" + when: (plasmoid.location === PlasmaCore.Types.TopEdge) + + AnchorChanges { + target: lowerIndicators + anchors{ top:parent.top; bottom:undefined; left:undefined; right:undefined; + horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} + } + AnchorChanges { + target: upperIndicators + anchors{ top:undefined; bottom:parent.bottom; left:undefined; right:undefined; + horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} + } + }, + State { + name: "left" + when: (plasmoid.location === PlasmaCore.Types.LeftEdge) + + AnchorChanges { + target: lowerIndicators + anchors{ top:undefined; bottom:undefined; left:parent.left; right:undefined; + horizontalCenter:undefined; verticalCenter:parent.verticalCenter} + } + AnchorChanges { + target: upperIndicators + anchors{ top:undefined; bottom:undefined; left:undefined; right:parent.right; + horizontalCenter:undefined; verticalCenter:parent.verticalCenter} + } + }, + State { + name: "right" + when: (plasmoid.location === PlasmaCore.Types.RightEdge) + + AnchorChanges { + target: lowerIndicators + anchors{ top:undefined; bottom:undefined; left:undefined; right:parent.right; + horizontalCenter:undefined; verticalCenter:parent.verticalCenter} + } + AnchorChanges { + target: upperIndicators + anchors{ top:undefined; bottom:undefined; left:parent.left; right:undefined; + horizontalCenter:undefined; verticalCenter:parent.verticalCenter} + } + } + ] } } - - //! States - states: [ - State { - name: "bottom" - when: (plasmoid.location === PlasmaCore.Types.BottomEdge) - - AnchorChanges { - target: lowerIndicators - anchors{ top:undefined; bottom:parent.bottom; left:undefined; right:undefined; - horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} - } - AnchorChanges { - target: upperIndicators - anchors{ top:parent.top; bottom:undefined; left:undefined; right:undefined; - horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} - } - }, - State { - name: "top" - when: (plasmoid.location === PlasmaCore.Types.TopEdge) - - AnchorChanges { - target: lowerIndicators - anchors{ top:parent.top; bottom:undefined; left:undefined; right:undefined; - horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} - } - AnchorChanges { - target: upperIndicators - anchors{ top:undefined; bottom:parent.bottom; left:undefined; right:undefined; - horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} - } - }, - State { - name: "left" - when: (plasmoid.location === PlasmaCore.Types.LeftEdge) - - AnchorChanges { - target: lowerIndicators - anchors{ top:undefined; bottom:undefined; left:parent.left; right:undefined; - horizontalCenter:undefined; verticalCenter:parent.verticalCenter} - } - AnchorChanges { - target: upperIndicators - anchors{ top:undefined; bottom:undefined; left:undefined; right:parent.right; - horizontalCenter:undefined; verticalCenter:parent.verticalCenter} - } - }, - State { - name: "right" - when: (plasmoid.location === PlasmaCore.Types.RightEdge) - - AnchorChanges { - target: lowerIndicators - anchors{ top:undefined; bottom:undefined; left:undefined; right:parent.right; - horizontalCenter:undefined; verticalCenter:parent.verticalCenter} - } - AnchorChanges { - target: upperIndicators - anchors{ top:undefined; bottom:undefined; left:parent.left; right:undefined; - horizontalCenter:undefined; verticalCenter:parent.verticalCenter} - } - } - ] } diff --git a/plasmoid/package/contents/ui/task/TaskItem.qml b/plasmoid/package/contents/ui/task/TaskItem.qml index ac4cc764..f57aacbc 100644 --- a/plasmoid/package/contents/ui/task/TaskItem.qml +++ b/plasmoid/package/contents/ui/task/TaskItem.qml @@ -1,1525 +1,1540 @@ /* * Copyright 2016 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import QtQuick 2.0 import QtQuick.Layouts 1.1 import QtGraphicalEffects 1.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.private.taskmanager 0.1 as TaskManagerApplet import org.kde.latte 0.2 as Latte import "animations" as TaskAnimations +import "indicator" as Indicator MouseArea{ id: taskItem visible: false //true//(isStartup && root.durationTime !== 0) ? false : true anchors.bottom: (root.position === PlasmaCore.Types.BottomPositioned) ? parent.bottom : undefined anchors.top: (root.position === PlasmaCore.Types.TopPositioned) ? parent.top : undefined anchors.left: (root.position === PlasmaCore.Types.LeftPositioned) ? parent.left : undefined anchors.right: (root.position === PlasmaCore.Types.RightPositioned) ? parent.right : undefined objectName: "TaskItem" width: { if (!visible) return 0; if (isSeparator) return root.vertical ? root.iconSize + root.thickMargins : (root.dragSource || !root.parabolicEffectEnabled ? 5+root.lengthMargins : 0); if (root.vertical) { return wrapper.width; } else { return hiddenSpacerLeft.width+wrapper.width+hiddenSpacerRight.width; } } /*onWidthChanged: { console.log("T: " + itemIndex + " - " + launcherUrl + " - " + width + " _ "+ hiddenSpacerLeft.width + " _ " + wrapper.width + " _ " + hiddenSpacerRight.width); }*/ height: { if (!visible) return 0; if (isSeparator) return !root.vertical ? root.iconSize + root.thickMargins : (root.dragSource || !root.parabolicEffectEnabled ? 5+root.lengthMargins: 0); if (root.vertical) { return hiddenSpacerLeft.height + wrapper.height + hiddenSpacerRight.height; } else { return wrapper.height; } } acceptedButtons: Qt.LeftButton | Qt.MidButton | Qt.RightButton hoverEnabled: visible && (!inAnimation) && (!IsStartup) && (!root.taskInAnimation) &&(!inBouncingAnimation) && !isSeparator // hoverEnabled: false //opacity : isSeparator && (hiddenSpacerLeft.neighbourSeparator || hiddenSpacerRight.neighbourSeparator) ? 0 : 1 property bool buffersAreReady: false property bool delayingRemove: ListView.delayRemove property bool scalesUpdatedOnce: false //states that exist in windows in a Group of windows property bool hasActive: isActive property bool hasMinimized: (IsGroupParent === true) ? subWindows.hasMinimized : isMinimized property bool hasShown: (IsGroupParent === true) ? subWindows.hasShown : !isMinimized && isWindow property bool inAttention: isDemandingAttention && plasmoid.status === PlasmaCore.Types.RequiresAttentionStatus ? true : false /*animations flags*/ property bool inAnimation: true property bool inAddRemoveAnimation: true property bool inAttentionAnimation: false property bool inBlockingAnimation: false property bool inBouncingAnimation: false property bool inFastRestoreAnimation: false property bool inMimicParabolicAnimation: false property bool inNewWindowAnimation: false property real mimicParabolicScale: -1 property bool inPopup: false property bool inRemoveStage: false property bool indicatorNeedsIconColors: false + property bool indicatorProvidesFrontLayer: false property bool isAbleToShowPreview: true property bool isActive: (IsActive === true) ? true : false property bool isDemandingAttention: (IsDemandingAttention === true) ? true : false property bool isDragged: false property bool isGroupable: (IsGroupable === true) ? true : false property bool isGroupParent: (IsGroupParent === true) ? true : false property bool isForcedHidden: false property bool isLauncher: (IsLauncher === true) ? true : false property bool isMinimized: (IsMinimized === true) ? true : false property bool isSeparator: false property bool isStartup: (IsStartup === true) ? true : false property bool isWindow: (IsWindow === true) ? true : false property bool isZoomed: false property bool canPublishGeometries: (isWindow || isStartup || isGroupParent) && visible && width>=root.iconSize && height>=root.iconSize && !taskItem.delayingRemove && (wrapper.mScale===1 || wrapper.mScale===root.zoomFactor) //don't publish during zoomFactor property bool pressed: false property bool wheelIsBlocked: false property int animationTime: (animationsEnabled ? root.durationTime : 2) * (1.2 *units.shortDuration) property int badgeIndicator: 0 //it is used from external apps property int hoveredIndex: icList.hoveredIndex property int itemIndex: index property int lastValidIndex: -1 //used for the removal animation property int lastButtonClicked: -1; property int pressX: -1 property int pressY: -1 property int resistanceDelay: 450 property int spacersMaxSize: Math.max(0,Math.ceil(0.55*root.iconSize) - root.lengthMargins) property int windowsCount: subWindows.windowsCount property int windowsMinimizedCount: subWindows.windowsMinimized property string activity: tasksModel.activity readonly property var m: model readonly property int pid: model && model.AppPid ? model.AppPid : -1 readonly property string appName: model && model.AppName ? model.AppName : "" property string modelLauncherUrl: (LauncherUrlWithoutIcon && LauncherUrlWithoutIcon !== null) ? LauncherUrlWithoutIcon : "" property string modelLauncherUrlWithIcon: (LauncherUrl && LauncherUrl !== null) ? LauncherUrl : "" property string launcherUrl: "" property string launcherUrlWithIcon: "" property string launcherName: "" property Item tooltipVisualParent: wrapper.titleTooltipVisualParent property Item previewsVisualParent: wrapper.previewsTooltipVisualParent property Item wrapperAlias: wrapper onModelLauncherUrlChanged: { if (modelLauncherUrl !== ""){ launcherUrl = modelLauncherUrl; //!extract the launcherName if possible var nameStarts = launcherUrl.lastIndexOf("/"); if (nameStarts === -1){ nameStarts = launcherUrl.lastIndexOf(":"); } var nameEnds = launcherUrl.lastIndexOf(".desktop"); if (nameStarts!==-1 && nameEnds!==-1 && nameStarts 0 && !isLauncher readonly property bool playingAudio: hasAudioStream && audioStreams.some(function (item) { return !item.corked }) readonly property bool muted: hasAudioStream && audioStreams.every(function (item) { return item.muted }) readonly property int volume: { if (!hasAudioStream){ return 0; } var maxVolume = 0; for (var i=0; i maxVolume) maxVolume = audioStreams[i].volume; } return maxVolume; } ////// property QtObject contextMenu: null property QtObject draggingResistaner: null property QtObject hoveredTimerObj: null signal groupWindowAdded(); signal groupWindowRemoved(); signal checkWindowsStates(); Behavior on opacity { // NumberAnimation { duration: (IsStartup || (IsLauncher) ) ? 0 : 400 } NumberAnimation { duration: root.durationTime*units.longDuration } } SubWindows{ id: subWindows property int previousCount: 0 onWindowsCountChanged: { if (root.showWindowsOnlyFromLaunchers && !root.indicatorsEnabled) { return; } if ((windowsCount >= 2) && (windowsCount > previousCount) && !(taskItem.containsMouse || parabolicManager.neighbourIsHovered(itemIndex)) ){ if(root.dragSource == null) taskItem.groupWindowAdded(); } else if ((windowsCount >=1) &&(windowsCount < previousCount)){ //sometimes this is triggered in dragging with no reason if(root.dragSource == null && !taskItem.delayingRemove) taskItem.groupWindowRemoved(); } if (windowsCount>=1) { taskItem.slotPublishGeometries(); } //! workaround in order to update correctly the previousCount //! windowsCount can not return to zero because is such case //! the window task is removed and the launcher is added from //! libtaskmanager if (windowsCount>=1) { previousCount = windowsCount; } } } Loader { id: isSeparatorRectangle active: (opacityN>0) width: taskItem.width height: taskItem.height anchors.centerIn: separatorItem property real opacityN: isSeparator && root.contextMenu && root.contextMenu.visualParent === taskItem ? 1 : 0 Behavior on opacityN { NumberAnimation { duration: root.durationTime*units.longDuration } } sourceComponent: Rectangle{ anchors.fill: parent opacity: isSeparatorRectangle.opacityN radius: 3 property color tempColor: theme.highlightColor color: tempColor border.width: 1 border.color: theme.highlightColor onTempColorChanged: tempColor.a = 0.35; } } Item{ id:separatorItem anchors.centerIn: parent opacity: separatorShadow.active || forceHiddenState ? 0 : 0.4 visible: taskItem.isSeparator width: root.vertical ? root.iconSize : (root.dragSource || root.editMode) ? 5+root.lengthMargins: 1 height: !root.vertical ? root.iconSize : (root.dragSource || root.editMode) ? 5+root.lengthMargins: 1 property bool forceHiddenState: false Behavior on opacity { NumberAnimation { duration: root.durationTime*units.longDuration } } function updateForceHiddenState() { if (!isSeparator || root.editMode || root.dragSource) { forceHiddenState = false; } else { var firstPosition = (index>=0) && (index < parabolicManager.firstRealTaskIndex); var sepNeighbour = taskItem.hasNeighbourSeparator(index-1, false); var firstSepFromLastSeparatorsGroup = (index>=0) && (index > parabolicManager.lastRealTaskIndex); forceHiddenState = (firstPosition || sepNeighbour || firstSepFromLastSeparatorsGroup); } } Component.onCompleted: { updateForceHiddenState(); root.hiddenTasksUpdated.connect(updateForceHiddenState); } Component.onDestruction: { root.hiddenTasksUpdated.disconnect(updateForceHiddenState); } onForceHiddenStateChanged: root.separatorsUpdated(); Connections{ target: root onEditModeChanged: separatorItem.updateForceHiddenState(); onDragSourceChanged: separatorItem.updateForceHiddenState(); onSeparatorsUpdated: separatorItem.updateForceHiddenState(); //! During dock sliding-in because the parabolic effect isnt trigerred //! immediately but we wait first the dock to go to its final normal //! place we might miss the activation of the parabolic effect. //! By catching that signal we are trying to solve this. onDockIsShownCompletelyChanged: { if (dockIsShownCompletely && taskItem.containsMouse) { if (root.vertical) { taskItem.mousePosChanged(taskItem.mouseY); } else { taskItem.mousePosChanged(taskItem.mouseX); } } } onGlobalDirectRenderChanged:{ if (root.globalDirectRender && restoreAnimation.running) { // console.log("Cleat Task Scale !!!!"); restoreAnimation.stop(); } } onShowWindowsOnlyFromLaunchersChanged: { if (!root.editMode) { return; } taskItem.updateVisibilityBasedOnLaunchers(); } onInActivityChangeChanged: { if (root.showWindowsOnlyFromLaunchers && !root.inActivityChange) { taskItem.updateVisibilityBasedOnLaunchers(); } } } Rectangle { anchors.centerIn: parent width: root.vertical ? root.iconSize - 4 : 1 height: !root.vertical ? root.iconSize - 4 : 1 color: enforceLattePalette ? latteBridge.palette.textColor : theme.textColor } } ///Shadow in tasks Loader{ id: separatorShadow anchors.fill: separatorItem active: root.enableShadows && isSeparator opacity: separatorItem.forceHiddenState ? 0 : 0.4 Behavior on opacity { NumberAnimation { duration: root.durationTime*units.longDuration } } sourceComponent: DropShadow{ anchors.fill: parent color: root.appShadowColor fast: true samples: 2 * radius source: separatorItem radius: root.appShadowSize verticalOffset: 2 } } /* Rectangle{ anchors.fill: parent color: "transparent" border.width: 1 border.color: "blue" } */ Flow{ id: taskFlow width: parent.width height: parent.height // a hidden spacer for the first element to add stability // IMPORTANT: hidden spacers must be tested on vertical !!! HiddenSpacer{ id:hiddenSpacerLeft;} Item{ width: wrapper.width height: wrapper.height - IndicatorLoader{id: indicatorBackLayer} + Indicator.Manager{ + id: indicatorManager + } + + Indicator.Loader{ + id: indicatorBackLayer + manager: indicatorManager + } Wrapper{id: wrapper} + + Indicator.Loader{ + id: indicatorFrontLayer + manager: indicatorManager + isBackLayer: false + } } // a hidden spacer on the right for the last item to add stability HiddenSpacer{ id:hiddenSpacerRight; rightSpacer: true } }// Flow with hidden spacers inside /*Rectangle{ anchors.fill: taskFlow color: "transparent" border.width: 1 border.color: "blue" }*/ Component { id: taskInitComponent Timer { id: timer interval: 800 repeat: false onTriggered: { // taskItem.hoverEnabled = true; slotPublishGeometries(); if (latteView && latteView.debugModeTimers) { console.log("plasmoid timer: taskInitComponentTimer called..."); } timer.destroy(); } Component.onCompleted: timer.start() } } ////// Values Changes ///// //restore scales when there is no zoom factor for that item or //the mouse is out of the ListView // onItemIndexChanged: { // } onAppNameChanged: updateAudioStreams() onPidChanged: updateAudioStreams() onHasAudioStreamChanged: updateAudioStreams() onCanPublishGeometriesChanged: { if (canPublishGeometries) { slotPublishGeometries(); taskInitComponent.createObject(taskItem); } } onHoveredIndexChanged: { var distanceFromHovered = Math.abs(index - icList.hoveredIndex); /*if( (distanceFromHovered > 1) && (hoveredIndex !== -1)){ if(!isDragged) wrapper.mScale = 1; }*/ if (distanceFromHovered >= 1 && !inAttentionAnimation && !inFastRestoreAnimation && !inMimicParabolicAnimation) { hiddenSpacerLeft.nScale = 0; hiddenSpacerRight.nScale = 0; } } onItemIndexChanged: { if (isSeparator) { root.separatorsUpdated(); } if (itemIndex>=0) lastValidTimer.start(); } onLastValidIndexChanged: { if (lastValidIndex>=0 && lastValidIndex parabolicManager.lastRealTaskIndex)) { parabolicManager.updateTasksEdgesIndexes(); } } if (parabolicManager.hasInternalSeparator) { root.separatorsUpdated(); } } onIsDraggedChanged: { if(isDragged && (!root.editMode)){ root.dragSource = taskItem; dragHelper.startDrag(taskItem, model.MimeType, model.MimeData, model.LauncherUrlWithoutIcon, model.decoration); pressX = -1; pressY = -1; } } onIsMinimizedChanged: { checkWindowsStates(); } onIsActiveChanged: { checkWindowsStates(); } onIsForcedHiddenChanged: root.hiddenTasksUpdated(); onIsSeparatorChanged: { if (isSeparator) { root.separatorsUpdated(); if (parabolicManager.isLauncherToBeMoved(launcherUrl) && itemIndex>=0) { parabolicManager.moveLauncherToCorrectPos(launcherUrl, itemIndex); } } else { root.separatorsUpdated(); } } onLauncherUrlChanged: updateBadge(); ////// End of Values Changes ///// ///////////////// Mouse Area Events /////////////////// onEntered: { root.stopCheckRestoreZoomTimer(); if (restoreAnimation.running) { restoreAnimation.stop(); } // console.log("entered task:" + icList.hoveredIndex); if (icList.hoveredIndex === -1 && root.dockHoveredIndex ===-1) { root.startDirectRenderDelayerDuringEntering(); } if ((icList.hoveredIndex !== itemIndex) && isLauncher && windowsPreviewDlg.visible) { windowsPreviewDlg.hide(1); } if (!latteView || (latteView && !(latteView.dockIsHidden || latteView.inSlidingIn || latteView.inSlidingOut))){ icList.hoveredIndex = index; } if (root.latteView && !root.showPreviews && root.titleTooltips){ showTitleTooltip(); } //! show previews if enabled if(isAbleToShowPreview && ((root.showPreviews && windowsPreviewDlg.activeItem !== taskItem) || root.highlightWindows)){ if (hoveredTimerObj) { //! don't delay showing preview in normal states, //! that is when the dock wasn't hidden if (!hoveredTimerObj.running) { hoveredTimerObj.start(); } } else { if (!root.disableAllWindowsFunctionality) { hoveredTimerObj = hoveredTimerComponent.createObject(taskItem); } } } if (root.latteView && root.latteView.isHalfShown) { return; } } // IMPORTANT: This must be improved ! even for small milliseconds it reduces performance onExited: { scalesUpdatedOnce = false; isAbleToShowPreview = true; if (root.latteView && (!root.showPreviews || (root.showPreviews && isLauncher))){ root.latteView.hideTooltipLabel(); } if(taskItem.contextMenu && taskItem.contextMenu.status == PlasmaComponents.DialogStatus.Open){ ///don't check to restore zooms } else{ if(!inAnimation){ root.startCheckRestoreZoomTimer(); } } /* if(draggingResistaner != null){ draggingResistaner.destroy(); draggingResistaner = null; isDragged = false; }*/ } //! mouseX-Y values are delayed to be updated onEntered events and at the same time //! onPositionChanged signal may be delayed. we can fix this by don't delay at all //! when mouseX-Y is updated based on the plasmoid formFactor function mousePosChanged(mousePos) { if (mousePos<0 || (inBlockingAnimation && !(inAttentionAnimation||inFastRestoreAnimation||inMimicParabolicAnimation))) return; root.stopCheckRestoreZoomTimer(); if (root.latteView && root.latteView.isHalfShown) { return; } if((inAnimation == false)&&(!root.taskInAnimation)&&(!root.disableRestoreZoom) && hoverEnabled){ if (icList.hoveredIndex === -1 && root.dockHoveredIndex ===-1) { root.startDirectRenderDelayerDuringEntering(); } if (!latteView || (latteView && !(latteView.dockIsHidden || latteView.inSlidingIn || latteView.inSlidingOut))){ icList.hoveredIndex = index; } if (!root.globalDirectRender && !root.directRenderDelayerIsRunning) { root.setGlobalDirectRender(true); } if( ((wrapper.mScale == 1 || wrapper.mScale === root.zoomFactor) && !root.globalDirectRender) || root.globalDirectRender || !scalesUpdatedOnce) { if(root.dragSource == null){ var step = Math.abs(icList.currentSpot-mousePos); if (step >= root.animationStep){ icList.currentSpot = mousePos; wrapper.calculateScales(mousePos); } } } } } onMouseXChanged: { if (!root.vertical) { mousePosChanged(mouseX); } } onMouseYChanged: { if (root.vertical) { mousePosChanged(mouseY); } } onPositionChanged: { if ((inBlockingAnimation && !(inAttentionAnimation||inFastRestoreAnimation||inMimicParabolicAnimation))) return; if (root.latteView && root.latteView.isHalfShown) { return; } if((inAnimation == false)&&(!root.taskInAnimation)&&(!root.disableRestoreZoom) && hoverEnabled){ // mouse.button is always 0 here, hence checking with mouse.buttons if (pressX != -1 && mouse.buttons == Qt.LeftButton && isDragged && dragHelper.isDrag(pressX, pressY, mouse.x, mouse.y) ) { root.dragSource = taskItem; dragHelper.startDrag(taskItem, model.MimeType, model.MimeData, model.LauncherUrlWithoutIcon, model.decoration); pressX = -1; pressY = -1; } } } onContainsMouseChanged:{ if(!containsMouse && !inAnimation) { pressed=false; } ////disable hover effect/// if (isWindow && root.highlightWindows && !containsMouse) { root.windowsHovered( root.plasma515 ? model.WinIdList : model.LegacyWinIdList , false); } } onPressed: { //console.log("Pressed Task Delegate.."); if (Latte.WindowSystem.compositingActive && !Latte.WindowSystem.isPlatformWayland) { if(root.leftClickAction !== Latte.Types.PreviewWindows) { isAbleToShowPreview = false; windowsPreviewDlg.hide(2); } } var modAccepted = modifierAccepted(mouse); if ((mouse.button == Qt.LeftButton)||(mouse.button == Qt.MidButton) || modAccepted) { lastButtonClicked = mouse.button; pressed = true; pressX = mouse.x; pressY = mouse.y; if(draggingResistaner == null && !modAccepted) draggingResistaner = resistanerTimerComponent.createObject(taskItem); } else if (mouse.button == Qt.RightButton && !modAccepted){ // When we're a launcher, there's no window controls, so we can show all // places without the menu getting super huge. if (model.IsLauncher === true && !isSeparator) { showContextMenu({showAllPlaces: true}) } else { showContextMenu(); } } } onReleased: { //console.log("Released Task Delegate..."); if (draggingResistaner != null){ draggingResistaner.destroy(); draggingResistaner = null; } if(pressed && (!inBlockingAnimation || inAttentionAnimation) && !isSeparator){ if (modifierAccepted(mouse) && !root.disableAllWindowsFunctionality){ if( !taskItem.isLauncher){ if (root.modifierClickAction == Latte.Types.NewInstance) { tasksModel.requestNewInstance(modelIndex()); } else if (root.modifierClickAction == Latte.Types.Close) { tasksModel.requestClose(modelIndex()); } else if (root.modifierClickAction == Latte.Types.ToggleMinimized) { tasksModel.requestToggleMinimized(modelIndex()); } else if ( root.modifierClickAction == Latte.Types.CycleThroughTasks) { if (isGroupParent) subWindows.activateNextTask(); else activateTask(); } else if (root.modifierClickAction == Latte.Types.ToggleGrouping) { tasksModel.requestToggleGrouping(modelIndex()); } } else { activateTask(); } } else if (mouse.button == Qt.MidButton && !root.disableAllWindowsFunctionality){ if( !taskItem.isLauncher){ if (root.middleClickAction == Latte.Types.NewInstance) { tasksModel.requestNewInstance(modelIndex()); } else if (root.middleClickAction == Latte.Types.Close) { tasksModel.requestClose(modelIndex()); } else if (root.middleClickAction == Latte.Types.ToggleMinimized) { tasksModel.requestToggleMinimized(modelIndex()); } else if ( root.middleClickAction == Latte.Types.CycleThroughTasks) { if (isGroupParent) subWindows.activateNextTask(); else activateTask(); } else if (root.middleClickAction == Latte.Types.ToggleGrouping) { tasksModel.requestToggleGrouping(modelIndex()); } } else { activateTask(); } } else if (mouse.button == Qt.LeftButton){ if( !taskItem.isLauncher){ if (root.leftClickAction === Latte.Types.PresentWindows && !(isGroupParent && !Latte.WindowSystem.compositingActive)) { activateTask(); } else if (root.leftClickAction === Latte.Types.CycleThroughTasks) { if (isGroupParent) subWindows.activateNextTask(); else activateTask(); } else if (root.leftClickAction === Latte.Types.PreviewWindows || !Latte.WindowSystem.compositingActive) { if(windowsPreviewDlg.activeItem !== taskItem){ showPreviewWindow(); } else { hidePreviewWindow(); } } } else { activateTask(); } } backend.cancelHighlightWindows(); } pressed = false; if(!inAnimation) { startCheckRestoreZoomTimer(3*units.longDuration); } } onWheel: { if (isSeparator || !root.mouseWheelActions || wheelIsBlocked || inBouncingAnimation || (latteView && (latteView.dockIsHidden || latteView.inSlidingIn || latteView.inSlidingOut))){ return; } var angle = wheel.angleDelta.y / 8; wheelIsBlocked = true; scrollDelayer.start(); //positive direction if (angle > 12) { if (isLauncher || root.disableAllWindowsFunctionality) { wrapper.runLauncherAnimation(); } else if (isGroupParent) { subWindows.activateNextTask(); } else { var taskIndex = modelIndex(); if (isMinimized) { tasksModel.requestToggleMinimized(taskIndex); } tasksModel.requestActivate(taskIndex); } hidePreviewWindow(); //negative direction } else if (angle < -12) { if (isLauncher || root.disableAllWindowsFunctionality) { // do nothing } else if (isGroupParent) { subWindows.activatePreviousTask(); } else { var taskIndex = modelIndex(); if (isMinimized) { tasksModel.requestToggleMinimized(taskIndex); } tasksModel.requestActivate(taskIndex); } hidePreviewWindow(); } } //! A timer is needed in order to handle also touchpads that probably //! send too many signals very fast. This way the signals per sec are limited. //! The user needs to have a steady normal scroll in order to not //! notice a annoying delay Timer{ id: scrollDelayer interval: 400 onTriggered: taskItem.wheelIsBlocked = false; } ///////////////// End Of Mouse Area Events /////////////////// ///// Handlers for Signals ///// function animationStarted(){ // console.log("Animation started: " + index); inAnimation = true; } function animationEnded(){ // console.log("Animation ended: " + index); inAnimation = false; } function clearZoom(){ if(!root) return; if (root.hoveredIndex === -1 && root.dockHoveredIndex === -1) { restoreAnimation.start(); } } function handlerDraggingFinished(){ isDragged = false; } ///// End of Handlers ////// ///// Helper functions ///// function activateNextTask() { subWindows.activateNextTask(); } function activateTask() { if( taskItem.isLauncher || root.disableAllWindowsFunctionality){ if (Latte.WindowSystem.compositingActive) { wrapper.runLauncherAnimation(); } else { launcherAction(); } } else{ if (model.IsGroupParent) { if (Latte.WindowSystem.compositingActive && backend.canPresentWindows()) { root.presentWindows(root.plasma515 ? model.WinIdList: model.LegacyWinIdList ); } } else { if (IsMinimized === true) { var i = modelIndex(); tasksModel.requestToggleMinimized(i); tasksModel.requestActivate(i); } else if (IsActive === true) { tasksModel.requestToggleMinimized(modelIndex()); } else { tasksModel.requestActivate(modelIndex()); } } } } function hasNeighbourSeparator(ind, positive) { var cursor = ind; while (((!positive && cursor>=0) || (positive && cursor<=root.tasksCount-1)) && parabolicManager.taskIsForcedHidden(cursor) ) { cursor = positive ? cursor + 1 : cursor - 1; } return parabolicManager.taskIsSeparator(cursor); } function showPreviewWindow() { if (root.disableAllWindowsFunctionality || !isAbleToShowPreview) { return; } if(windowsPreviewDlg.activeItem !== taskItem){ if (!root.latteView || (root.latteView && !root.latteView.isHalfShown && !root.latteView.inSlidingIn && !root.latteView.inSlidingOut)) { if (root.latteView && root.titleTooltips) { root.latteView.hideTooltipLabel(); } taskItem.preparePreviewWindow(false); windowsPreviewDlg.show(taskItem); } } } function showTitleTooltip() { if (root.latteView && root.titleTooltips){ var displayText = isWindow ? model.display : model.AppName; var maxCharacters = 80; var fixedDisplayText = displayText.length>maxCharacters ? displayText.substring(0,maxCharacters-1) + "..." : displayText; root.latteView.showTooltipLabel(taskItem, fixedDisplayText); } } function hidePreviewWindow() { if(windowsPreviewDlg.activeItem === taskItem){ windowsPreviewDlg.hide("14.1"); if (root.latteView && root.titleTooltips && containsMouse) { showTitleTooltip(); } } } function preparePreviewWindow(hideClose){ windowsPreviewDlg.visualParent = previewsVisualParent; toolTipDelegate.parentTask = taskItem; toolTipDelegate.rootIndex = tasksModel.makeModelIndex(itemIndex, -1); toolTipDelegate.hideCloseButtons = hideClose; toolTipDelegate.appName = Qt.binding(function() { return model.AppName; }); if (!isLauncher) { toolTipDelegate.pidParent = Qt.binding(function() { return model.AppPid; }); } else { toolTipDelegate.pidParent = -1; } toolTipDelegate.windows = Qt.binding(function() { return root.plasma515 ? model.WinIdList : model.LegacyWinIdList ; }); toolTipDelegate.isGroup = Qt.binding(function() { return model.IsGroupParent == true; }); toolTipDelegate.icon = Qt.binding(function() { return model.decoration; }); toolTipDelegate.launcherUrl = Qt.binding(function() { return model.LauncherUrlWithoutIcon; }); toolTipDelegate.isLauncher = Qt.binding(function() { return model.IsLauncher == true; }); toolTipDelegate.isMinimizedParent = Qt.binding(function() { return model.IsMinimized == true; }); toolTipDelegate.displayParent = Qt.binding(function() { return model.display; }); toolTipDelegate.genericName = Qt.binding(function() { return model.GenericName; }); toolTipDelegate.virtualDesktopParent = Qt.binding(function() { return model.VirtualDesktop != undefined ? model.VirtualDesktop : 0; }); toolTipDelegate.isOnAllVirtualDesktopsParent = Qt.binding(function() { return model.IsOnAllVirtualDesktops == true; }); toolTipDelegate.activitiesParent = Qt.binding(function() { return model.Activities; }); } function launcherAction(){ // if ((lastButtonClicked == Qt.LeftButton)||(lastButtonClicked == Qt.MidButton)){ if (Latte.WindowSystem.compositingActive) { inBouncingAnimation = true; root.addWaitingLauncher(taskItem.launcherUrl); } if (root.disableAllWindowsFunctionality) { tasksModel.requestNewInstance(modelIndex()); } else { tasksModel.requestActivate(modelIndex()); } } ///window previews/// function generateSubText(task) { var subTextEntries = new Array(); if (!plasmoid.configuration.showOnlyCurrentDesktop && virtualDesktopInfo.numberOfDesktops > 1 && model.IsOnAllVirtualDesktops !== true && model.VirtualDesktop != -1 && model.VirtualDesktop != undefined) { subTextEntries.push(i18n("On %1", virtualDesktopInfo.desktopNames[model.VirtualDesktop - 1])); } if (model.Activities == undefined) { return subTextEntries.join("\n"); } if (model.Activities.length == 0 && activityInfo.numberOfRunningActivities > 1) { subTextEntries.push(i18nc("Which virtual desktop a window is currently on", "Available on all activities")); } else if (model.Activities.length > 0) { var activityNames = new Array(); for (var i = 0; i < model.Activities.length; i++) { var activity = model.Activities[i]; if (plasmoid.configuration.showOnlyCurrentActivity) { if (activity != activityInfo.currentActivity) { activityNames.push(activityInfo.activityName(model.Activities[i])); } } else if (activity != activityInfo.currentActivity) { activityNames.push(activityInfo.activityName(model.Activities[i])); } } if (plasmoid.configuration.showOnlyCurrentActivity) { if (activityNames.length > 0) { subTextEntries.push(i18nc("Activities a window is currently on (apart from the current one)", "Also available on %1", activityNames.join(", "))); } } else if (activityNames.length > 0) { subTextEntries.push(i18nc("Which activities a window is currently on", "Available on %1", activityNames.join(", "))); } } return subTextEntries.join("\n"); } ///window previews//// function modelIndex(){ return tasksModel.makeModelIndex(index); } function showContextMenu(args) { if (isSeparator && !root.editMode) return; if (!root.contextMenu) { contextMenu = root.createContextMenu(taskItem, modelIndex(), args); contextMenu.show(); } else { //! make sure that context menu isnt deleted multiple times and creates a crash //! bug case: 397635 var cMenu = root.contextMenu; root.contextMenu = null; cMenu.destroy(); } } function modifierAccepted(mouse){ if (mouse.modifiers & root.modifierQt){ if ((mouse.button === Qt.LeftButton && root.modifierClick === Latte.Types.LeftClick) || (mouse.button === Qt.MiddleButton && root.modifierClick === Latte.Types.MiddleClick) || (mouse.button === Qt.RightButton && root.modifierClick === Latte.Types.RightClick)) return true; } return false; } function setBlockingAnimation(value){ inBlockingAnimation = value; } function slotMimicEnterForParabolic(){ if (containsMouse) { if (inMimicParabolicAnimation) { mimicParabolicScale = root.zoomFactor; } wrapper.calculateScales(icList.currentSpot); } } function slotShowPreviewForTasks(group) { if (group === taskItem && !windowsPreviewDlg.visible) { preparePreviewWindow(true); windowsPreviewDlg.show(taskItem); } } function slotPublishGeometries() { //! this way we make sure that layouts that are in different activities that the current layout //! don't publish their geometries if ( canPublishGeometries && (!latteView || (latteView && currentLayout && latteView.universalLayoutManager && currentLayout.name === latteView.universalLayoutManager.currentLayoutName))) { var globalChoords = backend.globalRect(taskItem); //! Magic Lamp effect doesn't like coordinates outside the screen and //! width,heights of zero value... So we now normalize the geometries //! sent in order to avoid such circumstances if (root.vertical) { globalChoords.width = 1; globalChoords.height = Math.max(root.iconSize, taskItem.height); } else { globalChoords.height = 1; globalChoords.width = Math.max(root.iconSize, taskItem.width); } if (root.position === PlasmaCore.Types.BottomPositioned) { globalChoords.y = plasmoid.screenGeometry.y+plasmoid.screenGeometry.height-1; } else if (root.position === PlasmaCore.Types.TopPositioned) { globalChoords.y = plasmoid.screenGeometry.y+1; } else if (root.position === PlasmaCore.Types.LeftPositioned) { globalChoords.x = plasmoid.screenGeometry.x+1; } else if (root.position === PlasmaCore.Types.RightPositioned) { globalChoords.x = plasmoid.screenGeometry.x+plasmoid.screenGeometry.width - 1; } tasksModel.requestPublishDelegateGeometry(taskItem.modelIndex(), globalChoords, taskItem); } } function slotWaitingLauncherRemoved(launch) { if ((isWindow || isStartup || isLauncher) && !visible && launch === launcherUrl) { wrapper.mScale = 1; visible = true; } } function updateAudioStreams() { if (root.dragSource !== null) { audioStreams = []; return; } var pa = pulseAudio.item; if (!pa) { audioStreams = []; return; } var streams = pa.streamsForPid(taskItem.pid); if (streams.length) { pa.registerPidMatch(taskItem.appName); } else { // We only want to fall back to appName matching if we never managed to map // a PID to an audio stream window. Otherwise if you have two instances of // an application, one playing and the other not, it will look up appName // for the non-playing instance and erroneously show an indicator on both. if (!pa.hasPidMatch(taskItem.appName)) { var streams_result; streams_result = pa.streamsForAppName(taskItem.appName); if (streams_result.length===0 && launcherName !== "") { streams_result = pa.streamsForAppName(launcherName); } streams = streams_result; } } // fix a binding loop concerning audiostreams, the audiostreams // should be updated only when they have changed var changed = false; if (streams.length !== audioStreams.length) { changed = true; } else { for(var i=0; i= 0){ taskItem.lastValidIndex = taskItem.itemIndex; if (root.showWindowsOnlyFromLaunchers) { parabolicManager.updateTasksEdgesIndexes(); } } if (latteView && latteView.debugModeTimers) { console.log("plasmoid timer: lastValidTimer called..."); } } } ///Item's Removal Animation ListView.onRemove: TaskAnimations.RealRemovalAnimation{ id: taskRealRemovalAnimation } }// main Item diff --git a/plasmoid/package/contents/ui/task/indicator/Loader.qml b/plasmoid/package/contents/ui/task/indicator/Loader.qml new file mode 100644 index 00000000..bb383fc1 --- /dev/null +++ b/plasmoid/package/contents/ui/task/indicator/Loader.qml @@ -0,0 +1,96 @@ +/* +* Copyright 2019 Michail Vourlakos +* +* This file is part of Latte-Dock +* +* Latte-Dock is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* Latte-Dock is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +import QtQuick 2.7 + +import org.kde.plasma.core 2.0 as PlasmaCore + +import org.kde.latte 0.2 as Latte + +Loader { + id: indicatorLoader + anchors.bottom: (root.position === PlasmaCore.Types.BottomPositioned) ? parent.bottom : undefined + anchors.top: (root.position === PlasmaCore.Types.TopPositioned) ? parent.top : undefined + anchors.left: (root.position === PlasmaCore.Types.LeftPositioned) ? parent.left : undefined + anchors.right: (root.position === PlasmaCore.Types.RightPositioned) ? parent.right : undefined + + anchors.horizontalCenter: !root.vertical ? parent.horizontalCenter : undefined + anchors.verticalCenter: root.vertical ? parent.verticalCenter : undefined + + property bool isBackLayer: true + property Item manager + + active: manager && manager.active && (isBackLayer || (!isBackLayer && taskItem.indicatorProvidesFrontLayer)) + sourceComponent: manager.sourceComponent + + width: { + if (locked) { + return visualLockedWidth; + } + + return !root.vertical ? taskItem.wrapperAlias.width - 2*taskItem.wrapperAlias.mScale*root.lengthExtMargin : taskItem.wrapperAlias.width; + } + + height: { + if (locked) { + return visualLockedHeight; + } + + return root.vertical ? taskItem.wrapperAlias.height - 2*taskItem.wrapperAlias.mScale*root.lengthExtMargin : taskItem.wrapperAlias.height; + } + + readonly property bool locked: inAttentionAnimation || inNewWindowAnimation + + property real visualLockedWidth: root.iconSize + root.internalWidthMargins + property real visualLockedHeight: root.iconSize + root.internalHeightMargins + + //! Used when the indicators need icon colors in orde to be painted + //! properly, for example the Unity indicator + Binding { + target: taskItem + property: "indicatorNeedsIconColors" + value: { + if (indicatorLoader.isBackLayer + && indicatorLoader.active + && indicatorLoader.item + && indicatorLoader.item.hasOwnProperty("needsIconColors")) { + return indicatorLoader.item.needsIconColors; + } + + return false; + } + } + + //! Used when the indicators property also a front layer + //! to be drawn above the icon + Binding { + target: taskItem + property: "indicatorProvidesFrontLayer" + value: { + if (indicatorLoader.isBackLayer + && indicatorLoader.active + && indicatorLoader.item + && indicatorLoader.item.hasOwnProperty("providesFrontLayer")) { + return indicatorLoader.item.providesFrontLayer; + } + + return false; + } + } +} diff --git a/plasmoid/package/contents/ui/task/IndicatorLoader.qml b/plasmoid/package/contents/ui/task/indicator/Manager.qml similarity index 66% rename from plasmoid/package/contents/ui/task/IndicatorLoader.qml rename to plasmoid/package/contents/ui/task/indicator/Manager.qml index adcdd730..d65ee254 100644 --- a/plasmoid/package/contents/ui/task/IndicatorLoader.qml +++ b/plasmoid/package/contents/ui/task/indicator/Manager.qml @@ -1,144 +1,100 @@ /* * Copyright 2019 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import QtQuick 2.7 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.latte 0.2 as Latte -Loader { - id: indicatorLoader - anchors.bottom: (root.position === PlasmaCore.Types.BottomPositioned) ? parent.bottom : undefined - anchors.top: (root.position === PlasmaCore.Types.TopPositioned) ? parent.top : undefined - anchors.left: (root.position === PlasmaCore.Types.LeftPositioned) ? parent.left : undefined - anchors.right: (root.position === PlasmaCore.Types.RightPositioned) ? parent.right : undefined - - anchors.horizontalCenter: !root.vertical ? parent.horizontalCenter : undefined - anchors.verticalCenter: root.vertical ? parent.verticalCenter : undefined - - width: { - if (locked) { - return visualLockedWidth; - } - - return !root.vertical ? taskItem.wrapperAlias.width - 2*taskItem.wrapperAlias.mScale*root.lengthExtMargin : taskItem.wrapperAlias.width; - } - - height: { - if (locked) { - return visualLockedHeight; - } - - return root.vertical ? taskItem.wrapperAlias.height - 2*taskItem.wrapperAlias.mScale*root.lengthExtMargin : taskItem.wrapperAlias.height; - } - - active: root.indicatorsEnabled - +Item { + id: indicatorManager + readonly property bool active: root.indicatorsEnabled readonly property bool locked: inAttentionAnimation || inNewWindowAnimation - property real visualLockedWidth: root.iconSize + root.internalWidthMargins - property real visualLockedHeight: root.iconSize + root.internalHeightMargins - - /* Indicators Properties in order use them*/ + /* Indicators Properties in order for indicators to use them*/ readonly property bool isTask: true readonly property bool isApplet: false readonly property bool isLauncher: taskItem.isLauncher readonly property bool isStartup: taskItem.isStartup readonly property bool isWindow: taskItem.isWindow readonly property bool isActive: taskItem.hasActive || (root.showPreviews && windowsPreviewDlg.activeItem && (windowsPreviewDlg.activeItem === taskItem)) readonly property bool isGroup: taskItem.isGroupParent readonly property bool isMinimized: taskItem.isMinimized readonly property bool inAttention: taskItem.inAttention readonly property bool inRemoving: taskItem.inRemoveStage readonly property bool hasActive: taskItem.hasActive readonly property bool hasMinimized: taskItem.hasMinimized readonly property bool hasShown: taskItem.hasShown readonly property int windowsCount: taskItem.windowsCount readonly property int windowsMinimizedCount: taskItem.windowsMinimizedCount readonly property int currentIconSize: root.iconSize readonly property int maxIconSize: root.maxIconSize readonly property int durationTime: root.durationTime readonly property real scaleFactor: taskItem.wrapperAlias.mScale readonly property color shadowColor: root.appShadowColorSolid readonly property bool dotsOnActive: root.dotsOnActive readonly property bool multiColorEnabled: root.threeColorsWindows readonly property bool reversedEnabled: root.reverseLinesPosition readonly property int activeIndicatorType: root.activeIndicatorType readonly property bool usePlasmaTabsStyle: false //!glow options readonly property bool glowEnabled: root.showGlow readonly property int glowOption: root.glowOption readonly property real glowOpacity: root.glowOpacity readonly property bool glow3D: root.glow3D //!icon colors property color backgroundColor: taskItem.wrapperAlias.backgroundColor property color glowColor: taskItem.wrapperAlias.glowColor - sourceComponent: { + readonly property Component sourceComponent: { switch (root.indicatorStyle) { case Latte.Types.LatteIndicator: return latteIndicatorComponent; case Latte.Types.PlasmaIndicator: return plasmaIndicatorComponent; case Latte.Types.UnityIndicator: return unityIndicatorComponent; default: return latteIndicatorComponent; }; } Component{ id:latteIndicatorComponent Latte.LatteIndicator{} } Component{ id: plasmaIndicatorComponent Latte.PlasmaIndicator{} } Component{ id:unityIndicatorComponent Latte.UnityIndicator{} } - - //! Used when the indicators need icon colors in orde to be painted - //! properly, for example the Unity indicator - Binding { - target: taskItem - property: "indicatorNeedsIconColors" - value: { - if (indicatorLoader.active - && indicatorLoader.item - && indicatorLoader.item.hasOwnProperty("needsIconColors")) { - return indicatorLoader.item.needsIconColors; - } - - return false; - } - } }