Warning, /plasma/kdeplasma-addons/kwin/windowswitchers/flipswitch/contents/ui/main.qml is written in an unsupported language. File is not indexed.

0001 /*
0002  SPDX-FileCopyrightText: 2021 Ismael Asensio <isma.af@gmail.com>
0003 
0004  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 import QtQuick 2.15
0008 import QtQuick.Controls 2.15 as QQC2
0009 import Qt5Compat.GraphicalEffects
0010 import QtQuick.Layouts 1.15
0011 import QtQuick.Window 2.15
0012 
0013 import org.kde.kirigami 2.20 as Kirigami
0014 import org.kde.ksvg 1.0 as KSvg
0015 import org.kde.plasma.components 3.0 as PC3
0016 
0017 import org.kde.kwin 3.0 as KWin
0018 import org.kde.kwin.private.effects 1.0
0019 
0020 
0021 KWin.TabBoxSwitcher {
0022     id: tabBox
0023     currentIndex: thumbnailView ? thumbnailView.currentIndex : -1
0024 
0025     // TODO: Make it user configurable ?
0026     property bool enableBlur: true
0027 
0028     Window {
0029         id: window
0030 
0031         x: tabBox.screenGeometry.x
0032         y: tabBox.screenGeometry.y
0033         width: tabBox.screenGeometry.width
0034         height: tabBox.screenGeometry.height
0035         flags: Qt.BypassWindowManagerHint | Qt.FramelessWindowHint
0036         visibility: Window.FullScreen
0037         // Workaround QTBUG-35244. Do not directly assign here to avoid warning
0038         visible: true
0039 
0040         color: "transparent"
0041 
0042         KWin.DesktopBackground {
0043             activity: KWin.Workspace.currentActivity
0044             desktop: KWin.Workspace.currentVirtualDesktop
0045             outputName: window.screen.name
0046 
0047             layer.enabled: true
0048             layer.effect: FastBlur {
0049                 radius: enableBlur ? 64 : 0
0050             }
0051         }
0052 
0053         Rectangle {
0054             anchors {
0055                 top: enableBlur ? parent.top : infoBar.top
0056                 topMargin: enableBlur ? 0 : -infoBar.anchors.bottomMargin
0057                 left: parent.left
0058                 right: parent.right
0059                 bottom: parent.bottom
0060             }
0061             color: Kirigami.Theme.backgroundColor
0062             opacity: enableBlur ? 0.5 : 0.75
0063         }
0064 
0065         PathView {
0066             id: thumbnailView
0067 
0068             readonly property int visibleCount: Math.min(count, pathItemCount)
0069             // Make thumbnails slightly smaller the more there are, so it doesn't feel too crowded
0070             // The sizeFactor curve parameters have been calculated experimentally
0071             readonly property real boxScaleFactor: 0.35 + (0.5 / (visibleCount + 1))
0072             readonly property int boxWidth: tabBox.screenGeometry.width * boxScaleFactor
0073             readonly property int boxHeight: tabBox.screenGeometry.height * boxScaleFactor
0074 
0075             focus: true
0076 
0077             anchors.fill: parent
0078 
0079             preferredHighlightBegin: 1/(visibleCount + 1)
0080             preferredHighlightEnd: preferredHighlightBegin
0081             highlightRangeMode: PathView.StrictlyEnforceRange
0082 
0083             // This property sets the animation duration between the current position to the next one,
0084             // without taking into account how much distance the thumbnails travel in that time.
0085             // To compensate the speed, we slowly reduce the duration with the number of thumbnails,
0086             // starting from `veryLongDuration` when there are 2 of them
0087             highlightMoveDuration: Kirigami.Units.veryLongDuration * (2 / Math.sqrt(visibleCount + 1))
0088 
0089             pathItemCount: 12
0090 
0091             path: Path {
0092                 // Nearest point of the path
0093                 startX: Math.round(thumbnailView.width * 0.75)
0094                 startY: Math.round(thumbnailView.height * 0.80)
0095                 PathAttribute { name: "progress"; value: 1 }
0096                 PathAttribute { name: "scale"; value: 1 }
0097 
0098                 // Back point of the path on top-left corner
0099                 PathLine {
0100                     x: Math.round(thumbnailView.width * 0.25)
0101                     y: Math.round(thumbnailView.height * 0.20)
0102                 }
0103                 PathAttribute { name: "progress"; value: 0 }
0104                 PathAttribute { name: "scale"; value: 0.6 }
0105             }
0106 
0107             model: tabBox.model
0108 
0109             delegate: Item {
0110                 readonly property string caption: model.caption
0111                 readonly property var icon: model.icon
0112 
0113                 readonly property real scaleFactor: {
0114                     if (thumbnail.implicitWidth < thumbnailView.boxWidth && thumbnail.implicitHeight < thumbnailView.boxHeight) {
0115                         // Do not scale up thumbnails smaller than the box frame
0116                         return 1;
0117                     } else if (thumbnail.ratio > thumbnailView.boxWidth / thumbnailView.boxHeight) {
0118                         // Thumbnail is wider than the box
0119                         return thumbnailView.boxWidth / thumbnail.implicitWidth;
0120                     } else {
0121                         // Thumbnail is taller than the box
0122                         return thumbnailView.boxHeight / thumbnail.implicitHeight;
0123                     }
0124                 }
0125 
0126                 width: Math.round(thumbnail.implicitWidth * scaleFactor)
0127                 height: Math.round(thumbnail.implicitHeight * scaleFactor)
0128                 scale: PathView.onPath ? PathView.scale : 0
0129                 z: PathView.onPath ? Math.floor(PathView.progress * thumbnailView.visibleCount) : -1
0130 
0131                 // Reduce opacity on the end so items dissapear more naturally
0132                 opacity: Math.min(1, (1 - PathView.progress) / thumbnailView.preferredHighlightBegin);
0133 
0134                 KWin.WindowThumbnail {
0135                     id: thumbnail
0136                     readonly property double ratio: implicitWidth / implicitHeight
0137 
0138                     wId: windowId
0139                     anchors.fill: parent
0140                 }
0141 
0142                 Kirigami.ShadowedRectangle {
0143                     anchors.fill: parent
0144                     z: -1
0145 
0146                     color: "transparent"
0147                     shadow.size: Kirigami.Units.gridUnit
0148                     shadow.color: "black"
0149                     opacity: 0.5
0150                     shadow.yOffset: 1
0151                 }
0152 
0153                 TapHandler {
0154                     grabPermissions: PointerHandler.TakeOverForbidden
0155                     gesturePolicy: TapHandler.WithinBounds
0156                     onSingleTapped: {
0157                         if (index === thumbnailView.currentIndex) {
0158                             thumbnailView.model.activate(index);
0159                             return;
0160                         }
0161                         thumbnailView.movementDirection = PathView.Positive
0162                         thumbnailView.currentIndex = index
0163                     }
0164                 }
0165             }
0166 
0167             transform: Rotation {
0168                 origin { x: thumbnailView.width/2; y: thumbnailView.height/2 }
0169                 axis { x: 0; y: 1; z: -0.15 }
0170                 angle: 10
0171             }
0172 
0173             highlight: KSvg.FrameSvgItem {
0174                 imagePath: "widgets/viewitem"
0175                 prefix: "hover"
0176 
0177                 readonly property Item target: thumbnailView.currentItem
0178 
0179                 visible: target !== null
0180                 anchors.centerIn: target
0181                 width: target ? target.width + 6 * Kirigami.Units.smallSpacing : 0
0182                 height: target ? target.height + 6 * Kirigami.Units.smallSpacing : 0
0183                 scale: target ? target.scale : 1
0184                 z: target ? target.z - 0.5 : -0.5
0185             }
0186 
0187             layer.enabled: true
0188             layer.smooth: true
0189 
0190             onMovementStarted: movementDirection = PathView.Shortest
0191 
0192             Keys.onUpPressed: decrementCurrentIndex()
0193             Keys.onLeftPressed: decrementCurrentIndex()
0194             Keys.onDownPressed: incrementCurrentIndex()
0195             Keys.onRightPressed: incrementCurrentIndex()
0196         }
0197 
0198         RowLayout {
0199             id: infoBar
0200 
0201             height: Kirigami.Units.iconSizes.large
0202             spacing: Kirigami.Units.gridUnit
0203 
0204             anchors {
0205                 horizontalCenter: parent.horizontalCenter
0206                 bottom: parent.bottom
0207                 margins: Kirigami.Units.gridUnit
0208             }
0209 
0210             Kirigami.Icon {
0211                 source: thumbnailView.currentItem ? thumbnailView.currentItem.icon : ""
0212                 implicitWidth: Kirigami.Units.iconSizes.large
0213                 implicitHeight: Kirigami.Units.iconSizes.large
0214                 Layout.alignment: Qt.AlignCenter
0215             }
0216 
0217             PC3.Label {
0218                 font.bold: true
0219                 font.pointSize: Math.round(Kirigami.Theme.defaultFont.pointSize * 1.6)
0220                 text: thumbnailView.currentItem ? thumbnailView.currentItem.caption : ""
0221                 textFormat: Text.PlainText
0222                 maximumLineCount: 1
0223                 elide: Text.ElideMiddle
0224                 Layout.maximumWidth: tabBox.screenGeometry.width * 0.8
0225                 Layout.alignment: Qt.AlignCenter
0226             }
0227         }
0228     }
0229 
0230     onCurrentIndexChanged: {
0231         if (currentIndex === thumbnailView.currentIndex) {
0232             return
0233         }
0234 
0235         // WindowSwitcher always changes currentIndex in increments of 1.
0236         // Detect the change direction and set the PathView movement accordingly, so fast changes
0237         // in the same direction don't result into a combination of forward and backward movements.
0238         if (thumbnailView.count === 2 || (currentIndex === 0 && thumbnailView.currentIndex === thumbnailView.count - 1)) {
0239             thumbnailView.movementDirection = PathView.Positive
0240         } else if (currentIndex === thumbnailView.count - 1 && thumbnailView.currentIndex === 0) {
0241             thumbnailView.movementDirection = PathView.Negative
0242         } else {
0243             thumbnailView.movementDirection = (currentIndex > thumbnailView.currentIndex) ? PathView.Positive : PathView.Negative
0244         }
0245 
0246         thumbnailView.currentIndex = tabBox.currentIndex
0247     }
0248 
0249     onVisibleChanged: {
0250         // Reset the PathView index when hiding to avoid unwanted animations on relaunch
0251         if (!visible) {
0252             thumbnailView.currentIndex = 0;
0253         }
0254         window.visible = visible;
0255     }
0256 }