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.
latte-dock/plasmoid/contents/ui/TaskIconItem.qml

998 lines
32 KiB
QML

/*
* Copyright 2016 Smith AR <audoban@openmailbox.org>
* 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.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.1 as Latte
//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: centralItem
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 necessarty 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 : Math.ceil(root.iconSize / 10)
readonly property bool smartLauncherEnabled: ((mainItemContainer.isStartup === false) && (root.smartLaunchersEnabled))
readonly property variant iconDecoration: decoration
property QtObject buffers: null
property QtObject smartLauncherItem: null
/* Rectangle{
anchors.fill: parent
border.width: 1
border.color: "green"
color: "transparent"
} */
Connections{
target: root
onZoomFactorChanged: updateImages()
onIconSizeChanged: updateImages()
onEnableShadowsChanged: updateImages()
}
onIconDecorationChanged: {
updateImages();
}
onSmartLauncherEnabledChanged: {
if (smartLauncherEnabled && !smartLauncherItem) {
var smartLauncher = Qt.createQmlObject(
" import org.kde.plasma.private.taskmanager 0.1 as TaskManagerApplet; TaskManagerApplet.SmartLauncherItem { }",
centralItem);
smartLauncher.launcherUrl = Qt.binding(function() { return model.LauncherUrlWithoutIcon; });
smartLauncherItem = smartLauncher;
} else if (!smartLauncherEnabled && smartLauncherItem) {
smartLauncherItem.destroy();
smartLauncherItem = null;
}
}
Rectangle{
id: draggedRectangle
width: iconImageBuffer.width+1
height: 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;
}
//temporary buffers containing the normal Image icon and the zoomed Image icon
// Image{id:zoomedImage; visible:false}
// Image{id:normalImage; visible:false}
Image{
id:shadowedImage
anchors.centerIn:iconGraphic
// anchors.horizontalCenter: iconImageBuffer.horizontalCenter
//anchors.verticalCenter: iconImageBuffer.verticalCenter
width:iconImageBuffer.width+2*shadowSize
height:iconImageBuffer.height+2*shadowSize
//visible: plasmoid.configuration.showShadows
visible: false
states: State {
name: "reparented"
ParentChange { target: shadowedImage; parent: root; }
}
//Corize to use when a window is removed....
/* Colorize{
id: removeImageColorizer
source: parent
anchors.fill: parent
enabled: false
visible: false
hue: 0
saturation: 0
lightness: 0
}*/
}
/* Rectangle{
anchors.fill: parent
border.width: 1
border.color: "red"
color: "transparent"
} */
// KQuickControlAddons.QIconItem{
Item{
id: iconGraphic
width: iconImageBuffer.width
height: iconImageBuffer.height
///states for launcher animation
states: [
State{
name: "*"
when: !launcherAnimation.running && !newWindowAnimation.running
AnchorChanges{
target:iconGraphic;
anchors.horizontalCenter: parent.horizontalCenter;
anchors.verticalCenter: parent.verticalCenter;
anchors.right: undefined;
anchors.left: undefined;
anchors.top: undefined;
anchors.bottom: undefined;
}
},
State{
name: "animating"
when: launcherAnimation.running || newWindowAnimation.running
AnchorChanges{
target:iconGraphic;
anchors.horizontalCenter: undefined;
anchors.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: "*"
AnchorAnimation { duration: 1.5*root.durationTime*units.longDuration }
}
]
Latte.IconItem{
id: iconImageBuffer
// anchors.centerIn: parent
width: Math.round(newTempSize) //+ 2*centralItem.shadowSize
height: Math.round(width)
//icon: decoration
source: decoration
//visible: !root.enableShadows
onValidChanged: {
if (!valid && (source === decoration || source === "unknown"))
source = "application-x-executable"
}
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: (wrapper.opacity == 1) ? Math.min(basicScalingWidth, basicScalingHeight) :
Math.max(basicScalingWidth, basicScalingHeight)
}
//////
Loader{
id: progressLoader
anchors.fill: parent
active: (centralItem.smartLauncherEnabled && centralItem.smartLauncherItem
&& centralItem.smartLauncherItem.progressVisible)
asynchronous: true
sourceComponent: Item{
ShaderEffect {
id: iconOverlay
enabled: false
anchors.fill: parent
property var source: ShaderEffectSource {
sourceItem: iconImageBuffer
hideSource: true
}
property var mask: ShaderEffectSource {
sourceItem: Item{
width: iconImageBuffer.width
height: iconImageBuffer.height
Rectangle{
id: maskRect
width: parent.width/2
height: width
radius: width
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;}
}
}
]
Connections{
target: plasmoid
onLocationChanged: iconOverlay.mask.scheduleUpdate();
}
}
//badgeMask
}
hideSource: true
live: false
}
// onWidthChanged: mask.scheduleUpdate();
// onHeightChanged: mask.scheduleUpdate();
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;
}
"
}
TaskProgressOverlay{
anchors.fill:parent
}
}
}
// Loader {
// anchors.fill: parent
//// asynchronous: true
// source: "TaskProgressOverlay.qml"
// active: true
//active: (centralItem.smartLauncherEnabled && centralItem.smartLauncherItem
// && centralItem.smartLauncherItem.progressVisible)
//}
}
///Shadow in tasks
Loader{
anchors.fill: iconGraphic
active: root.enableShadows
sourceComponent: DropShadow{
anchors.fill: parent
color: "#ff080808"
samples: 2 * radius
source: iconGraphic
radius: centralItem.shadowSize
verticalOffset: 2
}
}
VisualAddItem{
id: dropFilesVisual
anchors.fill: iconGraphic
visible: opacity == 0 ? false : true
opacity: root.dropNewLauncher && !mouseHandler.onlyLaunchers
&& (root.dragSource == null) && (mouseHandler.hoveredItem === mainItemContainer) ? 1 : 0
}
BrightnessContrast{
id:hoveredImage
opacity: mainItemContainer.containsMouse ? 1 : 0
anchors.fill: iconGraphic
brightness: 0.25
source: iconGraphic
Behavior on opacity {
NumberAnimation { duration: root.durationTime*units.longDuration }
}
}
BrightnessContrast {
id: brightnessTaskEffect
anchors.fill: iconGraphic
source: iconGraphic
visible: clickedAnimation.running
}
Colorize{
id: stateColorizer
source: progressLoader.active ? progressLoader : iconImageBuffer
anchors.fill: iconGraphic
//visible: false
opacity:0
hue:0
saturation:0
lightness:0
}
//Something to show until the buffers are updated
//KQuickControlAddons.QIconItem{
/*
PlasmaCore.IconItem{
id: iconImageBackground
//property real relatedSize: root.iconSize * ( (doubleSize - 7) / doubleSize );
// width: (visible) ? relatedSize * wrapper.scale : root.iconSize
width: (visible) ? root.iconSize * wrapper.scale : root.iconSize
height: width
anchors.centerIn: parent
// state: wrapper.containsMouse ? KQuickControlAddons.QIconItem.ActiveState : KQuickControlAddons.QIconItem.DefaultState
// icon: decoration
active: wrapper.containsMouse
enabled: true
source: decoration
usesPlasmaTheme: false
visible: ((iconImageBuffer.opacity == 1) && (root.enableShadows)) ? false : true
Component{
id:hideBackTimer
Timer{
id:hideBackgroundTimer
repeat:false
interval: centralItem.shadowInterval
onTriggered: {
// iconImageBackground.visible = false;
iconImageBuffer.opacity = 1;
hideBackgroundTimer.destroy();
// iconImageBuffer.visible = false;
}
Component.onCompleted: hideBackgroundTimer.start();
}
}
}*/
Loader{
id:defaultWithShadow
//sourceComponent: imageBufferingComponent
sourceComponent: TaskIconBuffers{}
active: mainItemContainer.isStartup ? false : true
}
///////Activate animation/////
SequentialAnimation{
id: clickedAnimation
property bool pressed: mainItemContainer.pressed
property int speed: root.durationTime*units.longDuration
ParallelAnimation{
PropertyAnimation {
target: brightnessTaskEffect
property: "brightness"
to: -0.5
duration: clickedAnimation.speed
easing.type: Easing.OutQuad
}
PropertyAnimation {
target: wrapper
property: "mScale"
to: root.taskInAnimation ? 0.9 : wrapper.mScale - (root.zoomFactor - 1) / 2
duration: clickedAnimation.speed
easing.type: Easing.OutQuad
}
}
ParallelAnimation{
PropertyAnimation {
target: brightnessTaskEffect
property: "brightness"
to: 0
duration: clickedAnimation.speed
easing.type: Easing.OutQuad
}
PropertyAnimation {
target: wrapper
property: "mScale"
to: root.taskInAnimation ? 1 : root.zoomFactor
duration: clickedAnimation.speed
easing.type: Easing.OutQuad
}
}
onPressedChanged: {
if( (pressed)&&
((mainItemContainer.lastButtonClicked == Qt.LeftButton)||(mainItemContainer.lastButtonClicked == Qt.MidButton)) ){
mainItemContainer.animationStarted();
start();
}
}
onStopped: {
if( !mainItemContainer.isDragged){
mainItemContainer.animationEnded();
checkListHovered.startDuration(6*units.longDuration);
}
}
}
Component.onCompleted: {
}
Component.onDestruction: {
centralItem.toBeDestroyed = true;
if(shadowedImage && shadowedImage.source)
shadowedImage.source.destroy();
if(removingAnimation.removingItem)
removingAnimation.removingItem.destroy();
gc();
}
////end of activate animation////
////bouncing task, e.g. on launcher activating and when a new window is
////added in a group task
SequentialAnimation{
id:launcherAnimation
property bool launchedAlready: false
property int speed: root.durationTime * 0.8 * units.longDuration
SequentialAnimation{
ParallelAnimation{
PropertyAnimation {
target: wrapper
property: (icList.orientation == Qt.Vertical) ? "tempScaleWidth" : "tempScaleHeight"
to: root.zoomFactor
duration: launcherAnimation.speed
easing.type: Easing.OutQuad
}
PropertyAnimation {
target: wrapper
property: (icList.orientation == Qt.Horizontal) ? "tempScaleWidth" : "tempScaleHeight"
to: 1
duration: launcherAnimation.speed
easing.type: Easing.OutQuad
}
}
PropertyAnimation {
target: wrapper
property: (icList.orientation == Qt.Vertical) ? "tempScaleWidth" : "tempScaleHeight"
to: 1
duration: 3*root.durationTime*launcherAnimation.speed
easing.type: Easing.OutBounce
}
ParallelAnimation{
PropertyAnimation {
target: wrapper
property: (icList.orientation == Qt.Vertical) ? "tempScaleHeight" : "tempScaleWidth"
to: 1
duration: root.durationTime*launcherAnimation.speed
easing.type: Easing.OutBounce
}
PropertyAnimation {
target: wrapper
property: "mScale"
to: 1
duration: root.durationTime*launcherAnimation.speed
easing.type: Easing.OutQuad
}
}
}
onStopped: {
//wrapper.mScale = 1;
/* if ( root.noTasksInAnimation>0 ) {
root.noTasksInAnimation--;
}
if ( root.animations>0 ) {
root.animations--;
}*/
//console.log ("Nooo 2: "+root.noTasksInAnimation + " - "+root.animations);
clearAnimationsSignals();
mainItemContainer.setBlockingAnimation(false);
mainItemContainer.animationEnded();
mainItemContainer.launcherAction();
}
function clearAnimationsSignals() {
if ( launchedAlready && root.noTasksInAnimation>0 ) {
root.noTasksInAnimation--;
}
if ( launchedAlready ) {
root.signalAnimationsNeedThickness(-1);
}
launchedAlready = false;
}
function init(){
//console.log ("Nooo 1 : "+root.noTasksInAnimation);
if(!launchedAlready) {
launchedAlready = true;
root.signalAnimationsNeedThickness(1);
root.noTasksInAnimation++;
mainItemContainer.setBlockingAnimation(true);
}
wrapper.tempScaleWidth = wrapper.mScale;
wrapper.tempScaleHeight = wrapper.mScale;
icList.hoveredIndex = -1;
}
function bounceLauncher(){
if(root.zoomFactor > 1){
mainItemContainer.animationStarted();
init();
start();
}
else
stopped();
}
Component.onCompleted: {
wrapper.runLauncherAnimation.connect(bounceLauncher);
}
Component.onDestruction: {
clearAnimationsSignals();
}
}
/////////////////// end of launcher animation
////////////////// new window and needs attention animation
//stop the bouncing animation for attention needed when the plasmoid
//does not need any more attention
Connections{
target: plasmoid
onStatusChanged:{
if ( (plasmoid.status === PlasmaCore.Types.PassiveStatus)
&& newWindowAnimation.running && (newWindowAnimation.loops > 2) ) {
newWindowAnimation.clear();
}
}
}
SequentialAnimation{
id:newWindowAnimation
property int speed: root.durationTime*units.longDuration
property bool isDemandingAttention: (IsDemandingAttention === true)
property bool entered: mainItemContainer.mouseEntered
property bool needsThicknessSent: false //flag to check if the signal for thickness was sent
SequentialAnimation{
ParallelAnimation{
PropertyAnimation {
target: wrapper
property: (icList.orientation == Qt.Vertical) ? "tempScaleWidth" : "tempScaleHeight"
to: 1 + (0.6 * (root.zoomFactor-1))
duration: newWindowAnimation.speed
easing.type: Easing.OutQuad
}
PropertyAnimation {
target: wrapper
property: (icList.orientation == Qt.Horizontal) ? "tempScaleWidth" : "tempScaleHeight"
to: 1
duration: newWindowAnimation.speed
easing.type: Easing.OutQuad
}
}
PropertyAnimation {
target: wrapper
property: (icList.orientation == Qt.Vertical) ? "tempScaleWidth" : "tempScaleHeight"
to: 1
duration: 3*root.durationTime*newWindowAnimation.speed
easing.type: Easing.OutBounce
}
}
function clear(){
loops = 1;
newWindowAnimation.stop();
// iconImageBuffer.anchors.centerIn = iconImageBuffer.parent;
wrapper.tempScaleWidth = 1;
wrapper.tempScaleHeight = 1;
}
onStopped: {
sendEndOfNeedThicknessAnimation();
clear();
}
onIsDemandingAttentionChanged: {
if( (!isDemandingAttention)&&(running)){
clear();
// wrapper.animationEnded();
}
else if(isDemandingAttention){
bounceNewWindow();
}
}
function sendEndOfNeedThicknessAnimation(){
if (needsThicknessSent) {
needsThicknessSent = false;
root.signalAnimationsNeedThickness(-1);
}
}
function init(){
wrapper.tempScaleWidth = wrapper.mScale;
wrapper.tempScaleHeight = wrapper.mScale;
if(!isDemandingAttention)
loops = 2;
else
loops = 20;
if (!needsThicknessSent) {
needsThicknessSent = true;
root.signalAnimationsNeedThickness(1);
}
// icList.hoveredIndex = -1;
}
function bounceNewWindow(){
if (isDemandingAttention || !root.dockIsHidden) {
newWindowAnimation.init();
start();
}
}
Component.onCompleted: {
mainItemContainer.groupWindowAdded.connect(bounceNewWindow);
}
Component.onDestruction: {
sendEndOfNeedThicknessAnimation();
}
}
/////Removing a Window from a group////
Item{
id:removingAnimation
function init(){
var relavantPoint
if(plasmoid.configuration.showShadows)
relavantPoint = root.mapFromItem(shadowedImage,0,0);
else
relavantPoint = root.mapFromItem(iconImageBuffer,0,0);
var removingItem = removeTaskComponent.createObject(root);
removingItem.x = relavantPoint.x;
removingItem.y = relavantPoint.y;
removingItem.start();
}
function removeTask(){
if(centralItem.firstDrawed && !centralItem.toBeDestroyed
&& mainItemContainer.buffersAreReady && plasmoid.configuration.showShadows
&& windowSystem.compositingActive){
removingAnimation.init();
}
}
Component.onCompleted: {
mainItemContainer.groupWindowRemoved.connect(removeTask);
}
///////////// Component for animating removing window from group
Component {
id: removeTaskComponent
Item{
id: removeTask
width: plasmoid.configuration.showShadows ? shadowedImage.width : iconImageBuffer.width
height: plasmoid.configuration.showShadows ? shadowedImage.height : iconImageBuffer.height
//parent: panel
visible: false
Image {
id: tempRemoveIcon
source: plasmoid.configuration.showShadows ? shadowedImage.source : iconImageBuffer.source
anchors.fill: parent
}
Colorize{
source: tempRemoveIcon
anchors.fill: tempRemoveIcon
hue: 0
saturation: 0
lightness: 0
}
ParallelAnimation{
id: componentRemoveAnimation
property int speed: 2*root.durationTime*units.longDuration
property Item removingItem: parent
property int toPoint: 0
PropertyAnimation {
target: removeTask
property: "opacity"
to: 0
duration: componentRemoveAnimation.speed
easing.type: Easing.InQuad
}
PropertyAnimation {
target: removeTask
property: (icList.orientation == Qt.Horizontal) ? "y" : "x"
to: componentRemoveAnimation.toPoint
duration: componentRemoveAnimation.speed
easing.type: Easing.InQuad
}
onStopped: {
removeTask.destroy();
gc();
}
}
function start(){
var tempPoint = 0;
if(icList.orientation == Qt.Horizontal)
tempPoint = y;
else
tempPoint = x;
if( (root.position === PlasmaCore.Types.BottomPositioned) ||
(root.position === PlasmaCore.Types.RightPositioned) ){
componentRemoveAnimation.toPoint = tempPoint + root.iconSize;
}
else{
componentRemoveAnimation.toPoint = tempPoint - root.iconSize;
}
visible = true;
componentRemoveAnimation.start();
}
}
}
}
//////////// States ////////////////////
states: [
State{
name: "*"
when: !mainItemContainer.isDragged
},
State{
name: "isDragged"
when: ( (mainItemContainer.isDragged) && (!root.editMode) )
// PropertyChanges { target: clickedAnimation; running:false }
PropertyChanges { target: wrapper; mScale:1 + ((root.zoomFactor - 1) / 2)}
}
]
//////////// Transitions //////////////
transitions: [
Transition{
id: isDraggedTransition
to: "isDragged"
property int speed: root.durationTime*units.longDuration
SequentialAnimation{
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: 1
duration: isDraggedTransition.speed
easing.type: Easing.OutQuad
}
}
}
onRunningChanged: {
if(running){
mainItemContainer.animationStarted();
//root.animations++;
root.updateScale(index-1, 1, 0);
root.updateScale(index+1, 1, 0);
}
}
},
Transition{
id: defaultTransition
from: "isDragged"
to: "*"
property int speed: root.durationTime*units.longDuration
SequentialAnimation{
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
}
}
onRunningChanged: {
if(!running){
var halfZoom = 1 + ((root.zoomFactor - 1) / 2);
root.updateScale(index-1, halfZoom, 0);
root.updateScale(index+1, halfZoom, 0);
mainItemContainer.animationEnded();
// root.animations--;
}
}
}
]
//////////////////////////
function updateImages(){
if(root){
if(defaultWithShadow.item){
defaultWithShadow.item.updateImage();
}
}
}
}// Icon Item