From f2fedd983fcd677ae5906f5e692a6478d76dc9aa Mon Sep 17 00:00:00 2001 From: Ismael Asensio Date: Tue, 4 Jan 2022 19:44:36 +0100 Subject: [PATCH 1/2] flip|cover-switchers: Small refactoring improvements - Switch to KWin 3.0 declarative API - Use anchors to simplify layout and avoid clipping - Use non-deprecated hints - Commonalize some more code between flip and cover --- .../coverswitch/contents/ui/main.qml | 52 ++++++++++--------- .../flipswitch/contents/ui/main.qml | 28 +++++----- 2 files changed, 44 insertions(+), 36 deletions(-) diff --git a/windowswitchers/coverswitch/contents/ui/main.qml b/windowswitchers/coverswitch/contents/ui/main.qml index 09fb85f13..6405c844a 100644 --- a/windowswitchers/coverswitch/contents/ui/main.qml +++ b/windowswitchers/coverswitch/contents/ui/main.qml @@ -12,10 +12,10 @@ import org.kde.kirigami 2.15 as Kirigami import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 3.0 as PC3 -import org.kde.kwin 2.0 as KWin +import org.kde.kwin 3.0 as KWin -KWin.Switcher { +KWin.TabBoxSwitcher { id: tabBox currentIndex: thumbnailView ? thumbnailView.currentIndex : -1 @@ -23,28 +23,28 @@ KWin.Switcher { id: dialog location: PlasmaCore.Types.Floating visible: tabBox.visible - flags: Qt.X11BypassWindowManagerHint + flags: Qt.BypassWindowManagerHint | Qt.FramelessWindowHint x: screenGeometry.x y: screenGeometry.y - mainItem: ColumnLayout { - width: tabBox.screenGeometry.width - dialog.margins.left - dialog.margins.right - height: tabBox.screenGeometry.height - dialog.margins.top - dialog.margins.bottom + mainItem: Item { + width: tabBox.screenGeometry.width + height: tabBox.screenGeometry.height PathView { id: thumbnailView readonly property int visibleCount: Math.min(count, pathItemCount) - - readonly property int boxWidth: tabBox.screenGeometry.width / 2 - readonly property int boxHeight: tabBox.screenGeometry.height / 2 + readonly property real boxScaleFactor: 0.5 + readonly property int boxWidth: tabBox.screenGeometry.width * boxScaleFactor + readonly property int boxHeight: tabBox.screenGeometry.height * boxScaleFactor focus: true - Layout.fillWidth: true - Layout.fillHeight: true + + anchors.fill: parent preferredHighlightBegin: 0.49 - preferredHighlightEnd: 0.49 + preferredHighlightEnd: preferredHighlightBegin highlightRangeMode: PathView.StrictlyEnforceRange // This property sets the animation duration between the current position to the next one, @@ -57,13 +57,13 @@ KWin.Switcher { path: Path { // Left stack - startX: thumbnailView.width * 0.1; startY: thumbnailView.height * 0.55 + startX: thumbnailView.width * 0.1; startY: thumbnailView.height * 0.5 PathAttribute { name: "progress"; value: 0 } PathAttribute { name: "scale"; value: 0.7 } PathAttribute { name: "rotation"; value: 70 } PathPercent { value: 0 } - PathLine { x: thumbnailView.width * 0.25 ; y: thumbnailView.height * 0.55 } + PathLine { x: thumbnailView.width * 0.25 ; y: thumbnailView.height * 0.5 } PathAttribute { name: "progress"; value: 0.8 } PathAttribute { name: "scale"; value: 0.7 } PathAttribute { name: "rotation"; value: 70 } @@ -71,8 +71,8 @@ KWin.Switcher { // Center Item PathQuad { - x: thumbnailView.width * 0.5 ; y: thumbnailView.height * 0.65 - controlX: thumbnailView.width * 0.45; controlY: thumbnailView.height * 0.6 + x: thumbnailView.width * 0.5 ; y: thumbnailView.height * 0.6 + controlX: thumbnailView.width * 0.45; controlY: thumbnailView.height * 0.55 } PathAttribute { name: "progress"; value: 1 } PathAttribute { name: "scale"; value: 1 } @@ -81,15 +81,15 @@ KWin.Switcher { // Right stack PathQuad { - x: thumbnailView.width * 0.75 ; y: thumbnailView.height * 0.55 - controlX: thumbnailView.width * 0.55; controlY: thumbnailView.height * 0.6 + x: thumbnailView.width * 0.75 ; y: thumbnailView.height * 0.5 + controlX: thumbnailView.width * 0.55; controlY: thumbnailView.height * 0.55 } PathAttribute { name: "progress"; value: 0.8 } PathAttribute { name: "scale"; value: 0.7 } PathAttribute { name: "rotation"; value: -70 } PathPercent { value: 0.6 } - PathLine { x: thumbnailView.width * 0.9 ; y: thumbnailView.height * 0.55 } + PathLine { x: thumbnailView.width * 0.9 ; y: thumbnailView.height * 0.5 } PathAttribute { name: "progress"; value: 0 } PathAttribute { name: "scale"; value: 0.7 } PathAttribute { name: "rotation"; value: -70 } @@ -122,7 +122,7 @@ KWin.Switcher { scale: PathView.onPath ? PathView.scale : 0 z: PathView.onPath ? Math.floor(PathView.progress * thumbnailView.visibleCount) : -1 - KWin.ThumbnailItem { + KWin.WindowThumbnailItem { id: thumbnail readonly property double ratio: implicitWidth / implicitHeight @@ -196,11 +196,15 @@ KWin.Switcher { } RowLayout { - Layout.preferredHeight: PlasmaCore.Units.iconSizes.large - Layout.margins: PlasmaCore.Units.gridUnit - Layout.alignment: Qt.AlignCenter + height: PlasmaCore.Units.iconSizes.large spacing: PlasmaCore.Units.gridUnit + anchors { + horizontalCenter: parent.horizontalCenter + bottom: parent.bottom + margins: PlasmaCore.Units.gridUnit + } + PlasmaCore.IconItem { source: thumbnailView.currentItem ? thumbnailView.currentItem.icon : "" implicitWidth: PlasmaCore.Units.iconSizes.large @@ -231,7 +235,7 @@ KWin.Switcher { // in the same direction don't result into a combination of forward and backward movements. if (thumbnailView.count === 2 || (currentIndex === 0 && thumbnailView.currentIndex === thumbnailView.count - 1)) { thumbnailView.movementDirection = PathView.Positive - } else if (currentIndex === (thumbnailView.count - 1) && thumbnailView.currentIndex === 0) { + } else if (currentIndex === thumbnailView.count - 1 && thumbnailView.currentIndex === 0) { thumbnailView.movementDirection = PathView.Negative } else { thumbnailView.movementDirection = (currentIndex > thumbnailView.currentIndex) ? PathView.Positive : PathView.Negative diff --git a/windowswitchers/flipswitch/contents/ui/main.qml b/windowswitchers/flipswitch/contents/ui/main.qml index 652702823..1b8772dfa 100644 --- a/windowswitchers/flipswitch/contents/ui/main.qml +++ b/windowswitchers/flipswitch/contents/ui/main.qml @@ -12,10 +12,10 @@ import org.kde.kirigami 2.15 as Kirigami import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 3.0 as PC3 -import org.kde.kwin 2.0 as KWin +import org.kde.kwin 3.0 as KWin -KWin.Switcher { +KWin.TabBoxSwitcher { id: tabBox currentIndex: thumbnailView ? thumbnailView.currentIndex : -1 @@ -23,13 +23,13 @@ KWin.Switcher { id: dialog location: PlasmaCore.Types.Floating visible: tabBox.visible - flags: Qt.X11BypassWindowManagerHint + flags: Qt.BypassWindowManagerHint | Qt.FramelessWindowHint x: screenGeometry.x y: screenGeometry.y - mainItem: ColumnLayout { - width: tabBox.screenGeometry.width - dialog.margins.left - dialog.margins.right - height: tabBox.screenGeometry.height - dialog.margins.top - dialog.margins.bottom + mainItem: Item { + width: tabBox.screenGeometry.width + height: tabBox.screenGeometry.height PathView { id: thumbnailView @@ -42,8 +42,8 @@ KWin.Switcher { readonly property int boxHeight: tabBox.screenGeometry.height * boxScaleFactor focus: true - Layout.fillWidth: true - Layout.fillHeight: true + + anchors.fill: parent preferredHighlightBegin: 1/(visibleCount + 1) preferredHighlightEnd: preferredHighlightBegin @@ -100,7 +100,7 @@ KWin.Switcher { // Reduce opacity on the end so items dissapear more naturally opacity: Math.min(1, (1 - PathView.progress) / thumbnailView.preferredHighlightBegin); - KWin.ThumbnailItem { + KWin.WindowThumbnailItem { id: thumbnail readonly property double ratio: implicitWidth / implicitHeight @@ -165,11 +165,15 @@ KWin.Switcher { } RowLayout { - Layout.preferredHeight: PlasmaCore.Units.iconSizes.large - Layout.margins: PlasmaCore.Units.gridUnit - Layout.alignment: Qt.AlignCenter + height: PlasmaCore.Units.iconSizes.large spacing: PlasmaCore.Units.gridUnit + anchors { + horizontalCenter: parent.horizontalCenter + bottom: parent.bottom + margins: PlasmaCore.Units.gridUnit + } + PlasmaCore.IconItem { source: thumbnailView.currentItem ? thumbnailView.currentItem.icon : "" implicitWidth: PlasmaCore.Units.iconSizes.large -- GitLab From 1ffba07cef9fdcdd56c650d945fdf83258493d2c Mon Sep 17 00:00:00 2001 From: Ismael Asensio Date: Tue, 17 May 2022 20:04:26 +0200 Subject: [PATCH 2/2] flip|cover-switchers: Improve transparent and blurred background Currently, the flip|cover window switchers use a Plasma Dialog as their background, which can provide transparency and blur but it's totally dependent on the plasma style, which may be not be designed for this purpose, and doesn't look clean with current breeze defaults. Use instead the same components that are used in the Overview effect, for consistency and simplicity. It should also help with efficiency, as the background and blur effects are done over DesktopBackground special component and not over the actual desktop. The code is also prepared with a bool property to not blur the background, but it doesn't add the user configuration part (yet). --- .../coverswitch/contents/ui/main.qml | 385 ++++++++++-------- .../flipswitch/contents/ui/main.qml | 325 ++++++++------- 2 files changed, 388 insertions(+), 322 deletions(-) diff --git a/windowswitchers/coverswitch/contents/ui/main.qml b/windowswitchers/coverswitch/contents/ui/main.qml index 6405c844a..f43b2305b 100644 --- a/windowswitchers/coverswitch/contents/ui/main.qml +++ b/windowswitchers/coverswitch/contents/ui/main.qml @@ -6,222 +6,254 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 as QQC2 +import QtGraphicalEffects 1.12 import QtQuick.Layouts 1.15 +import QtQuick.Window 2.15 import org.kde.kirigami 2.15 as Kirigami import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 3.0 as PC3 import org.kde.kwin 3.0 as KWin +import org.kde.kwin.private.effects 1.0 KWin.TabBoxSwitcher { id: tabBox currentIndex: thumbnailView ? thumbnailView.currentIndex : -1 - PlasmaCore.Dialog { - id: dialog - location: PlasmaCore.Types.Floating - visible: tabBox.visible + // TODO: Make it user configurable ? + property bool enableBlur: true + + Window { + id: window + + x: tabBox.screenGeometry.x + y: tabBox.screenGeometry.y + width: tabBox.screenGeometry.width + height: tabBox.screenGeometry.height flags: Qt.BypassWindowManagerHint | Qt.FramelessWindowHint - x: screenGeometry.x - y: screenGeometry.y - - mainItem: Item { - width: tabBox.screenGeometry.width - height: tabBox.screenGeometry.height - - PathView { - id: thumbnailView - - readonly property int visibleCount: Math.min(count, pathItemCount) - readonly property real boxScaleFactor: 0.5 - readonly property int boxWidth: tabBox.screenGeometry.width * boxScaleFactor - readonly property int boxHeight: tabBox.screenGeometry.height * boxScaleFactor - - focus: true - - anchors.fill: parent - - preferredHighlightBegin: 0.49 - preferredHighlightEnd: preferredHighlightBegin - highlightRangeMode: PathView.StrictlyEnforceRange - - // This property sets the animation duration between the current position to the next one, - // without taking into account how much distance the thumbnails travel in that time. - // To compensate the speed, we slowly reduce the duration with the number of thumbnails, - // starting from `veryLongDuration` when there are 2 of them - highlightMoveDuration: PlasmaCore.Units.veryLongDuration * (2 / Math.sqrt(visibleCount + 1)) - - pathItemCount: 13 - - path: Path { - // Left stack - startX: thumbnailView.width * 0.1; startY: thumbnailView.height * 0.5 - PathAttribute { name: "progress"; value: 0 } - PathAttribute { name: "scale"; value: 0.7 } - PathAttribute { name: "rotation"; value: 70 } - PathPercent { value: 0 } - - PathLine { x: thumbnailView.width * 0.25 ; y: thumbnailView.height * 0.5 } - PathAttribute { name: "progress"; value: 0.8 } - PathAttribute { name: "scale"; value: 0.7 } - PathAttribute { name: "rotation"; value: 70 } - PathPercent { value: 0.4 } - - // Center Item - PathQuad { - x: thumbnailView.width * 0.5 ; y: thumbnailView.height * 0.6 - controlX: thumbnailView.width * 0.45; controlY: thumbnailView.height * 0.55 - } - PathAttribute { name: "progress"; value: 1 } - PathAttribute { name: "scale"; value: 1 } - PathAttribute { name: "rotation"; value: 0 } - PathPercent { value: 0.49 } // A bit less than 50% so items preferrably stack on the right side - - // Right stack - PathQuad { - x: thumbnailView.width * 0.75 ; y: thumbnailView.height * 0.5 - controlX: thumbnailView.width * 0.55; controlY: thumbnailView.height * 0.55 - } - PathAttribute { name: "progress"; value: 0.8 } - PathAttribute { name: "scale"; value: 0.7 } - PathAttribute { name: "rotation"; value: -70 } - PathPercent { value: 0.6 } - - PathLine { x: thumbnailView.width * 0.9 ; y: thumbnailView.height * 0.5 } - PathAttribute { name: "progress"; value: 0 } - PathAttribute { name: "scale"; value: 0.7 } - PathAttribute { name: "rotation"; value: -70 } - PathPercent { value: 1 } - } + visibility: Window.FullScreen + // Workaround QTBUG-35244. Do not directly assign here to avoid warning + visible: true - model: tabBox.model + color: "transparent" - delegate: Item { - id: delegateItem + KWin.DesktopBackgroundItem { + activity: KWin.Workspace.currentActivity + desktop: KWin.Workspace.currentVirtualDesktop + outputName: window.screen.name - readonly property string caption: model.caption - readonly property var icon: model.icon + layer.enabled: true + layer.effect: FastBlur { + radius: enableBlur ? 64 : 0 + } + } - readonly property real scaleFactor: { - if (thumbnail.implicitWidth < thumbnailView.boxWidth && thumbnail.implicitHeight < thumbnailView.boxHeight) { - // Do not scale up thumbnails smaller than the box frame - return 1; - } else if (thumbnail.ratio > thumbnailView.boxWidth / thumbnailView.boxHeight) { - // Thumbnail is wider than the box - return thumbnailView.boxWidth / thumbnail.implicitWidth; - } else { - // Thumbnail is taller than the box - return thumbnailView.boxHeight / thumbnail.implicitHeight; - } - } + Rectangle { + anchors { + top: enableBlur ? parent.top : infoBar.top + topMargin: enableBlur ? 0 : -infoBar.anchors.bottomMargin + left: parent.left + right: parent.right + bottom: parent.bottom + } + color: PlasmaCore.ColorScope.backgroundColor + opacity: enableBlur ? 0.5 : 0.75 + } - width: Math.round(thumbnail.implicitWidth * scaleFactor) - height: Math.round(thumbnail.implicitHeight * scaleFactor) - scale: PathView.onPath ? PathView.scale : 0 - z: PathView.onPath ? Math.floor(PathView.progress * thumbnailView.visibleCount) : -1 + PathView { + id: thumbnailView - KWin.WindowThumbnailItem { - id: thumbnail - readonly property double ratio: implicitWidth / implicitHeight + readonly property int visibleCount: Math.min(count, pathItemCount) + readonly property real boxScaleFactor: 0.5 + readonly property int boxWidth: tabBox.screenGeometry.width * boxScaleFactor + readonly property int boxHeight: tabBox.screenGeometry.height * boxScaleFactor - wId: windowId - anchors.fill: parent - } + focus: true - Kirigami.ShadowedRectangle { - anchors.fill: parent - z: -1 + anchors.fill: parent - color: "transparent" - shadow.size: PlasmaCore.Units.gridUnit - shadow.color: "black" - opacity: 0.5 - } + preferredHighlightBegin: 0.49 + preferredHighlightEnd: preferredHighlightBegin + highlightRangeMode: PathView.StrictlyEnforceRange - transform: Rotation { - origin { x: delegateItem.width/2; y: delegateItem.height/2 } - axis { x: 0; y: 1; z: 0 } - angle: delegateItem.PathView.rotation - } + // This property sets the animation duration between the current position to the next one, + // without taking into account how much distance the thumbnails travel in that time. + // To compensate the speed, we slowly reduce the duration with the number of thumbnails, + // starting from `veryLongDuration` when there are 2 of them + highlightMoveDuration: PlasmaCore.Units.veryLongDuration * (2 / Math.sqrt(visibleCount + 1)) - TapHandler { - grabPermissions: PointerHandler.TakeOverForbidden - gesturePolicy: TapHandler.WithinBounds - onSingleTapped: { - if (index === thumbnailView.currentIndex) { - thumbnailView.model.activate(index); - return; - } - thumbnailView.movementDirection = (delegateItem.PathView.rotation < 0) ? PathView.Positive : PathView.Negative - thumbnailView.currentIndex = index - } - } + pathItemCount: 13 + + path: Path { + // Left stack + startX: thumbnailView.width * 0.1; startY: thumbnailView.height * 0.5 + PathAttribute { name: "progress"; value: 0 } + PathAttribute { name: "scale"; value: 0.7 } + PathAttribute { name: "rotation"; value: 70 } + PathPercent { value: 0 } + + PathLine { x: thumbnailView.width * 0.25 ; y: thumbnailView.height * 0.5 } + PathAttribute { name: "progress"; value: 0.8 } + PathAttribute { name: "scale"; value: 0.7 } + PathAttribute { name: "rotation"; value: 70 } + PathPercent { value: 0.4 } + + // Center Item + PathQuad { + x: thumbnailView.width * 0.5 ; y: thumbnailView.height * 0.6 + controlX: thumbnailView.width * 0.45; controlY: thumbnailView.height * 0.55 + } + PathAttribute { name: "progress"; value: 1 } + PathAttribute { name: "scale"; value: 1 } + PathAttribute { name: "rotation"; value: 0 } + PathPercent { value: 0.49 } // A bit less than 50% so items preferrably stack on the right side + + // Right stack + PathQuad { + x: thumbnailView.width * 0.75 ; y: thumbnailView.height * 0.5 + controlX: thumbnailView.width * 0.55; controlY: thumbnailView.height * 0.55 } + PathAttribute { name: "progress"; value: 0.8 } + PathAttribute { name: "scale"; value: 0.7 } + PathAttribute { name: "rotation"; value: -70 } + PathPercent { value: 0.6 } + + PathLine { x: thumbnailView.width * 0.9 ; y: thumbnailView.height * 0.5 } + PathAttribute { name: "progress"; value: 0 } + PathAttribute { name: "scale"; value: 0.7 } + PathAttribute { name: "rotation"; value: -70 } + PathPercent { value: 1 } + } + + model: tabBox.model - highlight: PlasmaCore.FrameSvgItem { - id: highlightItem - imagePath: "widgets/viewitem" - prefix: "hover" - - readonly property Item target: thumbnailView.currentItem - - visible: target !== null - // Make sure the highlight is pixel perfect aligned on both sides even if the target is not - anchors.centerIn: target - anchors.horizontalCenterOffset: target ? Math.round(target.x) - target.x : 0 - anchors.verticalCenterOffset: target ? Math.round(target.y) - target.y : 0 - width: target ? Math.round(target.width/2 + 3 * PlasmaCore.Units.smallSpacing) * 2 : 0 - height: target ? Math.round(target.height/2 + 3 * PlasmaCore.Units.smallSpacing) * 2 : 0 - scale: target ? target.scale : 1 - z: target ? target.z - 0.5 : -0.5 - // The transform cannot be directly assigned as the transform origin is different - transform: Rotation { - origin { x: highlightItem.width/2; y: highlightItem.height/2 } - axis { x: 0; y: 1; z: 0 } - angle: target ? target.PathView.rotation : 0 + delegate: Item { + id: delegateItem + + readonly property string caption: model.caption + readonly property var icon: model.icon + + readonly property real scaleFactor: { + if (thumbnail.implicitWidth < thumbnailView.boxWidth && thumbnail.implicitHeight < thumbnailView.boxHeight) { + // Do not scale up thumbnails smaller than the box frame + return 1; + } else if (thumbnail.ratio > thumbnailView.boxWidth / thumbnailView.boxHeight) { + // Thumbnail is wider than the box + return thumbnailView.boxWidth / thumbnail.implicitWidth; + } else { + // Thumbnail is taller than the box + return thumbnailView.boxHeight / thumbnail.implicitHeight; } } - layer.enabled: true - layer.smooth: true + width: Math.round(thumbnail.implicitWidth * scaleFactor) + height: Math.round(thumbnail.implicitHeight * scaleFactor) + scale: PathView.onPath ? PathView.scale : 0 + z: PathView.onPath ? Math.floor(PathView.progress * thumbnailView.visibleCount) : -1 - onMovementStarted: movementDirection = PathView.Shortest + KWin.WindowThumbnailItem { + id: thumbnail + readonly property double ratio: implicitWidth / implicitHeight - Keys.onUpPressed: decrementCurrentIndex() - Keys.onLeftPressed: decrementCurrentIndex() - Keys.onDownPressed: incrementCurrentIndex() - Keys.onRightPressed: incrementCurrentIndex() - } + wId: windowId + anchors.fill: parent + } - RowLayout { - height: PlasmaCore.Units.iconSizes.large - spacing: PlasmaCore.Units.gridUnit + Kirigami.ShadowedRectangle { + anchors.fill: parent + z: -1 - anchors { - horizontalCenter: parent.horizontalCenter - bottom: parent.bottom - margins: PlasmaCore.Units.gridUnit + color: "transparent" + shadow.size: PlasmaCore.Units.gridUnit + shadow.color: "black" + opacity: 0.5 } - PlasmaCore.IconItem { - source: thumbnailView.currentItem ? thumbnailView.currentItem.icon : "" - implicitWidth: PlasmaCore.Units.iconSizes.large - implicitHeight: PlasmaCore.Units.iconSizes.large - Layout.alignment: Qt.AlignCenter + transform: Rotation { + origin { x: delegateItem.width/2; y: delegateItem.height/2 } + axis { x: 0; y: 1; z: 0 } + angle: delegateItem.PathView.rotation } - PC3.Label { - font.bold: true - font.pointSize: Math.round(PlasmaCore.Theme.defaultFont.pointSize * 1.6) - text: thumbnailView.currentItem ? thumbnailView.currentItem.caption : "" - maximumLineCount: 1 - elide: Text.ElideMiddle - Layout.maximumWidth: tabBox.screenGeometry.width * 0.8 - Layout.alignment: Qt.AlignCenter + TapHandler { + grabPermissions: PointerHandler.TakeOverForbidden + gesturePolicy: TapHandler.WithinBounds + onSingleTapped: { + if (index === thumbnailView.currentIndex) { + thumbnailView.model.activate(index); + return; + } + thumbnailView.movementDirection = (delegateItem.PathView.rotation < 0) ? PathView.Positive : PathView.Negative + thumbnailView.currentIndex = index + } } } + + highlight: PlasmaCore.FrameSvgItem { + id: highlightItem + imagePath: "widgets/viewitem" + prefix: "hover" + + readonly property Item target: thumbnailView.currentItem + + visible: target !== null + // Make sure the highlight is pixel perfect aligned on both sides even if the target is not + anchors.centerIn: target + anchors.horizontalCenterOffset: target ? Math.round(target.x) - target.x : 0 + anchors.verticalCenterOffset: target ? Math.round(target.y) - target.y : 0 + width: target ? Math.round(target.width/2 + 3 * PlasmaCore.Units.smallSpacing) * 2 : 0 + height: target ? Math.round(target.height/2 + 3 * PlasmaCore.Units.smallSpacing) * 2 : 0 + scale: target ? target.scale : 1 + z: target ? target.z - 0.5 : -0.5 + // The transform cannot be directly assigned as the transform origin is different + transform: Rotation { + origin { x: highlightItem.width/2; y: highlightItem.height/2 } + axis { x: 0; y: 1; z: 0 } + angle: target ? target.PathView.rotation : 0 + } + } + + layer.enabled: true + layer.smooth: true + + onMovementStarted: movementDirection = PathView.Shortest + + Keys.onUpPressed: decrementCurrentIndex() + Keys.onLeftPressed: decrementCurrentIndex() + Keys.onDownPressed: incrementCurrentIndex() + Keys.onRightPressed: incrementCurrentIndex() + } + + RowLayout { + id: infoBar + + height: PlasmaCore.Units.iconSizes.large + spacing: PlasmaCore.Units.gridUnit + + anchors { + horizontalCenter: parent.horizontalCenter + bottom: parent.bottom + margins: PlasmaCore.Units.gridUnit + } + + PlasmaCore.IconItem { + source: thumbnailView.currentItem ? thumbnailView.currentItem.icon : "" + implicitWidth: PlasmaCore.Units.iconSizes.large + implicitHeight: PlasmaCore.Units.iconSizes.large + Layout.alignment: Qt.AlignCenter + } + + PC3.Label { + font.bold: true + font.pointSize: Math.round(PlasmaCore.Theme.defaultFont.pointSize * 1.6) + text: thumbnailView.currentItem ? thumbnailView.currentItem.caption : "" + maximumLineCount: 1 + elide: Text.ElideMiddle + Layout.maximumWidth: tabBox.screenGeometry.width * 0.8 + Layout.alignment: Qt.AlignCenter + } } } @@ -249,5 +281,6 @@ KWin.TabBoxSwitcher { if (!visible) { thumbnailView.currentIndex = 0; } + window.visible = visible; } } diff --git a/windowswitchers/flipswitch/contents/ui/main.qml b/windowswitchers/flipswitch/contents/ui/main.qml index 1b8772dfa..fa0e8c3b4 100644 --- a/windowswitchers/flipswitch/contents/ui/main.qml +++ b/windowswitchers/flipswitch/contents/ui/main.qml @@ -6,190 +6,222 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 as QQC2 +import QtGraphicalEffects 1.12 import QtQuick.Layouts 1.15 +import QtQuick.Window 2.15 import org.kde.kirigami 2.15 as Kirigami import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 3.0 as PC3 import org.kde.kwin 3.0 as KWin +import org.kde.kwin.private.effects 1.0 KWin.TabBoxSwitcher { id: tabBox currentIndex: thumbnailView ? thumbnailView.currentIndex : -1 - PlasmaCore.Dialog { - id: dialog - location: PlasmaCore.Types.Floating - visible: tabBox.visible + // TODO: Make it user configurable ? + property bool enableBlur: true + + Window { + id: window + + x: tabBox.screenGeometry.x + y: tabBox.screenGeometry.y + width: tabBox.screenGeometry.width + height: tabBox.screenGeometry.height flags: Qt.BypassWindowManagerHint | Qt.FramelessWindowHint - x: screenGeometry.x - y: screenGeometry.y - - mainItem: Item { - width: tabBox.screenGeometry.width - height: tabBox.screenGeometry.height - - PathView { - id: thumbnailView - - readonly property int visibleCount: Math.min(count, pathItemCount) - // Make thumbnails slightly smaller the more there are, so it doesn't feel too crowded - // The sizeFactor curve parameters have been calculated experimentally - readonly property real boxScaleFactor: 0.35 + (0.5 / (visibleCount + 1)) - readonly property int boxWidth: tabBox.screenGeometry.width * boxScaleFactor - readonly property int boxHeight: tabBox.screenGeometry.height * boxScaleFactor - - focus: true - - anchors.fill: parent - - preferredHighlightBegin: 1/(visibleCount + 1) - preferredHighlightEnd: preferredHighlightBegin - highlightRangeMode: PathView.StrictlyEnforceRange - - // This property sets the animation duration between the current position to the next one, - // without taking into account how much distance the thumbnails travel in that time. - // To compensate the speed, we slowly reduce the duration with the number of thumbnails, - // starting from `veryLongDuration` when there are 2 of them - highlightMoveDuration: PlasmaCore.Units.veryLongDuration * (2 / Math.sqrt(visibleCount + 1)) - - pathItemCount: 12 - - path: Path { - // Nearest point of the path - startX: Math.round(thumbnailView.width * 0.75) - startY: Math.round(thumbnailView.height * 0.80) - PathAttribute { name: "progress"; value: 1 } - PathAttribute { name: "scale"; value: 1 } - - // Back point of the path on top-left corner - PathLine { - x: Math.round(thumbnailView.width * 0.25) - y: Math.round(thumbnailView.height * 0.20) - } - PathAttribute { name: "progress"; value: 0 } - PathAttribute { name: "scale"; value: 0.6 } + visibility: Window.FullScreen + // Workaround QTBUG-35244. Do not directly assign here to avoid warning + visible: true + + color: "transparent" + + KWin.DesktopBackgroundItem { + activity: KWin.Workspace.currentActivity + desktop: KWin.Workspace.currentVirtualDesktop + outputName: window.screen.name + + layer.enabled: true + layer.effect: FastBlur { + radius: enableBlur ? 64 : 0 + } + } + + Rectangle { + anchors { + top: enableBlur ? parent.top : infoBar.top + topMargin: enableBlur ? 0 : -infoBar.anchors.bottomMargin + left: parent.left + right: parent.right + bottom: parent.bottom + } + color: PlasmaCore.ColorScope.backgroundColor + opacity: enableBlur ? 0.5 : 0.75 + } + + PathView { + id: thumbnailView + + readonly property int visibleCount: Math.min(count, pathItemCount) + // Make thumbnails slightly smaller the more there are, so it doesn't feel too crowded + // The sizeFactor curve parameters have been calculated experimentally + readonly property real boxScaleFactor: 0.35 + (0.5 / (visibleCount + 1)) + readonly property int boxWidth: tabBox.screenGeometry.width * boxScaleFactor + readonly property int boxHeight: tabBox.screenGeometry.height * boxScaleFactor + + focus: true + + anchors.fill: parent + + preferredHighlightBegin: 1/(visibleCount + 1) + preferredHighlightEnd: preferredHighlightBegin + highlightRangeMode: PathView.StrictlyEnforceRange + + // This property sets the animation duration between the current position to the next one, + // without taking into account how much distance the thumbnails travel in that time. + // To compensate the speed, we slowly reduce the duration with the number of thumbnails, + // starting from `veryLongDuration` when there are 2 of them + highlightMoveDuration: PlasmaCore.Units.veryLongDuration * (2 / Math.sqrt(visibleCount + 1)) + + pathItemCount: 12 + + path: Path { + // Nearest point of the path + startX: Math.round(thumbnailView.width * 0.75) + startY: Math.round(thumbnailView.height * 0.80) + PathAttribute { name: "progress"; value: 1 } + PathAttribute { name: "scale"; value: 1 } + + // Back point of the path on top-left corner + PathLine { + x: Math.round(thumbnailView.width * 0.25) + y: Math.round(thumbnailView.height * 0.20) } + PathAttribute { name: "progress"; value: 0 } + PathAttribute { name: "scale"; value: 0.6 } + } - model: tabBox.model - - delegate: Item { - readonly property string caption: model.caption - readonly property var icon: model.icon - - readonly property real scaleFactor: { - if (thumbnail.implicitWidth < thumbnailView.boxWidth && thumbnail.implicitHeight < thumbnailView.boxHeight) { - // Do not scale up thumbnails smaller than the box frame - return 1; - } else if (thumbnail.ratio > thumbnailView.boxWidth / thumbnailView.boxHeight) { - // Thumbnail is wider than the box - return thumbnailView.boxWidth / thumbnail.implicitWidth; - } else { - // Thumbnail is taller than the box - return thumbnailView.boxHeight / thumbnail.implicitHeight; - } + model: tabBox.model + + delegate: Item { + readonly property string caption: model.caption + readonly property var icon: model.icon + + readonly property real scaleFactor: { + if (thumbnail.implicitWidth < thumbnailView.boxWidth && thumbnail.implicitHeight < thumbnailView.boxHeight) { + // Do not scale up thumbnails smaller than the box frame + return 1; + } else if (thumbnail.ratio > thumbnailView.boxWidth / thumbnailView.boxHeight) { + // Thumbnail is wider than the box + return thumbnailView.boxWidth / thumbnail.implicitWidth; + } else { + // Thumbnail is taller than the box + return thumbnailView.boxHeight / thumbnail.implicitHeight; } + } - width: Math.round(thumbnail.implicitWidth * scaleFactor) - height: Math.round(thumbnail.implicitHeight * scaleFactor) - scale: PathView.onPath ? PathView.scale : 0 - z: PathView.onPath ? Math.floor(PathView.progress * thumbnailView.visibleCount) : -1 + width: Math.round(thumbnail.implicitWidth * scaleFactor) + height: Math.round(thumbnail.implicitHeight * scaleFactor) + scale: PathView.onPath ? PathView.scale : 0 + z: PathView.onPath ? Math.floor(PathView.progress * thumbnailView.visibleCount) : -1 - // Reduce opacity on the end so items dissapear more naturally - opacity: Math.min(1, (1 - PathView.progress) / thumbnailView.preferredHighlightBegin); + // Reduce opacity on the end so items dissapear more naturally + opacity: Math.min(1, (1 - PathView.progress) / thumbnailView.preferredHighlightBegin); - KWin.WindowThumbnailItem { - id: thumbnail - readonly property double ratio: implicitWidth / implicitHeight + KWin.WindowThumbnailItem { + id: thumbnail + readonly property double ratio: implicitWidth / implicitHeight - wId: windowId - anchors.fill: parent - } + wId: windowId + anchors.fill: parent + } - Kirigami.ShadowedRectangle { - anchors.fill: parent - z: -1 + Kirigami.ShadowedRectangle { + anchors.fill: parent + z: -1 - color: "transparent" - shadow.size: PlasmaCore.Units.gridUnit - shadow.color: "black" - opacity: 0.5 - shadow.yOffset: 1 - } + color: "transparent" + shadow.size: PlasmaCore.Units.gridUnit + shadow.color: "black" + opacity: 0.5 + shadow.yOffset: 1 + } - TapHandler { - grabPermissions: PointerHandler.TakeOverForbidden - gesturePolicy: TapHandler.WithinBounds - onSingleTapped: { - if (index === thumbnailView.currentIndex) { - thumbnailView.model.activate(index); - return; - } - thumbnailView.movementDirection = PathView.Positive - thumbnailView.currentIndex = index + TapHandler { + grabPermissions: PointerHandler.TakeOverForbidden + gesturePolicy: TapHandler.WithinBounds + onSingleTapped: { + if (index === thumbnailView.currentIndex) { + thumbnailView.model.activate(index); + return; } + thumbnailView.movementDirection = PathView.Positive + thumbnailView.currentIndex = index } } + } - transform: Rotation { - origin { x: thumbnailView.width/2; y: thumbnailView.height/2 } - axis { x: 0; y: 1; z: -0.15 } - angle: 10 - } + transform: Rotation { + origin { x: thumbnailView.width/2; y: thumbnailView.height/2 } + axis { x: 0; y: 1; z: -0.15 } + angle: 10 + } - layer.enabled: true - layer.smooth: true + highlight: PlasmaCore.FrameSvgItem { + imagePath: "widgets/viewitem" + prefix: "hover" - highlight: PlasmaCore.FrameSvgItem { - imagePath: "widgets/viewitem" - prefix: "hover" + readonly property Item target: thumbnailView.currentItem - readonly property Item target: thumbnailView.currentItem + visible: target !== null + anchors.centerIn: target + width: target ? target.width + 6 * PlasmaCore.Units.smallSpacing : 0 + height: target ? target.height + 6 * PlasmaCore.Units.smallSpacing : 0 + scale: target ? target.scale : 1 + z: target ? target.z - 0.5 : -0.5 + } - visible: target !== null - anchors.centerIn: target - width: target ? target.width + 6 * PlasmaCore.Units.smallSpacing : 0 - height: target ? target.height + 6 * PlasmaCore.Units.smallSpacing : 0 - scale: target ? target.scale : 1 - z: target ? target.z - 0.5 : -0.5 - } + layer.enabled: true + layer.smooth: true - onMovementStarted: movementDirection = PathView.Shortest + onMovementStarted: movementDirection = PathView.Shortest - Keys.onUpPressed: decrementCurrentIndex() - Keys.onLeftPressed: decrementCurrentIndex() - Keys.onDownPressed: incrementCurrentIndex() - Keys.onRightPressed: incrementCurrentIndex() - } + Keys.onUpPressed: decrementCurrentIndex() + Keys.onLeftPressed: decrementCurrentIndex() + Keys.onDownPressed: incrementCurrentIndex() + Keys.onRightPressed: incrementCurrentIndex() + } - RowLayout { - height: PlasmaCore.Units.iconSizes.large - spacing: PlasmaCore.Units.gridUnit + RowLayout { + id: infoBar - anchors { - horizontalCenter: parent.horizontalCenter - bottom: parent.bottom - margins: PlasmaCore.Units.gridUnit - } + height: PlasmaCore.Units.iconSizes.large + spacing: PlasmaCore.Units.gridUnit - PlasmaCore.IconItem { - source: thumbnailView.currentItem ? thumbnailView.currentItem.icon : "" - implicitWidth: PlasmaCore.Units.iconSizes.large - implicitHeight: PlasmaCore.Units.iconSizes.large - Layout.alignment: Qt.AlignCenter - } + anchors { + horizontalCenter: parent.horizontalCenter + bottom: parent.bottom + margins: PlasmaCore.Units.gridUnit + } - PC3.Label { - font.bold: true - font.pointSize: Math.round(PlasmaCore.Theme.defaultFont.pointSize * 1.6) - text: thumbnailView.currentItem ? thumbnailView.currentItem.caption : "" - maximumLineCount: 1 - elide: Text.ElideMiddle - Layout.maximumWidth: tabBox.screenGeometry.width * 0.8 - Layout.alignment: Qt.AlignCenter - } + PlasmaCore.IconItem { + source: thumbnailView.currentItem ? thumbnailView.currentItem.icon : "" + implicitWidth: PlasmaCore.Units.iconSizes.large + implicitHeight: PlasmaCore.Units.iconSizes.large + Layout.alignment: Qt.AlignCenter + } + + PC3.Label { + font.bold: true + font.pointSize: Math.round(PlasmaCore.Theme.defaultFont.pointSize * 1.6) + text: thumbnailView.currentItem ? thumbnailView.currentItem.caption : "" + maximumLineCount: 1 + elide: Text.ElideMiddle + Layout.maximumWidth: tabBox.screenGeometry.width * 0.8 + Layout.alignment: Qt.AlignCenter } } } @@ -218,5 +250,6 @@ KWin.TabBoxSwitcher { if (!visible) { thumbnailView.currentIndex = 0; } + window.visible = visible; } } -- GitLab