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

919 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"
} */
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;
}
/* Rectangle{
anchors.fill: parent
border.width: 1
border.color: "red"
color: "transparent"
} */
// KQuickControlAddons.QIconItem{
Item{
id: iconGraphic
//width: iconImageBuffer.width
//height: iconImageBuffer.height
width: parent.width
height: parent.height
Latte.IconItem{
id: iconImageBuffer
anchors.rightMargin: root.position === PlasmaCore.Types.RightPositioned
|| (root.position === PlasmaCore.Types.LeftPositioned
&& mainItemContainer.inAddRemoveAnimation)
? margin : 0
anchors.leftMargin: root.position === PlasmaCore.Types.LeftPositioned
|| (root.position === PlasmaCore.Types.RightPositioned
&& mainItemContainer.inAddRemoveAnimation)
? margin : 0
anchors.topMargin: root.position === PlasmaCore.Types.TopPositioned
|| (root.position === PlasmaCore.Types.BottomPositioned
&& mainItemContainer.inAddRemoveAnimation)
? margin : 0
anchors.bottomMargin: root.position === PlasmaCore.Types.BottomPositioned
|| (root.position === PlasmaCore.Types.TopPositioned
&& mainItemContainer.inAddRemoveAnimation)
? margin : 0
width: Math.round(newTempSize) //+ 2*centralItem.shadowSize
height: Math.round(width)
//icon: decoration
source: decoration
property int margin: root.iconMargin/2
//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)
///states for launcher animation
states: [
State{
name: "*"
when: !launcherAnimation.running && !newWindowAnimation.running && !mainItemContainer.inAddRemoveAnimation
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: mainItemContainer.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) && !mainItemContainer.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: "*"
AnchorAnimation { duration: 1.5*root.durationTime*units.longDuration }
}
]
}
//////
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{
id: taskWithShadow
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
}
///////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(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 = root.mapFromItem(iconImageBuffer,0,0);
var removingItem = removeTaskComponent.createObject(root);
removingItem.x = relavantPoint.x;
removingItem.y = relavantPoint.y;
removingItem.start();
}
function removeTask(){
if(!centralItem.toBeDestroyed && plasmoid.configuration.showShadows && Latte.WindowSystem.compositingActive){
removingAnimation.init();
}
}
Component.onCompleted: {
mainItemContainer.groupWindowRemoved.connect(removeTask);
}
///////////// Component for animating removing window from group
Component {
id: removeTaskComponent
Item{
id: removeTask
width: iconImageBuffer.width
height: iconImageBuffer.height
//parent: panel
visible: false
Latte.IconItem{
id: tempRemoveIcon
anchors.centerIn: parent
width: iconImageBuffer.width
height: width
source: decoration
}
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--;
}
}
}
]
}// Icon Item