Commit 1ffba07c authored by Ismael Asensio's avatar Ismael Asensio
Browse files

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).
parent f2fedd98
Pipeline #190459 passed with stage
in 1 minute and 47 seconds
......@@ -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;
}
}
......@@ -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)