Warning, /plasma/kwin/src/plugins/private/qml/WindowHeapDelegate.qml is written in an unsupported language. File is not indexed.

0001 /*
0002     SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0003     SPDX-FileCopyrightText: 2022 ivan tkachenko <me@ratijas.tk>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 import QtQuick
0009 import QtQuick.Window
0010 import Qt5Compat.GraphicalEffects
0011 import org.kde.kirigami 2.20 as Kirigami
0012 import org.kde.kwin as KWinComponents
0013 import org.kde.kwin.private.effects
0014 import org.kde.plasma.components 3.0 as PC3
0015 import org.kde.plasma.extras as PlasmaExtras
0016 import org.kde.ksvg 1.0 as KSvg
0017 
0018 Item {
0019     id: thumb
0020 
0021     required property QtObject window
0022     required property int index
0023     required property Item windowHeap
0024 
0025     readonly property bool selected: windowHeap.selectedIndex === index
0026     property real partialActivationFactor: effect.partialActivationFactor
0027     property bool gestureInProgress: effect.gestureInProgress
0028 
0029     // no desktops is a special value which means "All Desktops"
0030     readonly property bool presentOnCurrentDesktop: !window.desktops.length || window.desktops.indexOf(KWinComponents.Workspace.currentDesktop) !== -1
0031     readonly property bool initialHidden: window.minimized || !presentOnCurrentDesktop
0032     readonly property bool activeHidden: {
0033         if (window.skipSwitcher) {
0034             return true;
0035         } else if (windowHeap.showOnly === "activeClass") {
0036             if (!KWinComponents.Workspace.activeWindow) {
0037                 return true;
0038             } else {
0039                 return KWinComponents.Workspace.activeWindow.resourceName !== window.resourceName;
0040             }
0041         } else {
0042             return windowHeap.showOnly.length !== 0
0043                 && windowHeap.showOnly.indexOf(window.internalId) === -1;
0044         }
0045     }
0046 
0047     // Show a close button on this thumbnail
0048     property bool closeButtonVisible: true
0049     // Show a text label under this thumbnail
0050     property bool windowTitleVisible: true
0051 
0052     // Same as for window heap
0053     property bool animationEnabled: false
0054 
0055     //scale up and down the whole thumbnail without affecting layouting
0056     property real targetScale: 1.0
0057 
0058     property DragManager activeDragHandler: dragHandler
0059 
0060     // Swipe down gesture by touch, in some effects will close the window
0061     readonly property alias downGestureProgress: touchDragHandler.downGestureProgress
0062     signal downGestureTriggered()
0063 
0064     // "normal" | "pressed" | "drag" | "reparenting"
0065     property string substate: "normal"
0066 
0067     state: {
0068         if (thumb.gestureInProgress) {
0069             return "partial";
0070         }
0071         if (thumb.partialActivationFactor > 0.5 && (cell.isReady || activeHidden)) {
0072             return activeHidden ? "active-hidden" : `active-${substate}`;
0073         }
0074         return initialHidden ? "initial-hidden" : "initial";
0075     }
0076 
0077     visible: opacity > 0
0078     z: (activeDragHandler.active || returning.running) ? 1000
0079         : window.stackingOrder * (presentOnCurrentDesktop ? 1 : 0.001)
0080 
0081     function restoreDND(oldGlobalRect: rect) {
0082         thumbSource.restoreDND(oldGlobalRect);
0083     }
0084 
0085     component TweenBehavior : Behavior {
0086         enabled: thumb.state === "active-normal" && thumb.windowHeap.animationEnabled && thumb.animationEnabled && !thumb.activeDragHandler.active
0087         NumberAnimation {
0088             duration: thumb.windowHeap.animationDuration
0089             easing.type: Easing.InOutCubic
0090         }
0091     }
0092 
0093     TweenBehavior on x {}
0094     TweenBehavior on y {}
0095     TweenBehavior on width {}
0096     TweenBehavior on height {}
0097 
0098     KWinComponents.WindowThumbnail {
0099         id: thumbSource
0100         wId: thumb.window.internalId
0101 
0102         Drag.proposedAction: Qt.MoveAction
0103         Drag.supportedActions: Qt.MoveAction
0104         Drag.source: thumb.window
0105         Drag.hotSpot: Qt.point(
0106             thumb.activeDragHandler.centroid.pressPosition.x * thumb.targetScale,
0107             thumb.activeDragHandler.centroid.pressPosition.y * thumb.targetScale)
0108         Drag.keys: ["kwin-window"]
0109 
0110         onXChanged: effect.checkItemDraggedOutOfScreen(thumbSource)
0111         onYChanged: effect.checkItemDraggedOutOfScreen(thumbSource)
0112 
0113         function saveDND() {
0114             const oldGlobalRect = mapToItem(null, 0, 0, width, height);
0115             thumb.windowHeap.saveDND(thumb.window.internalId, oldGlobalRect);
0116         }
0117         function restoreDND(oldGlobalRect: rect) {
0118             thumb.substate = "reparenting";
0119 
0120             const newGlobalRect = mapFromItem(null, oldGlobalRect);
0121 
0122             x = newGlobalRect.x;
0123             y = newGlobalRect.y;
0124             width = newGlobalRect.width;
0125             height = newGlobalRect.height;
0126 
0127             thumb.substate = "normal";
0128         }
0129         function deleteDND() {
0130             thumb.windowHeap.deleteDND(thumb.window.internalId);
0131         }
0132 
0133         // Not using FrameSvg hover element intentionally for stylistic reasons
0134         Rectangle {
0135             border.width: Kirigami.Units.largeSpacing
0136             border.color: Kirigami.Theme.highlightColor
0137             anchors.fill: parent
0138             anchors.margins: -border.width
0139             radius: border.width
0140             color: "transparent"
0141             visible: !thumb.windowHeap.dragActive && (hoverHandler.hovered || (thumb.selected && Window.window.activeFocusItem)) && windowHeap.effectiveOrganized
0142         }
0143 
0144         MouseArea {
0145             anchors.fill: parent
0146             acceptedButtons: Qt.NoButton
0147             cursorShape: thumb.activeDragHandler.active ? Qt.ClosedHandCursor : Qt.ArrowCursor
0148         }
0149     }
0150 
0151     PC3.Label {
0152         anchors.fill: thumbSource
0153         horizontalAlignment: Text.AlignHCenter
0154         verticalAlignment: Text.AlignVCenter
0155         text: i18nd("kwin", "Drag Down To Close")
0156         opacity: thumbSource.opacity
0157         visible: !thumb.activeHidden && touchDragHandler.active
0158     }
0159 
0160     Kirigami.Icon {
0161         id: icon
0162         width: Kirigami.Units.iconSizes.large
0163         height: Kirigami.Units.iconSizes.large
0164         source: thumb.window.icon
0165         anchors.horizontalCenter: thumbSource.horizontalCenter
0166         anchors.bottom: thumbSource.bottom
0167         anchors.bottomMargin: -Math.round(height / 4)
0168         visible: !thumb.activeHidden && !activeDragHandler.active
0169 
0170 
0171         PlasmaExtras.ShadowedLabel {
0172             id: caption
0173             visible: thumb.windowTitleVisible
0174             width: cell.width
0175             maximumLineCount: 1
0176             anchors.top: parent.bottom
0177             anchors.horizontalCenter: parent.horizontalCenter
0178             elide: Text.ElideRight
0179             text: thumb.window.caption
0180             textFormat: Text.PlainText
0181             horizontalAlignment: Text.AlignHCenter
0182             verticalAlignment: Text.AlignVCenter
0183         }
0184     }
0185 
0186     ExpoCell {
0187         id: cell
0188         layout: windowHeap.layout
0189         enabled: !thumb.activeHidden
0190         naturalX: thumb.window.x
0191         naturalY: thumb.window.y
0192         naturalWidth: thumb.window.width
0193         naturalHeight: thumb.window.height
0194         persistentKey: thumb.window.internalId
0195         bottomMargin: icon.height / 4 + (thumb.windowTitleVisible ? caption.height : 0)
0196         property bool isReady: width !== 0 && height !== 0
0197     }
0198 
0199     states: [
0200         State {
0201             name: "initial"
0202             PropertyChanges {
0203                 target: thumb
0204                 x: thumb.window.x - targetScreen.geometry.x - (thumb.windowHeap.absolutePositioning ?  windowHeap.layout.Kirigami.ScenePosition.x : 0)
0205                 y: thumb.window.y - targetScreen.geometry.y - (thumb.windowHeap.absolutePositioning ?  windowHeap.layout.Kirigami.ScenePosition.y : 0)
0206                 width: thumb.window.width
0207                 height: thumb.window.height
0208             }
0209             PropertyChanges {
0210                 target: thumbSource
0211                 x: 0
0212                 y: 0
0213                 width: thumb.window.width
0214                 height: thumb.window.height
0215             }
0216             PropertyChanges {
0217                 target: icon
0218                 opacity: 0
0219             }
0220             PropertyChanges {
0221                 target: closeButton
0222                 opacity: 0
0223             }
0224         },
0225         State {
0226             name: "partial"
0227             PropertyChanges {
0228                 target: thumb
0229                 x: (thumb.window.x - targetScreen.geometry.x - (thumb.windowHeap.absolutePositioning ?  windowHeap.layout.Kirigami.ScenePosition.x : 0)) * (1 - thumb.partialActivationFactor) + cell.x * thumb.partialActivationFactor
0230                 y: (thumb.window.y - targetScreen.geometry.y - (thumb.windowHeap.absolutePositioning ?  windowHeap.layout.Kirigami.ScenePosition.y : 0)) * (1 - thumb.partialActivationFactor) + cell.y * thumb.partialActivationFactor
0231                 width: thumb.window.width * (1 - thumb.partialActivationFactor) + cell.width * thumb.partialActivationFactor
0232                 height: thumb.window.height * (1 - thumb.partialActivationFactor) + cell.height * thumb.partialActivationFactor
0233                 opacity: thumb.initialHidden
0234                     ? (thumb.activeHidden ? 0 : thumb.partialActivationFactor)
0235                     : (thumb.activeHidden ? 1 - thumb.partialActivationFactor : 1)
0236             }
0237             PropertyChanges {
0238                 target: thumbSource
0239                 x: 0
0240                 y: 0
0241                 width: thumb.width
0242                 height: thumb.height
0243             }
0244             PropertyChanges {
0245                 target: icon
0246                 opacity: thumb.partialActivationFactor
0247             }
0248             PropertyChanges {
0249                 target: closeButton
0250                 opacity: thumb.partialActivationFactor
0251             }
0252         },
0253         State {
0254             name: "initial-hidden"
0255             extend: "initial"
0256             PropertyChanges {
0257                 target: thumb
0258                 opacity: 0
0259             }
0260             PropertyChanges {
0261                 target: icon
0262                 opacity: 0
0263             }
0264             PropertyChanges {
0265                 target: closeButton
0266                 opacity: 0
0267             }
0268         },
0269         State {
0270             name: "active-hidden"
0271             extend: "initial-hidden"
0272         },
0273         State {
0274             // this state is never directly used without a substate
0275             name: "active"
0276             PropertyChanges {
0277                 target: thumb
0278                 x: cell.x
0279                 y: cell.y
0280                 width: cell.width
0281                 height: cell.height
0282             }
0283             PropertyChanges {
0284                 target: icon
0285                 opacity: 1
0286             }
0287             PropertyChanges {
0288                 target: closeButton
0289                 opacity: 1
0290             }
0291         },
0292         State {
0293             name: "active-normal"
0294             extend: "active"
0295             PropertyChanges {
0296                 target: thumbSource
0297                 x: 0
0298                 y: 0
0299                 width: thumb.width
0300                 height: thumb.height
0301             }
0302         },
0303         State {
0304             name: "active-pressed"
0305             extend: "active"
0306             PropertyChanges {
0307                 target: thumbSource
0308                 width: thumb.width
0309                 height: thumb.height
0310             }
0311         },
0312         State {
0313             name: "active-drag"
0314             extend: "active"
0315             PropertyChanges {
0316                 target: thumbSource
0317                 x: -thumb.activeDragHandler.centroid.pressPosition.x * thumb.targetScale +
0318                         thumb.activeDragHandler.centroid.position.x
0319                 y: -thumb.activeDragHandler.centroid.pressPosition.y * thumb.targetScale +
0320                         thumb.activeDragHandler.centroid.position.y
0321                 width: thumb.width * thumb.targetScale
0322                 height: thumb.height * thumb.targetScale
0323             }
0324         },
0325         State {
0326             name: "active-reparenting"
0327             extend: "active"
0328         }
0329     ]
0330 
0331     transitions: [
0332         Transition {
0333             id: returning
0334             from: "active-drag, active-reparenting"
0335             to: "active-normal"
0336             enabled: thumb.windowHeap.animationEnabled
0337             NumberAnimation {
0338                 duration: thumb.windowHeap.animationDuration
0339                 properties: "x, y, width, height"
0340                 easing.type: Easing.InOutCubic
0341             }
0342         }
0343     ]
0344 
0345     HoverHandler {
0346         id: hoverHandler
0347         onHoveredChanged: if (hovered !== selected) {
0348             thumb.windowHeap.resetSelected();
0349         }
0350     }
0351 
0352     TapHandler {
0353         acceptedButtons: Qt.LeftButton
0354         onTapped: {
0355             KWinComponents.Workspace.activeWindow = thumb.window;
0356             thumb.windowHeap.activated();
0357         }
0358         onPressedChanged: {
0359             if (pressed) {
0360                 var saved = Qt.point(thumbSource.x, thumbSource.y);
0361                 thumbSource.Drag.active = true;
0362                 thumb.substate = "pressed";
0363                 thumbSource.x = saved.x;
0364                 thumbSource.y = saved.y;
0365             } else if (!thumb.activeDragHandler.active) {
0366                 thumbSource.Drag.active = false;
0367                 thumb.substate = "normal";
0368             }
0369         }
0370     }
0371 
0372     component DragManager : DragHandler {
0373         target: null
0374         grabPermissions: PointerHandler.CanTakeOverFromAnything
0375         // This does not work when moving pointer fast and pressing along the way
0376         // See also QTBUG-105903, QTBUG-105904
0377         // enabled: thumb.state !== "active-normal"
0378 
0379         onActiveChanged: {
0380             thumb.windowHeap.dragActive = active;
0381             if (active) {
0382                 thumb.activeDragHandler = this;
0383                 thumb.substate = "drag";
0384             } else {
0385                 thumbSource.saveDND();
0386 
0387                 var action = thumbSource.Drag.drop();
0388                 if (action === Qt.MoveAction) {
0389                     // This whole component is in the process of being destroyed due to drop onto
0390                     // another virtual desktop (not another screen).
0391                     if (typeof thumbSource !== "undefined") {
0392                         // Except the case when it was dropped on the same desktop which it's already on, so let's return to normal state anyway.
0393                         thumbSource.deleteDND();
0394                         thumb.substate = "normal";
0395                     }
0396                     return;
0397                 }
0398 
0399                 var globalPos = targetScreen.mapToGlobal(centroid.scenePosition);
0400                 effect.checkItemDroppedOutOfScreen(globalPos, thumbSource);
0401 
0402                 if (typeof thumbSource !== "undefined") {
0403                     // else, return to normal without reparenting
0404                     thumbSource.deleteDND();
0405                     thumb.substate = "normal";
0406                 }
0407             }
0408         }
0409     }
0410 
0411     DragManager {
0412         id: dragHandler
0413         acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad | PointerDevice.Stylus
0414     }
0415 
0416     DragManager {
0417         id: touchDragHandler
0418         acceptedDevices: PointerDevice.TouchScreen
0419         readonly property double downGestureProgress: {
0420             if (!active) {
0421                 return 0.0;
0422             }
0423 
0424             const startDistance = thumb.windowHeap.Kirigami.ScenePosition.y + thumb.windowHeap.height - centroid.scenePressPosition.y;
0425             const localPosition = thumb.windowHeap.Kirigami.ScenePosition.y + thumb.windowHeap.height - centroid.scenePosition.y;
0426             return 1 - Math.min(localPosition/startDistance, 1);
0427         }
0428 
0429         onActiveChanged: {
0430             if (!active) {
0431                 if (downGestureProgress > 0.6) {
0432                     thumb.downGestureTriggered();
0433                 }
0434             }
0435         }
0436     }
0437 
0438     Loader {
0439         id: closeButton
0440         LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft
0441 
0442         anchors {
0443             right: thumbSource.right
0444             top: thumbSource.top
0445             margins: Kirigami.Units.smallSpacing
0446         }
0447         active: thumb.closeButtonVisible && (hoverHandler.hovered || Kirigami.Settings.tabletMode || Kirigami.Settings.hasTransientTouchInput) && thumb.window.closeable && !thumb.activeDragHandler.active
0448 
0449         sourceComponent: PC3.Button {
0450             text: i18ndc("kwin", "@info:tooltip as in: 'close this window'", "Close window")
0451             icon.name: "window-close"
0452             display: PC3.AbstractButton.IconOnly
0453 
0454             PC3.ToolTip.text: text
0455             PC3.ToolTip.visible: hovered && display === PC3.AbstractButton.IconOnly
0456             PC3.ToolTip.delay: Kirigami.Units.toolTipDelay
0457             Accessible.name: text
0458 
0459             onClicked: thumb.window.closeWindow();
0460         }
0461     }
0462 
0463     Component.onDestruction: {
0464         if (selected) {
0465             windowHeap.resetSelected();
0466         }
0467     }
0468 }