You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
615 lines
23 KiB
QML
615 lines
23 KiB
QML
/*
|
|
* Copyright 2021 Michail Vourlakos <mvourlakos@gmail.com>
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import QtQuick 2.7
|
|
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.kirigami 2.0 as Kirigami
|
|
|
|
import org.kde.latte.core 0.2 as LatteCore
|
|
import org.kde.latte.components 1.0 as LatteComponents
|
|
|
|
import "animations" as TaskAnimations
|
|
|
|
Item {
|
|
id: taskIconContainer
|
|
anchors.fill: parent
|
|
property bool toBeDestroyed: false
|
|
|
|
readonly property color backgroundColor: iconColorsLoader.active ? iconColorsLoader.item.backgroundColor : theme.backgroundColor
|
|
readonly property color glowColor: iconColorsLoader.active ? iconColorsLoader.item.glowColor : theme.textColor
|
|
|
|
readonly property bool smartLauncherEnabled: ((taskItem.isStartup === false) && (root.showInfoBadge || root.showProgressBadge))
|
|
readonly property bool progressVisible: smartLauncherItem && smartLauncherItem.progressVisible
|
|
readonly property real progress: smartLauncherItem && smartLauncherItem.progress ? smartLauncherItem.progress : 0
|
|
property QtObject smartLauncherItem: null
|
|
|
|
Rectangle{
|
|
id: draggedRectangle
|
|
width: parent.width + 2
|
|
height: parent.height + 2
|
|
anchors.centerIn: taskIconContainer
|
|
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;
|
|
}
|
|
|
|
//! Provide icon background and glow colors
|
|
Loader {
|
|
id: iconColorsLoader
|
|
active: taskItem.abilities.indicators.info.needsIconColors
|
|
visible: false
|
|
|
|
sourceComponent: LatteCore.IconItem{
|
|
width:64
|
|
height:64
|
|
source: taskIconItem.source
|
|
providesColors: true
|
|
}
|
|
}
|
|
|
|
Kirigami.Icon {
|
|
id: taskIconItem
|
|
anchors.fill: parent
|
|
//roundToIconSize: false
|
|
source: decoration
|
|
visible: !badgesLoader.active
|
|
|
|
readonly property real size: Math.min(width,height)
|
|
|
|
///states for launcher animation
|
|
states: [
|
|
State{
|
|
name: "*"
|
|
//! since qt 5.14 default state can not use "when" property
|
|
//! it breaks restoring transitions otherwise
|
|
|
|
AnchorChanges{
|
|
target: taskIconItem;
|
|
anchors.horizontalCenter: !root.vertical ? parent.horizontalCenter : undefined;
|
|
anchors.verticalCenter: root.vertical ? parent.verticalCenter : undefined;
|
|
anchors.right: root.location === PlasmaCore.Types.RightEdge ? parent.right : undefined;
|
|
anchors.left: root.location === PlasmaCore.Types.LeftEdge ? parent.left : undefined;
|
|
anchors.top: root.location === PlasmaCore.Types.TopEdge ? parent.top : undefined;
|
|
anchors.bottom: root.location === PlasmaCore.Types.BottomEdge ? parent.bottom : undefined;
|
|
}
|
|
},
|
|
|
|
State{
|
|
name: "animating"
|
|
when: !taskItem.inAddRemoveAnimation && (launcherAnimation.running || newWindowAnimation.running || fastRestoreAnimation.running)
|
|
|
|
AnchorChanges{
|
|
target: taskIconItem;
|
|
anchors.horizontalCenter: !root.vertical ? parent.horizontalCenter : undefined;
|
|
anchors.verticalCenter: root.vertical ? parent.verticalCenter : undefined;
|
|
anchors.right: root.location === PlasmaCore.Types.LeftEdge ? parent.right : undefined;
|
|
anchors.left: root.location === PlasmaCore.Types.RightEdge ? parent.left : undefined;
|
|
anchors.top: root.location === PlasmaCore.Types.BottomEdge ? parent.top : undefined;
|
|
anchors.bottom: root.location === PlasmaCore.Types.TopEdge ? parent.bottom : undefined;
|
|
}
|
|
}
|
|
]
|
|
|
|
///transitions, basic for the anchor changes
|
|
transitions: [
|
|
Transition{
|
|
from: "animating"
|
|
to: "*"
|
|
enabled: !fastRestoreAnimation.running
|
|
|
|
AnchorAnimation { duration: 1.5 * taskItem.abilities.animations.speedFactor.current * taskItem.abilities.animations.duration.large }
|
|
}
|
|
]
|
|
}
|
|
|
|
//! Combined Loader for Progress and Audio badges masks
|
|
Loader{
|
|
id: badgesLoader
|
|
anchors.fill: taskIconContainer
|
|
active: (activateProgress > 0) && taskItem.abilities.environment.isGraphicsSystemAccelerated
|
|
asynchronous: true
|
|
opacity: stateColorizer.opacity > 0 ? 0 : 1
|
|
|
|
property real activateProgress: showInfo || showProgress || showAudio ? 1 : 0
|
|
|
|
property bool showInfo: (root.showInfoBadge && taskIcon.smartLauncherItem
|
|
&& (taskIcon.smartLauncherItem.countVisible || taskItem.badgeIndicator > 0) && !taskIcon.smartLauncherItem.progressVisible)
|
|
|
|
property bool showProgress: root.showProgressBadge && taskIcon.smartLauncherItem
|
|
&& taskIcon.smartLauncherItem.progressVisible
|
|
|
|
property bool showAudio: (root.showAudioBadge && taskItem.hasAudioStream && taskItem.playingAudio)
|
|
|
|
Behavior on activateProgress {
|
|
NumberAnimation { duration: 2 * taskItem.abilities.animations.speedFactor.current * taskItem.abilities.animations.duration.large }
|
|
}
|
|
|
|
sourceComponent: Item{
|
|
ShaderEffect {
|
|
id: iconOverlay
|
|
enabled: false
|
|
anchors.fill: parent
|
|
property var source: ShaderEffectSource {
|
|
sourceItem: Kirigami.Icon{
|
|
width: taskIconItem.width
|
|
height: taskIconItem.height
|
|
smooth: taskIconItem.smooth
|
|
source: taskIconItem.source
|
|
//roundToIconSize: taskIconItem.roundToIconSize
|
|
active: taskIconItem.active
|
|
|
|
Loader{
|
|
anchors.fill: parent
|
|
active: plasmoid.configuration.forceMonochromaticIcons
|
|
|
|
sourceComponent: ColorOverlay {
|
|
anchors.fill: parent
|
|
color: latteBridge ? latteBridge.palette.textColor : "transparent"
|
|
source: taskIconItem
|
|
}
|
|
}
|
|
}
|
|
}
|
|
property var mask: ShaderEffectSource {
|
|
sourceItem: Item{
|
|
LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft && !root.vertical
|
|
LayoutMirroring.childrenInherit: true
|
|
|
|
width: taskIconContainer.width
|
|
height: taskIconContainer.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: (root.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: (root.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: (root.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: (root.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: taskIconContainer
|
|
active: (badgesLoader.activateProgress > 0)
|
|
|
|
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
|
|
}
|
|
|
|
AudioStream{
|
|
id: audioStreamBadge
|
|
anchors.fill: parent
|
|
opacity: badgesLoader.activateProgress
|
|
visible: badgesLoader.showAudio
|
|
}
|
|
|
|
Binding {
|
|
target: badgeVisualsLoader
|
|
property: "publishedInfoBadgeWidth"
|
|
value: infoBadge.contentWidth
|
|
}
|
|
}
|
|
}
|
|
|
|
//! GREY-ing the information badges when the task is dragged
|
|
//! moved out of badgeVisualsLoader in order to avoid crashes
|
|
//! when the latte view is removed
|
|
Loader {
|
|
anchors.fill: parent
|
|
active: badgeVisualsLoader.active
|
|
&& taskItem.abilities.environment.isGraphicsSystemAccelerated
|
|
sourceComponent: Colorize{
|
|
source: badgeVisualsLoader.item
|
|
|
|
//! HACK TO AVOID PIXELIZATION
|
|
//! WORKAROUND: When Effects are enabled e.g. BrightnessContrast, Colorize etc.
|
|
//! the icon appears pixelated. It is even most notable when parabolic.factor.zoom === 1
|
|
//! I don't know enabling cached=true helps, but it does.
|
|
//! In Question?
|
|
//cached: true
|
|
|
|
opacity: stateColorizer.opacity
|
|
hue: stateColorizer.hue
|
|
saturation: stateColorizer.saturation
|
|
lightness: stateColorizer.lightness
|
|
}
|
|
}
|
|
//! END: Badges Visuals
|
|
|
|
//! Effects
|
|
Colorize{
|
|
id: stateColorizer
|
|
anchors.fill: parent
|
|
source: badgesLoader.active ? badgesLoader : taskIconItem
|
|
|
|
opacity:0
|
|
|
|
hue:0
|
|
saturation:0
|
|
lightness:0
|
|
}
|
|
|
|
BrightnessContrast{
|
|
id:hoveredImage
|
|
anchors.fill: parent
|
|
|
|
//! HACK TO AVOID PIXELIZATION
|
|
//! WORKAROUND: When Effects are enabled e.g. BrightnessContrast, Colorize etc.
|
|
//! the icon appears pixelated. It is even most notable when parabolic.factor.zoom === 1
|
|
//! I don't know enabling cached=true helps, but it does.
|
|
//! In Question?
|
|
//cached: true
|
|
|
|
source: badgesLoader.active ? badgesLoader : taskIconItem
|
|
|
|
opacity: taskItem.containsMouse && !clickedAnimation.running && !taskItem.abilities.indicators.info.providesHoveredAnimation ? 1 : 0
|
|
brightness: 0.30
|
|
contrast: 0.1
|
|
|
|
Behavior on opacity {
|
|
NumberAnimation { duration: taskItem.abilities.animations.speedFactor.current * taskItem.abilities.animations.duration.large }
|
|
}
|
|
}
|
|
|
|
BrightnessContrast {
|
|
id: brightnessTaskEffect
|
|
anchors.fill: parent
|
|
|
|
//! HACK TO AVOID PIXELIZATION
|
|
//! WORKAROUND: When Effects are enabled e.g. BrightnessContrast, Colorize etc.
|
|
//! the icon appears pixelated. It is even most notable when parabolic.factor.zoom === 1
|
|
//! I don't know enabling cached=true helps, but it does.
|
|
//! In Question?
|
|
//cached: true
|
|
|
|
source: badgesLoader.active ? badgesLoader : taskIconItem
|
|
|
|
visible: clickedAnimation.running
|
|
}
|
|
//! Effects
|
|
|
|
Loader {
|
|
id: dropFilesVisual
|
|
active: applyOpacity>0
|
|
|
|
width: !root.vertical ? length : thickness
|
|
height: !root.vertical ? thickness : length
|
|
anchors.centerIn: parent
|
|
|
|
readonly property int length: taskItem.abilities.metrics.totals.length
|
|
readonly property int thickness: taskItem.abilities.metrics.totals.thickness
|
|
|
|
readonly property real applyOpacity: (mouseHandler.isDroppingSeparator || mouseHandler.isDroppingFiles)
|
|
&& (root.dragSource === null)
|
|
&& (mouseHandler.hoveredItem === taskItem) ? 0.7 : 0
|
|
|
|
sourceComponent: LatteComponents.AddItem {
|
|
anchors.fill: parent
|
|
backgroundOpacity: dropFilesVisual.applyOpacity
|
|
}
|
|
}
|
|
|
|
//! Animations
|
|
TaskAnimations.ClickedAnimation { id: clickedAnimation }
|
|
|
|
TaskAnimations.LauncherAnimation { id:launcherAnimation }
|
|
|
|
TaskAnimations.NewWindowAnimation { id: newWindowAnimation }
|
|
|
|
TaskAnimations.RemoveWindowFromGroupAnimation { id: removingAnimation }
|
|
|
|
TaskAnimations.FastRestoreAnimation { id: fastRestoreAnimation }
|
|
//! Animations
|
|
|
|
Component.onDestruction: {
|
|
taskIcon.toBeDestroyed = true;
|
|
|
|
if(removingAnimation.removingItem)
|
|
removingAnimation.removingItem.destroy();
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
Connections{
|
|
target: taskItem
|
|
|
|
onInAttentionChanged:{
|
|
if (!taskItem.inAttention && newWindowAnimation.running && taskItem.inAttentionAnimation) {
|
|
newWindowAnimation.pause();
|
|
fastRestoreAnimation.start();
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////// States ////////////////////
|
|
states: [
|
|
State{
|
|
name: "*"
|
|
//! since qt 5.14 default state can not use "when" property
|
|
//! it breaks restoring transitions otherwise
|
|
},
|
|
|
|
State{
|
|
name: "isDragged"
|
|
when: taskItem.isDragged
|
|
}
|
|
]
|
|
|
|
//////////// Transitions //////////////
|
|
|
|
readonly property string draggingNeedThicknessEvent: taskIcon + "_dragging"
|
|
|
|
transitions: [
|
|
Transition{
|
|
id: isDraggedTransition
|
|
to: "isDragged"
|
|
property int speed: taskItem.abilities.animations.speedFactor.current * taskItem.abilities.animations.duration.large
|
|
|
|
SequentialAnimation{
|
|
ScriptAction{
|
|
script: {
|
|
taskItem.abilities.animations.needThickness.addEvent(draggingNeedThicknessEvent);
|
|
taskItem.inBlockingAnimation = true;
|
|
taskItem.abilities.parabolic.setDirectRenderingEnabled(false);
|
|
}
|
|
}
|
|
|
|
PropertyAnimation {
|
|
target: taskItem.parabolicItem
|
|
property: "zoom"
|
|
to: 1
|
|
duration: taskItem.abilities.parabolic.factor.zoom === 1 ? 0 : (isDraggedTransition.speed*1.2)
|
|
easing.type: Easing.OutQuad
|
|
}
|
|
|
|
ParallelAnimation{
|
|
PropertyAnimation {
|
|
target: draggedRectangle
|
|
property: "opacity"
|
|
to: 1
|
|
duration: isDraggedTransition.speed
|
|
easing.type: Easing.OutQuad
|
|
}
|
|
|
|
PropertyAnimation {
|
|
target: taskIconItem
|
|
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
|
|
}
|
|
}
|
|
|
|
ScriptAction{
|
|
script: {
|
|
taskItem.abilities.animations.needThickness.removeEvent(draggingNeedThicknessEvent);
|
|
}
|
|
}
|
|
}
|
|
|
|
onRunningChanged: {
|
|
if(running){
|
|
taskItem.animationStarted();
|
|
} else {
|
|
taskItem.abilities.animations.needThickness.removeEvent(draggingNeedThicknessEvent);
|
|
}
|
|
}
|
|
},
|
|
Transition{
|
|
id: defaultTransition
|
|
from: "isDragged"
|
|
to: "*"
|
|
property int speed: taskItem.abilities.animations.speedFactor.current * taskItem.abilities.animations.duration.large
|
|
|
|
SequentialAnimation{
|
|
ScriptAction{
|
|
script: {
|
|
taskItem.abilities.parabolic.setDirectRenderingEnabled(false);
|
|
}
|
|
}
|
|
|
|
ParallelAnimation{
|
|
PropertyAnimation {
|
|
target: draggedRectangle
|
|
property: "opacity"
|
|
to: 0
|
|
duration: defaultTransition.speed
|
|
easing.type: Easing.OutQuad
|
|
}
|
|
|
|
PropertyAnimation {
|
|
target: taskIconItem
|
|
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
|
|
}
|
|
}
|
|
|
|
ScriptAction{
|
|
script: {
|
|
taskItem.inBlockingAnimation = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
onRunningChanged: {
|
|
if(!running){
|
|
taskItem.animationEnded();
|
|
}
|
|
}
|
|
}
|
|
]
|
|
|
|
}
|