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 }