diff --git a/containment/package/contents/ui/applet/AppletItem.qml b/containment/package/contents/ui/applet/AppletItem.qml index f0c5f50a..d4d4e705 100644 --- a/containment/package/contents/ui/applet/AppletItem.qml +++ b/containment/package/contents/ui/applet/AppletItem.qml @@ -1,896 +1,914 @@ /* * 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 "indicators" as Indicators Item { id: appletItem visible: false width: isInternalViewSplitter && !root.editMode ? 0 : (root.isHorizontal ? computeWidth : computeWidth + shownAppletMargin) height: isInternalViewSplitter && !root.editMode ? 0 : (root.isVertical ? computeHeight : computeHeight + shownAppletMargin) 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.parabolicEffectEnabled property bool showZoomed: false property bool lockZoom: false + property bool isActive: (isExpanded && !isSystray + && 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" property bool isSpacer: latteSpacer !== null 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: root.durationTime* (1.2 *units.shortDuration) // 70 property int hoveredIndex: layoutsContainer.hoveredIndex property int index: -1 property int appletMargin: (applet && (applet.pluginName === root.plasmoidName)) || isInternalViewSplitter || root.reverseLinesPosition ? 0 : root.statesLineSize property int maxWidth: root.isHorizontal ? root.height : root.width property int maxHeight: root.isHorizontal ? root.height : root.width property int shownAppletMargin: isSystray ? 0 : appletMargin 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.5*root.iconSize) - root.iconMargin) 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 latteSpacer: applet && (applet.pluginName === "org.kde.latte.spacer") ? (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 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) && root.isHorizontal) || (applet && (applet.Layout.minimumHeight > maxSize) && root.isVertical)) && (applet && applet.pluginName !== "org.kde.plasma.panelspacer" && (applet.pluginName !== "org.kde.latte.spacer")) && !communicator.canShowOverlaiedLatteIcon) || (isSystray) || (appletItem.needsFillSpace) ) { canBeHovered = false; } else{ 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); } } onLatteSpacerChanged: { if(appletItem.latteSpacer){ latteSpacer.latteDock = root; //appletItem.lockZoom = true; } } onNeedsFillSpaceChanged: checkCanBeHovered(); onShowZoomedChanged: { if(showZoomed){ //var newZ = appletItem.maxHeight / root.iconSize; //wrapper.zoomScale = newZ; wrapper.zoomScale = 1; } else{ wrapper.zoomScale = 1; } } 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 anchors.rightMargin: (latteApplet || (showZoomed && root.editMode)) || (plasmoid.location !== PlasmaCore.Types.RightEdge) ? 0 : shownAppletMargin anchors.leftMargin: (latteApplet || (showZoomed && root.editMode)) || (plasmoid.location !== PlasmaCore.Types.LeftEdge) ? 0 : shownAppletMargin anchors.topMargin: (latteApplet || (showZoomed && root.editMode)) || (plasmoid.location !== PlasmaCore.Types.TopEdge)? 0 : shownAppletMargin anchors.bottomMargin: (latteApplet || (showZoomed && root.editMode)) || (plasmoid.location !== PlasmaCore.Types.BottomEdge) ? 0 : shownAppletMargin // 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 + //! Active Indicator loader + Loader{ + anchors.fill: parent + active: (root.activeIndicator === Latte.Types.AllIndicator + || (root.activeIndicator === Latte.Types.InternalsIndicator && communicator.overlayLatteIconIsActive)) + && communicator.activeIndicatorEnabled + && !(root.indicatorStyle === Latte.Types.UnityIndicator && !communicator.overlayLatteIconIsActive) + + + sourceComponent: root.indicatorStyle===Latte.Types.LatteIndicator ? + latteStyleIndicator : (root.indicatorStyle===Latte.Types.PlasmaIndicator ? plasmaStyleIndicator : unityStyleIndicator) + + Component { + id: latteStyleIndicator + + Item{ + anchors.fill: parent + Indicators.LatteIndicator{} + } + } + + Component { + id: plasmaStyleIndicator + + Indicators.PlasmaIndicator { + anchors.fill: parent + } + } + + Component{ + id:unityStyleIndicator + Indicators.UnityIndicator{ + anchors.fill: parent + backgroundColor: wrapper.overlayIconLoader.backgroundColor + glowColor: wrapper.overlayIconLoader.glowColor + } + } + } + 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 } } } - //! Active Indicator loader - Loader{ - anchors.fill: parent - active: (root.activeIndicator === Latte.Types.AllIndicator - || (root.activeIndicator === Latte.Types.InternalsIndicator && communicator.overlayLatteIconIsActive)) - && communicator.activeIndicatorEnabled - - sourceComponent: root.indicatorStyle===Latte.Types.PlasmaIndicator ? plasmaStyleIndicator : latteStyleIndicator - - Component { - id: latteStyleIndicator - Item{ - anchors.fill: parent - Indicators.LatteIndicator{} - } - } - - Component { - id: plasmaStyleIndicator - Item{ - anchors.fill: parent - Indicators.PlasmaIndicator {} - } - } - } - //! 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 && !root.editMode && !lockZoom && communicator.parabolicEffectEnabled hoverEnabled: !root.editMode && (!latteApplet) ? true : false 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.parabolicEffectEnabled && canBeHovered && !(appletItem.isSeparator && !root.editMode) property bool blockWheel: false property bool pressed: false onClicked: { pressed = false; mouse.accepted = false; } onEntered: { if (containsMouse && !appletItem.lockZoom && communicator.parabolicEffectEnabled && appletItem.canBeHovered){ root.stopCheckRestoreZoomTimer(); } if (restoreAnimation.running) { restoreAnimation.stop(); } if (!root.editMode && !(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.parabolicEffectEnabled || !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/ItemWrapper.qml b/containment/package/contents/ui/applet/ItemWrapper.qml index 426c3a2a..18dc5173 100644 --- a/containment/package/contents/ui/applet/ItemWrapper.qml +++ b/containment/package/contents/ui/applet/ItemWrapper.qml @@ -1,736 +1,757 @@ /* * 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.latte 0.2 as Latte import "../../code/MathTools.js" as MathTools Item{ id: wrapper width: { if (appletItem.isInternalViewSplitter && !root.editMode) return 0; if (appletItem.isSeparator && !root.editMode) { if (!root.isVertical) return -1; else return root.iconSize; } //! width for applets that use fillWidth/fillHeight such plasma taskmanagers and AWC if (appletItem.needsFillSpace && root.isHorizontal) { if (root.panelAlignment !== Latte.Types.Justify) { var maximumValue = (applet.Layout.maximumWidth === Infinity) || applet.Layout.maximumWidth === -1 ? appletItem.sizeForFill : Math.min(appletItem.sizeForFill, applet.Layout.maximumWidth); var constrainedWidth = MathTools.bound(applet.Layout.minimumWidth, applet.Layout.preferredWidth, maximumValue); return root.editMode ? Math.max(constrainedWidth, root.iconSize) : constrainedWidth; } if(appletItem.sizeForFill>-1){ return appletItem.sizeForFill; } } if (appletItem.latteApplet) { //! commented because it was breaking the context menu available area, I don't remember where //! we needed this... // if (appletItem.showZoomed && root.isVertical) // return root.statesLineSize + root.thickMargin + root.iconSize + 1; //else return latteApplet.tasksWidth; } else { return scaledWidth; } } height: { if (appletItem.isInternalViewSplitter && !root.editMode) return 0; if (appletItem.isSeparator && !root.editMode) { if (root.isVertical) return -1; else return root.iconSize; } //! height for applets that use fillWidth/fillHeight such plasma taskmanagers and AWC if (appletItem.needsFillSpace && root.isVertical) { if (root.panelAlignment !== Latte.Types.Justify) { var maximumValue = (applet.Layout.maximumHeight === Infinity) || applet.Layout.maximumHeight === -1 ? appletItem.sizeForFill : Math.min(appletItem.sizeForFill, applet.Layout.maximumHeight); var constrainedHeight = MathTools.bound(applet.Layout.minimumHeight, applet.Layout.preferredHeight, maximumValue); return root.editMode ? Math.max(constrainedHeight, root.iconSize) : constrainedHeight; } if (appletItem.sizeForFill>-1){ return appletItem.sizeForFill; } } if (appletItem.latteApplet) { //! commented because it was breaking the context menu available area, I don't remember where //! we needed this... //if (appletItem.showZoomed && root.isHorizontal) // return root.statesLineSize + root.thickMargin + root.iconSize + 1; // else return latteApplet.tasksHeight; } else { return scaledHeight; } } opacity: appletColorizer.mustBeShown ? 0 : 1 //width: appletItem.isInternalViewSplitter && !root.editMode ? 0 : Math.round( latteApplet ? ((appletItem.showZoomed && root.isVertical) ? // scaledWidth : latteApplet.tasksWidth) : scaledWidth ) //height: appletItem.isInternalViewSplitter&& !root.editMode ? 0 : Math.round( latteApplet ? ((appletItem.showZoomed && root.isHorizontal) ? // scaledHeight : latteApplet.tasksHeight ): scaledHeight ) property bool disableScaleWidth: false property bool disableScaleHeight: false property bool editMode: root.editMode property int appletMinimumWidth: applet && applet.Layout ? applet.Layout.minimumWidth : -1 property int appletMinimumHeight: applet && applet.Layout ? applet.Layout.minimumHeight : -1 property int appletPreferredWidth: applet && applet.Layout ? applet.Layout.preferredWidth : -1 property int appletPreferredHeight: applet && applet.Layout ? applet.Layout.preferredHeight : -1 property int appletMaximumWidth: applet && applet.Layout ? applet.Layout.maximumWidth : -1 property int appletMaximumHeight: applet && applet.Layout ? applet.Layout.maximumHeight : -1 property int iconSize: root.iconSize property int marginWidth: root.isVertical ? (appletItem.isSystray ? root.thickMarginBase : root.thickMargin ) : (root.inFullJustify && (appletItem.firstChildOfStartLayout || appletItem.lastChildOfEndLayout ) ? 0 : root.iconMargin) //Fitt's Law property int marginHeight: root.isHorizontal ? (appletItem.isSystray ? root.thickMarginBase : root.thickMargin ) : (root.inFullJustify && (appletItem.firstChildOfStartLayout || appletItem.lastChildOfEndLayout ) ? 0 : root.iconMargin) //Fitt's Law property real scaledWidth: zoomScaleWidth * (layoutWidth + marginWidth) property real scaledHeight: zoomScaleHeight * (layoutHeight + marginHeight) property real zoomScaleWidth: disableScaleWidth ? 1 : zoomScale property real zoomScaleHeight: disableScaleHeight ? 1 : zoomScale property int layoutWidthResult: 0 property int layoutWidth property int layoutHeight // property int localMoreSpace: root.reverseLinesPosition ? root.statesLineSize + 2 : appletMargin property int localMoreSpace: appletMargin property int moreHeight: (appletItem.isSystray || root.reverseLinesPosition) && root.isHorizontal ? localMoreSpace : 0 property int moreWidth: (appletItem.isSystray || root.reverseLinesPosition) && root.isVertical ? localMoreSpace : 0 property real center:(width + hiddenSpacerLeft.separatorSpace + hiddenSpacerRight.separatorSpace) / 2 property real zoomScale: 1 property int index: appletItem.index property Item wrapperContainer: _wrapperContainer property Item clickedEffect: _clickedEffect property Item containerForOverlayIcon: _containerForOverlayIcon + property Item overlayIconLoader: _overlayIconLoader Behavior on opacity { NumberAnimation { duration: 0.8 * root.animationTime easing.type: Easing.OutCubic } } // property int pHeight: applet ? applet.Layout.preferredHeight : -10 /*function debugLayouts(){ if(applet){ console.log("---------- "+ applet.pluginName +" ----------"); console.log("MinW "+applet.Layout.minimumWidth); console.log("PW "+applet.Layout.preferredWidth); console.log("MaxW "+applet.Layout.maximumWidth); console.log("FillW "+applet.Layout.fillWidth); console.log("-----"); console.log("MinH "+applet.Layout.minimumHeight); console.log("PH "+applet.Layout.preferredHeight); console.log("MaxH "+applet.Layout.maximumHeight); console.log("FillH "+applet.Layout.fillHeight); console.log("-----"); console.log("LayoutW: " + layoutWidth); console.log("LayoutH: " + layoutHeight); } } onLayoutWidthChanged: { debugLayouts(); } onLayoutHeightChanged: { debugLayouts(); }*/ onAppletMinimumWidthChanged: { if(zoomScale == 1) checkCanBeHovered(); updateLayoutWidth(); } onAppletMinimumHeightChanged: { if(zoomScale == 1) checkCanBeHovered(); updateLayoutHeight(); } onAppletPreferredWidthChanged: updateLayoutWidth(); onAppletPreferredHeightChanged: updateLayoutHeight(); onAppletMaximumWidthChanged: updateLayoutWidth(); onAppletMaximumHeightChanged: updateLayoutHeight(); onIconSizeChanged: { updateLayoutWidth(); updateLayoutHeight(); } onEditModeChanged: { updateLayoutWidth(); updateLayoutHeight(); } onZoomScaleChanged: { if ((zoomScale === root.zoomFactor) && !root.globalDirectRender) { root.setGlobalDirectRender(true); } if ((zoomScale > 1) && !appletItem.isZoomed) { appletItem.isZoomed = true; if (!root.editMode && !animationWasSent) { root.slotAnimationsNeedBothAxis(1); animationWasSent = true; } } else if ((zoomScale == 1) && appletItem.isZoomed) { appletItem.isZoomed = false; if (animationWasSent) { root.slotAnimationsNeedBothAxis(-1); animationWasSent = false; } } } Connections { target: root onIsVerticalChanged: { if (appletItem.latteApplet) { return; } wrapper.disableScaleWidth = false; wrapper.disableScaleHeight = false; if (root.isVertical) { wrapper.updateLayoutHeight(); wrapper.updateLayoutWidth(); } else { wrapper.updateLayoutWidth(); wrapper.updateLayoutHeight(); } } } function updateLayoutHeight(){ if (appletItem.needsFillSpace && root.isVertical) { layoutsContainer.updateSizeForAppletsInFill(); return; } if (isLattePlasmoid) { return; } else if (appletItem.isInternalViewSplitter){ if(!root.editMode) layoutHeight = 0; else layoutHeight = root.iconSize + moreHeight + root.statesLineSize; } else if(applet && applet.pluginName === "org.kde.plasma.panelspacer"){ layoutHeight = root.iconSize + moreHeight; } else if(appletItem.isSystray && root.isHorizontal){ layoutHeight = root.statesLineSize + root.iconSize; } else{ if(applet && (applet.Layout.minimumHeight > root.iconSize) && root.isVertical && !canBeHovered && !communicator.overlayLatteIconIsActive){ layoutHeight = applet.Layout.minimumHeight; } //it is used for plasmoids that need to scale only one axis... e.g. the Weather Plasmoid else if(applet && ( applet.Layout.maximumHeight < root.iconSize || applet.Layout.preferredHeight > root.iconSize || appletItem.lockZoom) && root.isVertical && !disableScaleWidth && !communicator.overlayLatteIconIsActive) { if (!appletItem.isSpacer) { disableScaleHeight = true; } //this way improves performance, probably because during animation the preferred sizes update a lot if((applet.Layout.maximumHeight < root.iconSize)){ layoutHeight = applet.Layout.maximumHeight; } else if (applet.Layout.minimumHeight > root.iconSize){ layoutHeight = applet.Layout.minimumHeight; } else if ((applet.Layout.preferredHeight > root.iconSize) || (appletItem.lockZoom && applet.Layout.preferredHeight > 0 )){ layoutHeight = applet.Layout.preferredHeight; } else{ layoutHeight = root.iconSize + moreHeight; } } else { layoutHeight = root.iconSize + moreHeight; } } } function updateLayoutWidth(){ if (appletItem.needsFillSpace && root.isHorizontal) { layoutsContainer.updateSizeForAppletsInFill(); return; } if (isLattePlasmoid) { return; } else if (appletItem.isInternalViewSplitter){ if(!root.editMode) layoutWidth = 0; else layoutWidth = root.iconSize + moreWidth + root.statesLineSize; } else if(applet && applet.pluginName === "org.kde.plasma.panelspacer"){ layoutWidth = root.iconSize + moreWidth; } else if(appletItem.isSystray && root.isVertical){ layoutWidth = root.statesLineSize + root.iconSize; } else{ if(applet && (applet.Layout.minimumWidth > root.iconSize) && root.isHorizontal && !canBeHovered && !communicator.overlayLatteIconIsActive){ layoutWidth = applet.Layout.minimumWidth; } //it is used for plasmoids that need to scale only one axis... e.g. the Weather Plasmoid else if(applet && ( applet.Layout.maximumWidth < root.iconSize || applet.Layout.preferredWidth > root.iconSize || appletItem.lockZoom) && root.isHorizontal && !disableScaleHeight && !communicator.overlayLatteIconIsActive){ if (!appletItem.isSpacer) { disableScaleWidth = true; } //this way improves performance, probably because during animation the preferred sizes update a lot if((applet.Layout.maximumWidth < root.iconSize)){ // return applet.Layout.maximumWidth; layoutWidth = applet.Layout.maximumWidth; } else if (applet.Layout.minimumWidth > root.iconSize){ layoutWidth = applet.Layout.minimumWidth; } else if ((applet.Layout.preferredWidth > root.iconSize) || (appletItem.lockZoom && applet.Layout.preferredWidth > 0 )){ layoutWidth = applet.Layout.preferredWidth; } else{ layoutWidth = root.iconSize + moreWidth; } } else{ layoutWidth = root.iconSize + moreWidth; } } } Item{ id:_wrapperContainer width:{ if (appletItem.needsFillSpace && (appletItem.sizeForFill>-1) && root.isHorizontal){ return wrapper.width; } if (appletItem.isInternalViewSplitter) { return wrapper.layoutWidth; } else { if (plasmoid.formFactor === PlasmaCore.Types.Vertical) { return parent.zoomScaleWidth * (root.iconSize + root.thickMarginBase + root.thickMarginHigh); } else { return parent.zoomScaleWidth * wrapper.layoutWidth; } } } height:{ if (appletItem.needsFillSpace && (appletItem.sizeForFill>-1) && root.isVertical){ return wrapper.height; } if (appletItem.isInternalViewSplitter) { return wrapper.layoutHeight; } else { if (plasmoid.formFactor === PlasmaCore.Types.Horizontal) { return parent.zoomScaleHeight * (root.iconSize + root.thickMarginBase + root.thickMarginHigh); } else { return parent.zoomScaleHeight * wrapper.layoutHeight; } } } //width: Math.round( appletItem.isInternalViewSplitter ? wrapper.layoutWidth : parent.zoomScaleWidth * wrapper.layoutWidth ) //height: Math.round( appletItem.isInternalViewSplitter ? wrapper.layoutHeight : parent.zoomScaleHeight * wrapper.layoutHeight ) anchors.rightMargin: plasmoid.location === PlasmaCore.Types.RightEdge ? lowThickUsed : 0 anchors.leftMargin: plasmoid.location === PlasmaCore.Types.LeftEdge ? lowThickUsed : 0 anchors.topMargin: plasmoid.location === PlasmaCore.Types.TopEdge ? lowThickUsed : 0 anchors.bottomMargin: plasmoid.location === PlasmaCore.Types.BottomEdge ? lowThickUsed : 0 opacity: appletShadow.active ? 0 : 1 property int lowThickUsed: 0 //root.thickMarginBase //BEGIN states states: [ State { name: "left" when: (plasmoid.location === PlasmaCore.Types.LeftEdge) AnchorChanges { target: _wrapperContainer anchors{ verticalCenter:wrapper.verticalCenter; horizontalCenter:undefined; top:undefined; bottom:undefined; left:parent.left; right:undefined;} } }, State { name: "right" when: (plasmoid.location === PlasmaCore.Types.RightEdge) AnchorChanges { target: _wrapperContainer anchors{ verticalCenter:wrapper.verticalCenter; horizontalCenter:undefined; top:undefined; bottom:undefined; left:undefined; right:parent.right;} } }, State { name: "bottom" when: (plasmoid.location === PlasmaCore.Types.BottomEdge) AnchorChanges { target: _wrapperContainer anchors{ verticalCenter:undefined; horizontalCenter:wrapper.horizontalCenter; top:undefined; bottom:parent.bottom; left:undefined; right:undefined;} } }, State { name: "top" when: (plasmoid.location === PlasmaCore.Types.TopEdge) AnchorChanges { target: _wrapperContainer anchors{ verticalCenter:undefined; horizontalCenter:wrapper.horizontalCenter; top:parent.top; bottom:undefined; left:undefined; right:undefined;} } } ] //END states ///Secret MouseArea to be used by the folder widget Loader{ anchors.fill: parent active: communicator.overlayLatteIconIsActive && applet.pluginName === "org.kde.plasma.folder" sourceComponent: MouseArea{ onClicked: latteView.toggleAppletExpanded(applet.id); } } Item{ id: _containerForOverlayIcon anchors.fill: parent } Loader{ + id: _overlayIconLoader anchors.fill: parent active: communicator.overlayLatteIconIsActive + + property color backgroundColor: "black" + property color glowColor: "white" + sourceComponent: Latte.IconItem{ id: overlayIconItem anchors.fill: parent source: { if (communicator.appletIconItemIsShown()) return communicator.appletIconItem.source; else if (communicator.appletImageItemIsShown()) return communicator.appletImageItem.source; + + return ""; } + providesColors: root.indicatorStyle === Latte.Types.UnityIndicator && source != "" usesPlasmaTheme: communicator.appletIconItemIsShown() ? communicator.appletIconItem.usesPlasmaTheme : false + Binding{ + target: _overlayIconLoader + property: "backgroundColor" + when: overlayIconItem.providesColors + value: overlayIconItem.backgroundColor + } + + Binding{ + target: _overlayIconLoader + property: "glowColor" + when: overlayIconItem.providesColors + value: overlayIconItem.glowColor + } + Loader{ anchors.centerIn: parent active: root.debugModeOverloadedIcons sourceComponent: Rectangle{ width: 30 height: 30 color: "green" opacity: 0.65 } } - - //ActiveIndicator{} } } } //spacer background Loader{ anchors.fill: _wrapperContainer active: applet && (applet.pluginName === "org.kde.plasma.panelspacer") && root.editMode sourceComponent: Rectangle{ anchors.fill: parent border.width: 1 border.color: theme.textColor color: "transparent" opacity: 0.7 radius: root.iconMargin Rectangle{ anchors.centerIn: parent color: parent.border.color width: parent.width - 1 height: parent.height - 1 opacity: 0.2 } } } Loader{ anchors.fill: _wrapperContainer active: appletItem.isInternalViewSplitter && root.editMode rotation: root.isVertical ? 90 : 0 sourceComponent: PlasmaCore.SvgItem{ id:splitterImage anchors.fill: parent svg: PlasmaCore.Svg{ imagePath: root.universalSettings.splitterIconPath() } layer.enabled: true layer.effect: DropShadow { radius: root.appShadowSize fast: true samples: 2 * radius color: root.appShadowColor verticalOffset: 2 } Component.onCompleted: { if (root.isVertical) { wrapper.updateLayoutHeight(); wrapper.updateLayoutWidth(); } else { wrapper.updateLayoutWidth(); wrapper.updateLayoutHeight(); } } } } ///Shadow in applets Loader{ id: appletShadow anchors.fill: appletItem.appletWrapper active: appletItem.applet && !appletColorizer.mustBeShown && (((plasmoid.configuration.shadows === 1 /*Locked Applets*/ && (!appletItem.canBeHovered || (appletItem.lockZoom && (applet.pluginName !== root.plasmoidName))) ) || (plasmoid.configuration.shadows === 2 /*All Applets*/ && (applet.pluginName !== root.plasmoidName))) || (root.forceTransparentPanel && plasmoid.configuration.shadows>0 && applet.pluginName !== root.plasmoidName)) /*on forced transparent state*/ onActiveChanged: { if (active) { wrapperContainer.opacity = 0; } else { wrapperContainer.opacity = 1; } } sourceComponent: DropShadow{ anchors.fill: parent color: root.appShadowColor //"#ff080808" fast: true samples: 2 * radius source: communicator.overlayLatteIconIsActive ? _wrapperContainer : appletItem.applet radius: shadowSize verticalOffset: forcedShadow ? 0 : 2 property int shadowSize : root.appShadowSize //Math.ceil(root.iconSize / 12) property bool forcedShadow: root.forceTransparentPanel && plasmoid.configuration.shadows>0 && applet.pluginName !== root.plasmoidName ? true : false } } BrightnessContrast{ id:hoveredImage anchors.fill: _wrapperContainer source: _wrapperContainer enabled: opacity != 0 ? true : false opacity: appletMouseArea.containsMouse ? 1 : 0 brightness: 0.25 contrast: 0.15 Behavior on opacity { NumberAnimation { duration: root.durationTime*units.longDuration } } } BrightnessContrast { id: _clickedEffect anchors.fill: _wrapperContainer source: _wrapperContainer visible: clickedAnimation.running } /* onHeightChanged: { if ((index == 1)|| (index==3)){ console.log("H: "+index+" ("+zoomScale+"). "+currentLayout.children[1].height+" - "+currentLayout.children[3].height+" - "+(currentLayout.children[1].height+currentLayout.children[3].height)); } } onZoomScaleChanged:{ if ((index == 1)|| (index==3)){ console.log(index+" ("+zoomScale+"). "+currentLayout.children[1].height+" - "+currentLayout.children[3].height+" - "+(currentLayout.children[1].height+currentLayout.children[3].height)); } }*/ Loader{ anchors.fill: parent active: root.debugMode sourceComponent: Rectangle{ anchors.fill: parent color: "transparent" //! red visualizer, in debug mode for the applets that use fillWidth or fillHeight //! green, for the rest border.color: (appletItem.needsFillSpace && (appletItem.sizeForFill>-1) && root.isHorizontal) ? "red" : "green" border.width: 1 } } Behavior on zoomScale { enabled: !root.globalDirectRender NumberAnimation { duration: 3 * appletItem.animationTime easing.type: Easing.OutCubic } } Behavior on zoomScale { enabled: root.globalDirectRender && !restoreAnimation.running NumberAnimation { duration: root.directRenderAnimationTime } } function calculateScales( currentMousePosition ){ if (root.editMode || root.zoomFactor===1 || root.durationTime===0) { return; } var distanceFromHovered = Math.abs(index - layoutsContainer.hoveredIndex); // A new algorithm trying to make the zoom calculation only once // and at the same time fixing glitches if ((distanceFromHovered == 0)&& (currentMousePosition > 0) ){ //use the new parabolicManager in order to handle all parabolic effect messages var scales = parabolicManager.applyParabolicEffect(index, currentMousePosition, center); /*if (root.latteApplet && Math.abs(index - root.latteAppletPos) > 2){ root.latteApplet.clearZoom(); }*/ //Left hiddenSpacer if(appletItem.startEdge){ hiddenSpacerLeft.nScale = scales.leftScale - 1; } //Right hiddenSpacer ///there is one more item in the currentLayout ???? if(appletItem.endEdge){ hiddenSpacerRight.nScale = scales.rightScale - 1; } zoomScale = root.zoomFactor; } } //scale function signalUpdateScale(nIndex, nScale, step){ if(appletItem && !appletItem.containsMouse && (appletItem.index === nIndex)){ if ( ((canBeHovered && !lockZoom ) || appletItem.latteApplet) && (applet && applet.status !== PlasmaCore.Types.HiddenStatus) //&& (index != currentLayout.hoveredIndex) ){ if(!appletItem.latteApplet){ if(nScale >= 0) zoomScale = nScale + step; else zoomScale = zoomScale + step; } } } } Component.onCompleted: { root.updateScale.connect(signalUpdateScale); } Component.onDestruction: { root.updateScale.disconnect(signalUpdateScale); } }// Main task area // id:wrapper diff --git a/containment/package/contents/ui/applet/indicators/LatteIndicator.qml b/containment/package/contents/ui/applet/indicators/LatteIndicator.qml index 5090b58c..5cc6ac49 100644 --- a/containment/package/contents/ui/applet/indicators/LatteIndicator.qml +++ b/containment/package/contents/ui/applet/indicators/LatteIndicator.qml @@ -1,240 +1,234 @@ /* * 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 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.latte 0.2 as Latte Item{ id:glowFrame width: !root.isVertical ? parent.width : size height: root.isVertical ? parent.height : size property int size: mimicPlasmaPanel ? 4 : root.statesLineSize property bool mimicPlasmaPanel: !root.latteApplet && plasmoid.configuration.panelSize===100 ? !plasmoid.configuration.reverseLinesPosition : false /*Rectangle{ anchors.fill: parent border.width: 1 border.color: "yellow" color: "transparent" opacity:0.6 }*/ Latte.GlowPoint{ id:activePoint anchors.centerIn: parent basicColor: theme.buttonFocusColor roundCorners: true showGlow: root.showGlow glow3D: root.glow3D animation: Math.max(1.65*3*units.longDuration,root.durationTime*3*units.longDuration) location: plasmoid.location glowOpacity: root.glowOpacity contrastColor: root.appShadowColorSolid opacity:{ - if ( (!vertical && width <= glowFrame.size && !isActive) - || (vertical && height <= glowFrame.size && !isActive)) + if ( (!vertical && width <= glowFrame.size && !appletItem.isActive) + || (vertical && height <= glowFrame.size && !appletItem.isActive)) return 0; return 1; } - property bool isActive: (appletItem.isExpanded && !appletItem.isSystray - && applet.pluginName !== root.plasmoidName - && applet.pluginName !== "org.kde.activeWindowControl" - && applet.pluginName !== "org.kde.plasma.appmenu") - property bool vertical: root.isVertical property int animationTime: root.durationTime * (1.2*units.longDuration) property int stateWidth: root.activeIndicatorType === Latte.Types.DotIndicator ? glowFrame.size : glowFrame.width property int stateHeight: root.activeIndicatorType === Latte.Types.DotIndicator ? glowFrame.size : glowFrame.height property real scaleFactor: wrapper.zoomScale function updateInitialSizes(){ if(glowFrame){ if(vertical) width = glowFrame.size; else height = glowFrame.size; - if(vertical && isActive) + if(vertical && appletItem.isActive) height = stateHeight; else height = glowFrame.size; - if(!vertical && isActive) + if(!vertical && appletItem.isActive) width = stateWidth; else width = glowFrame.size; } } - - onIsActiveChanged: { - // if(mainItemContainer.hasActive || windowsPreviewDlg.visible) - activeAndReverseAnimation.start(); + Connections{ + target: appletItem + onIsActiveChanged: activeAndReverseAnimation.start(); } onScaleFactorChanged: { if ( root.activeIndicatorType === Latte.Types.LineIndicator ) { - if(!activeAndReverseAnimation.running && !root.vertical && isActive){ + if(!activeAndReverseAnimation.running && !root.vertical && appletItem.isActive){ width = stateWidth; } - else if (!activeAndReverseAnimation.running && root.vertical && isActive){ + else if (!activeAndReverseAnimation.running && root.vertical && appletItem.isActive){ height = stateHeight; } } } onStateWidthChanged:{ - if(!activeAndReverseAnimation.running && !vertical && isActive && root.activeIndicatorType === Latte.Types.LineIndicator) + if(!activeAndReverseAnimation.running && !vertical && appletItem.isActive && root.activeIndicatorType === Latte.Types.LineIndicator) width = stateWidth; } onStateHeightChanged:{ - if(!activeAndReverseAnimation.running && vertical && isActive && root.activeIndicatorType === Latte.Types.LineIndicator) + if(!activeAndReverseAnimation.running && vertical && appletItem.isActive && root.activeIndicatorType === Latte.Types.LineIndicator) height = stateHeight; } onVerticalChanged: updateInitialSizes(); Component.onCompleted: { updateInitialSizes(); root.onIconSizeChanged.connect(updateInitialSizes); } Component.onDestruction: { root.onIconSizeChanged.disconnect(updateInitialSizes); } NumberAnimation{ id: activeAndReverseAnimation target: activePoint property: root.isVertical ? "height" : "width" - to: !activePoint.isActive ? (root.isVertical ? activePoint.stateHeight : activePoint.stateWidth) : glowFrame.size + to: !appletItem.isActive ? (root.isVertical ? activePoint.stateHeight : activePoint.stateWidth) : glowFrame.size duration: activePoint.animationTime easing.type: Easing.InQuad onStopped: activePoint.updateInitialSizes() } } states: [ State { name: "leftLatte" when: ((plasmoid.location === PlasmaCore.Types.LeftEdge && !root.reverseLinesPosition) || (plasmoid.location === PlasmaCore.Types.RightEdge && root.reverseLinesPosition)) && !mimicPlasmaPanel AnchorChanges { target: glowFrame anchors{ verticalCenter:parent.verticalCenter; horizontalCenter:undefined; top:undefined; bottom:undefined; left:undefined; right:parent.left;} } }, State { name: "bottomLatte" when: ((plasmoid.location === PlasmaCore.Types.BottomEdge && !root.reverseLinesPosition) || (plasmoid.location === PlasmaCore.Types.TopEdge && root.reverseLinesPosition)) && !mimicPlasmaPanel AnchorChanges { target: glowFrame anchors{ verticalCenter:undefined; horizontalCenter:parent.horizontalCenter; top:parent.bottom; bottom:undefined; left:undefined; right:undefined;} } }, State { name: "topLatte" when: ((plasmoid.location === PlasmaCore.Types.TopEdge && !root.reverseLinesPosition) || (plasmoid.location === PlasmaCore.Types.BottomEdge && root.reverseLinesPosition)) && !mimicPlasmaPanel AnchorChanges { target: glowFrame anchors{ verticalCenter:undefined; horizontalCenter:parent.horizontalCenter; top:undefined; bottom:parent.top; left:undefined; right:undefined;} } }, State { name: "rightLatte" when: ((plasmoid.location === PlasmaCore.Types.RightEdge && !root.reverseLinesPosition) || (plasmoid.location === PlasmaCore.Types.LeftEdge && root.reverseLinesPosition)) && !mimicPlasmaPanel AnchorChanges { target: glowFrame anchors{ verticalCenter:parent.verticalCenter; horizontalCenter:undefined; top:undefined; bottom:undefined; left:parent.right; right:undefined;} } }, State { name: "topPlasma" when: plasmoid.location === PlasmaCore.Types.TopEdge && mimicPlasmaPanel AnchorChanges { target: glowFrame anchors{ verticalCenter:undefined; horizontalCenter:parent.horizontalCenter; top:undefined; bottom:parent.bottom; left:undefined; right:undefined;} } }, State { name: "bottomPlasma" when: plasmoid.location === PlasmaCore.Types.BottomEdge && mimicPlasmaPanel AnchorChanges { target: glowFrame anchors{ verticalCenter:undefined; horizontalCenter:parent.horizontalCenter; top:parent.top; bottom:undefined; left:undefined; right:undefined;} } }, State { name: "leftPlasma" when: plasmoid.location === PlasmaCore.Types.LeftEdge && mimicPlasmaPanel AnchorChanges { target: glowFrame anchors{ verticalCenter:undefined; horizontalCenter:parent.horizontalCenter; top:undefined; bottom:undefined; left:undefined; right:parent.right;} } }, State { name: "rightPlasma" when: plasmoid.location === PlasmaCore.Types.LeftEdge && mimicPlasmaPanel AnchorChanges { target: glowFrame anchors{ verticalCenter:undefined; horizontalCenter:parent.horizontalCenter; top:undefined; bottom:undefined; left:parent.left; right:undefined;} } } ] } diff --git a/containment/package/contents/ui/applet/indicators/UnityIndicator.qml b/containment/package/contents/ui/applet/indicators/UnityIndicator.qml new file mode 100644 index 00000000..de147365 --- /dev/null +++ b/containment/package/contents/ui/applet/indicators/UnityIndicator.qml @@ -0,0 +1,56 @@ +/* +* 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.core 2.0 as PlasmaCore + +import org.kde.latte 0.2 as Latte + +Rectangle { + id: unityRect + anchors.bottom: parent.bottom + anchors.bottomMargin: 2 + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width - 4 + height: parent.height - 4 + radius: 4 + //height: (root.iconSize + root.thickMargin)/6 - 4 + //radius: height/2 + color: backgroundColor + clip: true + + property color backgroundColor + property color glowColor + + RadialGradient{ + anchors.verticalCenter: parent.top + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width + height: width + + gradient: Gradient { + GradientStop { position: 0.0; color: glowColor } + GradientStop { position: 0.6; color: "transparent" } + } + } + + visible: isActive +} diff --git a/liblatte2/iconitem.cpp b/liblatte2/iconitem.cpp index 5d4fa201..26ef2c87 100644 --- a/liblatte2/iconitem.cpp +++ b/liblatte2/iconitem.cpp @@ -1,557 +1,558 @@ /* * Copyright 2012 Marco Martin * Copyright 2014 David Edmundson * Copyright 2016 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock and is a Fork of PlasmaCore::IconItem * * 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 . */ #include "iconitem.h" // local #include "../liblatte2/extras.h" // Qt #include #include #include #include #include #include #include // KDE #include #include #include namespace Latte { IconItem::IconItem(QQuickItem *parent) : QQuickItem(parent), m_lastValidSourceName(QString()), m_smooth(false), m_active(false), m_textureChanged(false), m_sizeChanged(false), m_usesPlasmaTheme(false) { setFlag(ItemHasContents, true); connect(KIconLoader::global(), SIGNAL(iconLoaderSettingsChanged()), this, SIGNAL(implicitWidthChanged())); connect(KIconLoader::global(), SIGNAL(iconLoaderSettingsChanged()), this, SIGNAL(implicitHeightChanged())); connect(this, &QQuickItem::enabledChanged, this, &IconItem::enabledChanged); connect(this, &QQuickItem::windowChanged, this, &IconItem::schedulePixmapUpdate); connect(this, SIGNAL(overlaysChanged()), this, SLOT(schedulePixmapUpdate())); connect(this, SIGNAL(providesColorsChanged()), this, SLOT(schedulePixmapUpdate())); //initialize implicit size to the Dialog size setImplicitWidth(KIconLoader::global()->currentSize(KIconLoader::Dialog)); setImplicitHeight(KIconLoader::global()->currentSize(KIconLoader::Dialog)); setSmooth(true); } IconItem::~IconItem() { } void IconItem::setSource(const QVariant &source) { if (source == m_source) { return; } m_source = source; QString sourceString = source.toString(); // If the QIcon was created with QIcon::fromTheme(), try to load it as svg if (source.canConvert() && !source.value().name().isEmpty()) { sourceString = source.value().name(); - setLastValidSourceName(sourceString); } if (!sourceString.isEmpty()) { + setLastValidSourceName(sourceString); + //If a url in the form file:// is passed, take the image pointed by that from disk QUrl url(sourceString); if (url.isLocalFile()) { m_icon = QIcon(); m_imageIcon = QImage(url.path()); m_svgIconName.clear(); m_svgIcon.reset(); } else { if (!m_svgIcon) { m_svgIcon = std::make_unique(this); m_svgIcon->setColorGroup(Plasma::Theme::NormalColorGroup); m_svgIcon->setStatus(Plasma::Svg::Normal); m_svgIcon->setUsingRenderingCache(false); m_svgIcon->setDevicePixelRatio((window() ? window()->devicePixelRatio() : qApp->devicePixelRatio())); connect(m_svgIcon.get(), &Plasma::Svg::repaintNeeded, this, &IconItem::schedulePixmapUpdate); } if (m_usesPlasmaTheme) { //try as a svg icon from plasma theme m_svgIcon->setImagePath(QLatin1String("icons/") + sourceString.split('-').first()); m_svgIcon->setContainsMultipleImages(true); //invalidate the image path to recalculate it later } else { m_svgIcon->setImagePath(QString()); } //success? if (m_svgIcon->isValid() && m_svgIcon->hasElement(sourceString)) { m_icon = QIcon(); m_svgIconName = sourceString; //ok, svg not available from the plasma theme } else { //try to load from iconloader an svg with Plasma::Svg const auto *iconTheme = KIconLoader::global()->theme(); QString iconPath; if (iconTheme) { iconPath = iconTheme->iconPath(sourceString + QLatin1String(".svg") , static_cast(qMin(width(), height())) , KIconLoader::MatchBest); if (iconPath.isEmpty()) { iconPath = iconTheme->iconPath(sourceString + QLatin1String(".svgz") , static_cast(qMin(width(), height())) , KIconLoader::MatchBest); } } else { qWarning() << "KIconLoader has no theme set"; } if (!iconPath.isEmpty()) { m_svgIcon->setImagePath(iconPath); m_svgIconName = sourceString; //fail, use QIcon } else { //if we started with a QIcon use that. m_icon = source.value(); if (m_icon.isNull()) { m_icon = QIcon::fromTheme(sourceString); } m_svgIconName.clear(); m_svgIcon.reset(); m_imageIcon = QImage(); } } } } else if (source.canConvert()) { m_icon = source.value(); m_imageIcon = QImage(); m_svgIconName.clear(); m_svgIcon.reset(); } else if (source.canConvert()) { m_icon = QIcon(); m_imageIcon = source.value(); m_svgIconName.clear(); m_svgIcon.reset(); } else { m_icon = QIcon(); m_imageIcon = QImage(); m_svgIconName.clear(); m_svgIcon.reset(); } if (width() > 0 && height() > 0) { schedulePixmapUpdate(); } emit sourceChanged(); emit validChanged(); } QVariant IconItem::source() const { return m_source; } QString IconItem::lastValidSourceName() { return m_lastValidSourceName; } void IconItem::setLastValidSourceName(QString name) { if (m_lastValidSourceName == name || name == "" || name == "application-x-executable") { return; } m_lastValidSourceName = name; emit lastValidSourceNameChanged(); } void IconItem::setOverlays(const QStringList &overlays) { if (overlays == m_overlays) { return; } m_overlays = overlays; emit overlaysChanged(); } QStringList IconItem::overlays() const { return m_overlays; } bool IconItem::isActive() const { return m_active; } void IconItem::setActive(bool active) { if (m_active == active) { return; } m_active = active; if (isComponentComplete()) { schedulePixmapUpdate(); } emit activeChanged(); } bool IconItem::providesColors() const { return m_providesColors; } void IconItem::setProvidesColors(const bool provides) { if (m_providesColors == provides) { return; } m_providesColors = provides; emit providesColorsChanged(); } void IconItem::setSmooth(const bool smooth) { if (smooth == m_smooth) { return; } m_smooth = smooth; update(); } bool IconItem::smooth() const { return m_smooth; } bool IconItem::isValid() const { return !m_icon.isNull() || m_svgIcon || !m_imageIcon.isNull(); } int IconItem::paintedWidth() const { return boundingRect().size().toSize().width(); } int IconItem::paintedHeight() const { return boundingRect().size().toSize().height(); } bool IconItem::usesPlasmaTheme() const { return m_usesPlasmaTheme; } void IconItem::setUsesPlasmaTheme(bool usesPlasmaTheme) { if (m_usesPlasmaTheme == usesPlasmaTheme) { return; } m_usesPlasmaTheme = usesPlasmaTheme; // Reload icon with new settings const QVariant src = m_source; m_source.clear(); setSource(src); update(); emit usesPlasmaThemeChanged(); } void IconItem::updatePolish() { QQuickItem::updatePolish(); loadPixmap(); } QSGNode *IconItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) { Q_UNUSED(updatePaintNodeData) if (m_iconPixmap.isNull() || width() < 1.0 || height() < 1.0) { delete oldNode; return nullptr; } ManagedTextureNode *textureNode = dynamic_cast(oldNode); if (!textureNode || m_textureChanged) { if (oldNode) delete oldNode; textureNode = new ManagedTextureNode; textureNode->setTexture(QSharedPointer(window()->createTextureFromImage(m_iconPixmap.toImage()))); m_sizeChanged = true; m_textureChanged = false; } if (m_sizeChanged) { const auto iconSize = qMin(boundingRect().size().width(), boundingRect().size().height()); const QRectF destRect(QPointF(boundingRect().center() - QPointF(iconSize / 2, iconSize / 2)), QSizeF(iconSize, iconSize)); textureNode->setRect(destRect); m_sizeChanged = false; } return textureNode; } void IconItem::schedulePixmapUpdate() { polish(); } void IconItem::enabledChanged() { schedulePixmapUpdate(); } QColor IconItem::backgroundColor() const { return m_backgroundColor; } void IconItem::setBackgroundColor(QColor background) { if (m_backgroundColor == background) { return; } m_backgroundColor = background; emit backgroundColorChanged(); } QColor IconItem::glowColor() const { return m_glowColor; } void IconItem::setGlowColor(QColor glow) { if (m_glowColor == glow) { return; } m_glowColor = glow; emit glowColorChanged(); } void IconItem::updateColors() { QImage icon = m_iconPixmap.toImage(); if (icon.format() != QImage::Format_Invalid) { float rtotal = 0, gtotal = 0, btotal = 0; float total = 0.0f; for(int row=0; row 0.15f) { tempColor.setHsvF(tempColor.hueF(), 0.65f, tempColor.valueF()); } tempColor.setHsvF(tempColor.hueF(), tempColor.saturationF(), 0.55f); //original 0.90f ??? setBackgroundColor(tempColor); tempColor.setHsvF(tempColor.hueF(), tempColor.saturationF(), 1.0f); setGlowColor(tempColor); } } void IconItem::loadPixmap() { if (!isComponentComplete()) { return; } const auto size = qMin(width(), height()); //final pixmap to paint QPixmap result; if (size <= 0) { m_iconPixmap = QPixmap(); update(); return; } else if (m_svgIcon) { m_svgIcon->resize(size, size); if (m_svgIcon->hasElement(m_svgIconName)) { result = m_svgIcon->pixmap(m_svgIconName); } else if (!m_svgIconName.isEmpty()) { const auto *iconTheme = KIconLoader::global()->theme(); QString iconPath; if (iconTheme) { iconPath = iconTheme->iconPath(m_svgIconName + QLatin1String(".svg") , static_cast(qMin(width(), height())) , KIconLoader::MatchBest); if (iconPath.isEmpty()) { iconPath = iconTheme->iconPath(m_svgIconName + QLatin1String(".svgz"), static_cast(qMin(width(), height())) , KIconLoader::MatchBest); } } else { qWarning() << "KIconLoader has no theme set"; } if (!iconPath.isEmpty()) { m_svgIcon->setImagePath(iconPath); } result = m_svgIcon->pixmap(); } } else if (!m_icon.isNull()) { result = m_icon.pixmap(QSize(static_cast(size), static_cast(size)) * (window() ? window()->devicePixelRatio() : qApp->devicePixelRatio())); } else if (!m_imageIcon.isNull()) { result = QPixmap::fromImage(m_imageIcon); } else { m_iconPixmap = QPixmap(); update(); return; } // Strangely KFileItem::overlays() returns empty string-values, so // we need to check first whether an overlay must be drawn at all. // It is more efficient to do it here, as KIconLoader::drawOverlays() // assumes that an overlay will be drawn and has some additional // setup time. foreach (const QString &overlay, m_overlays) { if (!overlay.isEmpty()) { // There is at least one overlay, draw all overlays above m_pixmap // and cancel the check KIconLoader::global()->drawOverlays(m_overlays, result, KIconLoader::Desktop); break; } } if (!isEnabled()) { result = KIconLoader::global()->iconEffect()->apply(result, KIconLoader::Desktop, KIconLoader::DisabledState); } else if (m_active) { result = KIconLoader::global()->iconEffect()->apply(result, KIconLoader::Desktop, KIconLoader::ActiveState); } m_iconPixmap = result; if (m_providesColors && m_lastValidSourceName != m_lastColorsSourceName) { m_lastColorsSourceName = m_lastValidSourceName; updateColors(); } m_textureChanged = true; //don't animate initial setting update(); } void IconItem::itemChange(ItemChange change, const ItemChangeData &value) { QQuickItem::itemChange(change, value); } void IconItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { if (newGeometry.size() != oldGeometry.size()) { m_sizeChanged = true; if (newGeometry.width() > 1 && newGeometry.height() > 1) { schedulePixmapUpdate(); } else { update(); } const auto oldSize = qMin(oldGeometry.size().width(), oldGeometry.size().height()); const auto newSize = qMin(newGeometry.size().width(), newGeometry.size().height()); if (!almost_equal(oldSize, newSize, 2)) { emit paintedSizeChanged(); } } QQuickItem::geometryChanged(newGeometry, oldGeometry); } void IconItem::componentComplete() { QQuickItem::componentComplete(); schedulePixmapUpdate(); } } diff --git a/plasmoid/package/contents/ui/task/IconItem.qml b/plasmoid/package/contents/ui/task/IconItem.qml index 5ab7676f..34a12429 100644 --- a/plasmoid/package/contents/ui/task/IconItem.qml +++ b/plasmoid/package/contents/ui/task/IconItem.qml @@ -1,774 +1,789 @@ /* * 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.4 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.kquickcontrolsaddons 2.0 as KQuickControlAddons import org.kde.latte 0.2 as Latte import "animations" as TaskAnimations import "indicators" as Indicators //I am using KQuickControlAddons.QIconItem even though onExit it triggers the following error //QObject::~QObject: Timers cannot be stopped from another thread //but it increases performance almost to double during animation Item{ id: taskIcon width: wrapper.regulatorWidth height: wrapper.regulatorHeight //big interval to show shadows only after all the crappy adds and removes of tasks //have happened property bool firstDrawed: true property bool toBeDestroyed: false // three intervals in order to create the necessary buffers from the // PlasmaCore.IconItem, one big interval for the first creation of the // plasmoid, a second one for the first creation of a task and a small one // for simple updates. // This is done before especially on initialization stage some visuals // are not ready and empty buffers are created //property int firstDrawedInterval: root.initializationStep ? 2000 : 1000 // property int shadowInterval: firstDrawed ? firstDrawedInterval : 250 property int shadowInterval: firstDrawed ? 1000 : 250 property int shadowSize : root.appShadowSize readonly property bool smartLauncherEnabled: ((taskItem.isStartup === false) && (root.showInfoBadge || root.showProgressBadge)) readonly property variant iconDecoration: decoration property QtObject buffers: null property QtObject smartLauncherItem: null property Item titleTooltipVisualParent: titleTooltipParent property Item previewsTootipVisualParent: previewsTooltipParent /* Rectangle{ anchors.fill: parent border.width: 1 border.color: "green" color: "transparent" } */ onSmartLauncherEnabledChanged: { if (smartLauncherEnabled && !smartLauncherItem) { var smartLauncher = Qt.createQmlObject( " import org.kde.plasma.private.taskmanager 0.1 as TaskManagerApplet; TaskManagerApplet.SmartLauncherItem { }", taskIcon); smartLauncher.launcherUrl = Qt.binding(function() { return taskItem.launcherUrlWithIcon; }); smartLauncherItem = smartLauncher; } else if (!smartLauncherEnabled && smartLauncherItem) { smartLauncherItem.destroy(); smartLauncherItem = null; } } Rectangle{ id: draggedRectangle width: taskItem.isSeparator ? parent.width + 1 : iconImageBuffer.width+1 height: taskItem.isSeparator ? parent.height + 1 : iconImageBuffer.height+1 anchors.centerIn: iconGraphic opacity: 0 radius: 3 anchors.margins: 5 property color tempColor: theme.highlightColor color: tempColor border.width: 1 border.color: theme.highlightColor onTempColorChanged: tempColor.a = 0.35; } Loader { anchors.fill: parent - active: root.activeIndicator !== Latte.Types.NoneIndicator && root.indicatorStyle === Latte.Types.PlasmaIndicator - sourceComponent: Indicators.PlasmaIndicator{} + active: root.activeIndicator !== Latte.Types.NoneIndicator + && (root.indicatorStyle === Latte.Types.PlasmaIndicator || root.indicatorStyle === Latte.Types.UnityIndicator) + + sourceComponent: root.indicatorStyle === Latte.Types.PlasmaIndicator ? plasmaIndicatorComponent : unityIndicatorComponent + + Component{ + id: plasmaIndicatorComponent + Indicators.PlasmaIndicator{} + } + Component{ + id:unityIndicatorComponent + Indicators.UnityIndicator{ + backgroundColor: iconImageBuffer.backgroundColor + glowColor: iconImageBuffer.glowColor + } + } } TitleTooltipParent{ id: titleTooltipParent thickness: (root.zoomFactor * root.realSize) + root.statesLineSize } TitleTooltipParent{ id: previewsTooltipParent thickness: (root.zoomFactor * (root.thickMarginBase + root.iconSize)) + root.statesLineSize + 1 } // KQuickControlAddons.QIconItem{ Item{ id: iconGraphic width: parent.width height: parent.height //fix bug #478, when changing form factor sometimes the tasks are not positioned //correctly, in such case we make a fast reinitialization for the sizes Connections { target: plasmoid onFormFactorChanged:{ taskItem.inAddRemoveAnimation = false; wrapper.mScale = 1.01; wrapper.tempScaleWidth = 1.01; wrapper.tempScaleHeight = 1.01; wrapper.mScale = 1; wrapper.tempScaleWidth = 1; wrapper.tempScaleHeight = 1; } } Latte.IconItem{ id: iconImageBuffer anchors.rightMargin:{ if (root.position === PlasmaCore.Types.RightPositioned) return root.thickMarginBase; else if (root.position === PlasmaCore.Types.LeftPositioned) return wrapper.mScale * root.thickMarginHigh; else return 0; } anchors.leftMargin: { if (root.position === PlasmaCore.Types.LeftPositioned) return root.thickMarginBase; else if (root.position === PlasmaCore.Types.RightPositioned) return wrapper.mScale * root.thickMarginHigh; else return 0; } anchors.topMargin: { if (root.position === PlasmaCore.Types.TopPositioned) return root.thickMarginBase; else if (root.position === PlasmaCore.Types.BottomPositioned) return wrapper.mScale * root.thickMarginHigh; else return 0; } anchors.bottomMargin:{ if (root.position === PlasmaCore.Types.BottomPositioned) return root.thickMarginBase; else if (root.position === PlasmaCore.Types.TopPositioned) return wrapper.mScale * root.thickMarginHigh; else return 0; } width: Math.round(newTempSize) //+ 2*taskIcon.shadowSize height: Math.round(width) source: decoration + providesColors: root.indicatorStyle === Latte.Types.UnityIndicator opacity: root.enableShadows ? 0 : 1 visible: !taskItem.isSeparator && !badgesLoader.active //visible: !root.enableShadows onValidChanged: { if (!valid && (source === decoration || source === "unknown")) { source = "application-x-executable"; } } //! try to show the correct icon when a window is removed... libtaskmanager when a window is removed //! sends an unknown pixmap as icon Connections { target: taskItem onInRemoveStageChanged: { if (taskItem.inRemoveStage && iconImageBuffer.lastValidSourceName !== "") { iconImageBuffer.source = iconImageBuffer.lastValidSourceName; } } } property int zoomedSize: root.zoomFactor * root.iconSize property real basicScalingWidth : wrapper.inTempScaling ? (root.iconSize * wrapper.scaleWidth) : root.iconSize * wrapper.mScale property real basicScalingHeight : wrapper.inTempScaling ? (root.iconSize * wrapper.scaleHeight) : root.iconSize * wrapper.mScale property real newTempSize: { if (wrapper.opacity == 1) return Math.min(basicScalingWidth, basicScalingHeight) else return Math.max(basicScalingWidth, basicScalingHeight) } ///states for launcher animation states: [ State{ name: "*" when: !launcherAnimation.running && !newWindowAnimation.running && !taskItem.inAddRemoveAnimation && !fastRestoreAnimation.running AnchorChanges{ target:iconImageBuffer; anchors.horizontalCenter: !root.vertical ? parent.horizontalCenter : undefined; anchors.verticalCenter: root.vertical ? parent.verticalCenter : undefined; anchors.right: root.position === PlasmaCore.Types.RightPositioned ? parent.right : undefined; anchors.left: root.position === PlasmaCore.Types.LeftPositioned ? parent.left : undefined; anchors.top: root.position === PlasmaCore.Types.TopPositioned ? parent.top : undefined; anchors.bottom: root.position === PlasmaCore.Types.BottomPositioned ? parent.bottom : undefined; } }, State{ name: "inAddRemoveAnimation" when: taskItem.inAddRemoveAnimation AnchorChanges{ target:iconImageBuffer; anchors.horizontalCenter: !root.vertical ? parent.horizontalCenter : undefined; anchors.verticalCenter: root.vertical ? parent.verticalCenter : undefined; anchors.right: root.position === PlasmaCore.Types.LeftPositioned ? parent.right : undefined; anchors.left: root.position === PlasmaCore.Types.RightPositioned ? parent.left : undefined; anchors.top: root.position === PlasmaCore.Types.BottomPositioned ? parent.top : undefined; anchors.bottom: root.position === PlasmaCore.Types.TopPositioned ? parent.bottom : undefined; } }, State{ name: "animating" when: (launcherAnimation.running || newWindowAnimation.running || fastRestoreAnimation.running) && !taskItem.inAddRemoveAnimation AnchorChanges{ target:iconImageBuffer; anchors.horizontalCenter: !root.vertical ? parent.horizontalCenter : undefined; anchors.verticalCenter: root.vertical ? parent.verticalCenter : undefined; anchors.right: root.position === PlasmaCore.Types.LeftPositioned ? parent.right : undefined; anchors.left: root.position === PlasmaCore.Types.RightPositioned ? parent.left : undefined; anchors.top: root.position === PlasmaCore.Types.BottomPositioned ? parent.top : undefined; anchors.bottom: root.position === PlasmaCore.Types.TopPositioned ? parent.bottom : undefined; } } ] ///transitions, basic for the anchor changes transitions: [ Transition{ from: "animating" to: "*" enabled: !fastRestoreAnimation.running && !taskItem.inMimicParabolicAnimation AnchorAnimation { duration: 1.5*root.durationTime*units.longDuration } } ] } //IconImageBuffer //! Shadows Loader{ id: taskWithShadow anchors.fill: iconImageBuffer active: root.enableShadows && !taskItem.isSeparator sourceComponent: DropShadow{ anchors.fill: parent color: root.appShadowColor fast: true samples: 2 * radius source: badgesLoader.active ? badgesLoader.item : iconImageBuffer radius: root.appShadowSize verticalOffset: 2 } } //! Shadows //! Combined Loader for Progress and Audio badges masks Loader{ id: badgesLoader anchors.fill: iconImageBuffer active: activateProgress > 0 asynchronous: true opacity: stateColorizer.opacity > 0 ? 0 : 1 property real activateProgress: showInfo || showProgress || showAudio ? 1 : 0 property bool showInfo: (root.showInfoBadge && taskIcon.smartLauncherItem && !taskItem.isSeparator && (taskIcon.smartLauncherItem.countVisible || taskItem.badgeIndicator > 0)) property bool showProgress: root.showProgressBadge && taskIcon.smartLauncherItem && !taskItem.isSeparator && taskIcon.smartLauncherItem.progressVisible property bool showAudio: root.showAudioBadge && taskItem.hasAudioStream && taskItem.playingAudio && !taskItem.isSeparator Behavior on activateProgress { NumberAnimation { duration: root.durationTime*2*units.longDuration } } sourceComponent: Item{ ShaderEffect { id: iconOverlay enabled: false anchors.fill: parent property var source: ShaderEffectSource { sourceItem: Latte.IconItem{ width: iconImageBuffer.width height: iconImageBuffer.height source: iconImageBuffer.source } } property var mask: ShaderEffectSource { sourceItem: Item{ LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft && !root.vertical LayoutMirroring.childrenInherit: true width: iconImageBuffer.width height: iconImageBuffer.height Rectangle{ id: maskRect width: Math.max(badgeVisualsLoader.infoBadgeWidth, parent.width / 2) height: parent.height / 2 radius: parent.height visible: badgesLoader.showInfo || badgesLoader.showProgress //! Removes any remainings from the icon around the roundness at the corner Rectangle{ id: maskCorner width: parent.width/2 height: parent.height/2 } states: [ State { name: "default" when: (plasmoid.location !== PlasmaCore.Types.RightEdge) AnchorChanges { target: maskRect anchors{ top:parent.top; bottom:undefined; left:undefined; right:parent.right;} } AnchorChanges { target: maskCorner anchors{ top:parent.top; bottom:undefined; left:undefined; right:parent.right;} } }, State { name: "right" when: (plasmoid.location === PlasmaCore.Types.RightEdge) AnchorChanges { target: maskRect anchors{ top:parent.top; bottom:undefined; left:parent.left; right:undefined;} } AnchorChanges { target: maskCorner anchors{ top:parent.top; bottom:undefined; left:parent.left; right:undefined;} } } ] } // progressMask Rectangle{ id: maskRect2 width: parent.width/2 height: width radius: width visible: badgesLoader.showAudio Rectangle{ id: maskCorner2 width:parent.width/2 height:parent.height/2 } states: [ State { name: "default" when: (plasmoid.location !== PlasmaCore.Types.RightEdge) AnchorChanges { target: maskRect2 anchors{ top:parent.top; bottom:undefined; left:parent.left; right:undefined;} } AnchorChanges { target: maskCorner2 anchors{ top:parent.top; bottom:undefined; left:parent.left; right:undefined;} } }, State { name: "right" when: (plasmoid.location === PlasmaCore.Types.RightEdge) AnchorChanges { target: maskRect2 anchors{ top:parent.top; bottom:undefined; left:undefined; right:parent.right;} } AnchorChanges { target: maskCorner2 anchors{ top:parent.top; bottom:undefined; left:undefined; right:parent.right;} } } ] } // audio mask } hideSource: true live: true } //end of mask supportsAtlasTextures: true fragmentShader: " varying highp vec2 qt_TexCoord0; uniform highp float qt_Opacity; uniform lowp sampler2D source; uniform lowp sampler2D mask; void main() { gl_FragColor = texture2D(source, qt_TexCoord0.st) * (1.0 - (texture2D(mask, qt_TexCoord0.st).a)) * qt_Opacity; } " } //end of sourceComponent } } ////! //! START: Badges Visuals //! the badges visual get out from iconGraphic in order to be able to draw shadows that //! extend beyond the iconGraphic boundaries Loader { id: badgeVisualsLoader anchors.fill: iconImageBuffer active: badgesLoader.active readonly property int infoBadgeWidth: active ? publishedInfoBadgeWidth : 0 property int publishedInfoBadgeWidth: 0 sourceComponent: Item { ProgressOverlay{ id: infoBadge anchors.right: parent.right anchors.top: parent.top width: Math.max(parent.width, contentWidth) height: parent.height opacity: badgesLoader.activateProgress visible: badgesLoader.showInfo || badgesLoader.showProgress layer.enabled: root.enableShadows layer.effect: DropShadow { color: root.appShadowColor fast: true samples: 2 * radius source: infoBadge radius: root.appShadowSize verticalOffset: 2 } } AudioStream{ id: audioStreamBadge anchors.fill: parent opacity: badgesLoader.activateProgress visible: badgesLoader.showAudio layer.enabled: root.enableShadows layer.effect: DropShadow { color: root.appShadowColor fast: true samples: 2 * radius source: audioStreamBadge radius: root.appShadowSize verticalOffset: 2 } } Binding { target: badgeVisualsLoader property: "publishedInfoBadgeWidth" value: infoBadge.contentWidth } //! grey-ing the badges when the task is dragged Colorize{ anchors.centerIn: parent width: source.width height: source.height source: parent opacity: stateColorizer.opacity hue: stateColorizer.hue saturation: stateColorizer.saturation lightness: stateColorizer.lightness } /*BrightnessContrast{ anchors.fill: parent source: parent opacity: hoveredImage.opacity brightness: hoveredImage.brightness contrast: hoveredImage.contrast } BrightnessContrast { anchors.fill: parent source: parent visible: brightnessTaskEffect.visible }*/ } } //! END: Badges Visuals //! Effects Colorize{ id: stateColorizer anchors.fill: iconImageBuffer source: badgesLoader.active ? badgesLoader : iconImageBuffer opacity:0 hue:0 saturation:0 lightness:0 } BrightnessContrast{ id:hoveredImage anchors.fill: iconImageBuffer source: badgesLoader.active ? badgesLoader : iconImageBuffer opacity: taskItem.containsMouse && !clickedAnimation.running ? 1 : 0 brightness: 0.30 contrast: 0.1 Behavior on opacity { NumberAnimation { duration: root.durationTime*units.longDuration } } } BrightnessContrast { id: brightnessTaskEffect anchors.fill: iconImageBuffer source: badgesLoader.active ? badgesLoader : iconImageBuffer visible: clickedAnimation.running } //! Effects ShortcutBadge{} } VisualAddItem{ id: dropFilesVisual anchors.fill: iconGraphic visible: opacity == 0 ? false : true opacity: root.dropNewLauncher && !mouseHandler.onlyLaunchers && (root.dragSource == null) && (mouseHandler.hoveredItem === taskItem) ? 1 : 0 } Component.onDestruction: { taskIcon.toBeDestroyed = true; if(removingAnimation.removingItem) removingAnimation.removingItem.destroy(); } Connections{ target: taskItem onShowAttentionChanged:{ if (!taskItem.showAttention && newWindowAnimation.running && taskItem.inAttentionAnimation) { newWindowAnimation.pause(); fastRestoreAnimation.start(); } } } ///// Animations ///// TaskAnimations.ClickedAnimation { id: clickedAnimation } TaskAnimations.LauncherAnimation { id:launcherAnimation } TaskAnimations.NewWindowAnimation { id: newWindowAnimation } TaskAnimations.RemoveWindowFromGroupAnimation { id: removingAnimation } TaskAnimations.FastRestoreAnimation { id: fastRestoreAnimation } //////////// States //////////////////// states: [ State{ name: "*" when: !taskItem.isDragged }, State{ name: "isDragged" when: ( (taskItem.isDragged) && (!root.editMode) ) } ] //////////// Transitions ////////////// transitions: [ Transition{ id: isDraggedTransition to: "isDragged" property int speed: root.durationTime*units.longDuration SequentialAnimation{ ScriptAction{ script: { icList.directRender = false; if(latteView) { latteView.globalDirectRender=false; } taskItem.inBlockingAnimation = true; root.clearZoom(); } } PropertyAnimation { target: wrapper property: "mScale" to: 1 + ((root.zoomFactor - 1) / 3) duration: isDraggedTransition.speed / 2 easing.type: Easing.OutQuad } ParallelAnimation{ PropertyAnimation { target: draggedRectangle property: "opacity" to: 1 duration: isDraggedTransition.speed easing.type: Easing.OutQuad } PropertyAnimation { target: iconImageBuffer property: "opacity" to: 0 duration: isDraggedTransition.speed easing.type: Easing.OutQuad } PropertyAnimation { target: stateColorizer property: "opacity" to: taskItem.isSeparator ? 0 : 1 duration: isDraggedTransition.speed easing.type: Easing.OutQuad } } } onRunningChanged: { if(running){ taskItem.animationStarted(); //root.animations++; parabolicManager.clearTasksGreaterThan(index); parabolicManager.clearTasksLowerThan(index); if (latteView){ latteView.parabolicManager.clearAppletsGreaterThan(latteView.latteAppletPos); latteView.parabolicManager.clearAppletsLowerThan(latteView.latteAppletPos); } } } }, Transition{ id: defaultTransition from: "isDragged" to: "*" property int speed: root.durationTime*units.longDuration SequentialAnimation{ ScriptAction{ script: { icList.directRender = false; if(latteView) { latteView.globalDirectRender=false; } } } ParallelAnimation{ PropertyAnimation { target: draggedRectangle property: "opacity" to: 0 duration: defaultTransition.speed easing.type: Easing.OutQuad } PropertyAnimation { target: iconImageBuffer property: "opacity" to: 1 duration: defaultTransition.speed easing.type: Easing.OutQuad } PropertyAnimation { target: stateColorizer property: "opacity" to: 0 duration: isDraggedTransition.speed easing.type: Easing.OutQuad } } /* PropertyAnimation { target: wrapper property: "mScale" to: 1; duration: isDraggedTransition.speed easing.type: Easing.OutQuad }*/ ScriptAction{ script: { taskItem.inBlockingAnimation = false; } } } onRunningChanged: { if(!running){ var halfZoom = 1 + ((root.zoomFactor - 1) / 2); wrapper.calculateScales((root.iconSize+root.iconMargin)/2); taskItem.animationEnded(); // root.animations--; } } } ] }// Icon Item diff --git a/plasmoid/package/contents/ui/task/indicators/UnityIndicator.qml b/plasmoid/package/contents/ui/task/indicators/UnityIndicator.qml new file mode 100644 index 00000000..ecb52c16 --- /dev/null +++ b/plasmoid/package/contents/ui/task/indicators/UnityIndicator.qml @@ -0,0 +1,56 @@ +/* +* 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.core 2.0 as PlasmaCore + +import org.kde.latte 0.2 as Latte + +Rectangle { + id: unityRect + anchors.bottom: parent.bottom + anchors.bottomMargin: 2 + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width - 4 + height: parent.height - 4 + radius: 4 + //height: (root.iconSize + root.thickMargin)/6 - 4 + //radius: height/2 + color: backgroundColor + clip: true + + property color backgroundColor + property color glowColor + + RadialGradient{ + anchors.verticalCenter: parent.top + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width + height: width + + gradient: Gradient { + GradientStop { position: 0.0; color: glowColor } + GradientStop { position: 0.6; color: "transparent" } + } + } + + visible: isWindow +}