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 }