diff --git a/components/mobileshell/CMakeLists.txt b/components/mobileshell/CMakeLists.txt index 4daf7068..83914359 100644 --- a/components/mobileshell/CMakeLists.txt +++ b/components/mobileshell/CMakeLists.txt @@ -11,9 +11,12 @@ set(mobileshellplugin_SRCS notifications/notificationthumbnailer.cpp notifications/notificationfilemenu.cpp notifications/notificationfileinfo.cpp + masklayer/masklayer.cpp + masklayer/maskmanager.cpp ) target_include_directories(mobileshellplugin PRIVATE components) target_include_directories(mobileshellplugin PRIVATE notifications) +target_include_directories(mobileshellplugin PRIVATE masklayer) target_sources(mobileshellplugin PRIVATE ${mobileshellplugin_SRCS}) # Singleton declarations diff --git a/components/mobileshell/masklayer/masklayer.cpp b/components/mobileshell/masklayer/masklayer.cpp new file mode 100644 index 00000000..04cc732a --- /dev/null +++ b/components/mobileshell/masklayer/masklayer.cpp @@ -0,0 +1,268 @@ +// SPDX-FileCopyrightText: 2025 Micah Stanley +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "masklayer.h" + +#include + +// helper function for creating rounded rectangles +static void createRoundedRectGeometry(QSGGeometry *geometry, const QRectF &rect, qreal radius) +{ + geometry->setDrawingMode(QSGGeometry::DrawTriangles); + radius = qMin(radius, qMin(rect.width(), rect.height()) / 2.0); // clamp radius + + // if the radius is too small, draw a simple rectangle instead + if (radius < 0.1) { + // 4 vertices, 6 indices (2 triangles * 3 indices) + geometry->allocate(4, 6); + + // fill vertex data + QSGGeometry::Point2D *vertices = geometry->vertexDataAsPoint2D(); + vertices[0].set(rect.left(), rect.top()); + vertices[1].set(rect.right(), rect.top()); + vertices[2].set(rect.left(), rect.bottom()); + vertices[3].set(rect.right(), rect.bottom()); + + // fill index data + quint16 *indices = geometry->indexDataAsUShort(); + indices[0] = 0; indices[1] = 2; indices[2] = 1; // first triangle (TL, BL, TR) + indices[3] = 1; indices[4] = 2; indices[5] = 3; // second triangle (TR, BL, BR) + + geometry->markVertexDataDirty(); + geometry->markIndexDataDirty(); + return; + } + + const int segments_per_corner = 16; + const int perimeter_verts = segments_per_corner * 4; + const int vertex_count = 1 + perimeter_verts; + const int index_count = perimeter_verts * 3; + + geometry->allocate(vertex_count, index_count); + + QSGGeometry::Point2D *vertices = geometry->vertexDataAsPoint2D(); + quint16 *indices = geometry->indexDataAsUShort(); + + int vertIndex = 0; + int indexPos = 0; + + // define the center vertex + const quint16 center_vert_index = vertIndex; + vertices[vertIndex++].set(rect.center().x(), rect.center().y()); + + // define the center of the corners + const QPointF tl_c = {rect.left() + radius, rect.top() + radius}; + const QPointF tr_c = {rect.right() - radius, rect.top() + radius}; + const QPointF br_c = {rect.right() - radius, rect.bottom() - radius}; + const QPointF bl_c = {rect.left() + radius, rect.bottom() - radius}; + + // create all perimeter vertices + // top-right + for (int i = 0; i < segments_per_corner; ++i) { + const qreal angle = M_PI * 1.5 + (M_PI_2 * i / segments_per_corner); + vertices[vertIndex++].set(tr_c.x() + radius * cos(angle), tr_c.y() + radius * sin(angle)); + } + // bottom-right + for (int i = 0; i < segments_per_corner; ++i) { + const qreal angle = (M_PI_2 * i / segments_per_corner); + vertices[vertIndex++].set(br_c.x() + radius * cos(angle), br_c.y() + radius * sin(angle)); + } + // bottom-left + for (int i = 0; i < segments_per_corner; ++i) { + const qreal angle = M_PI_2 + (M_PI_2 * i / segments_per_corner); + vertices[vertIndex++].set(bl_c.x() + radius * cos(angle), bl_c.y() + radius * sin(angle)); + } + // top-left + for (int i = 0; i < segments_per_corner; ++i) { + const qreal angle = M_PI + (M_PI_2 * i / segments_per_corner); + vertices[vertIndex++].set(tl_c.x() + radius * cos(angle), tl_c.y() + radius * sin(angle)); + } + + // create the triangles using indices + // loop through all perimeter vertices and connect them to the center and the next vertex + for (quint16 i = 0; i < perimeter_verts; ++i) { + indices[indexPos++] = center_vert_index; // center vertex + indices[indexPos++] = center_vert_index + 1 + i; // current perimeter vertex + // the next perimeter vertex / wrapping around to the start at the end + indices[indexPos++] = center_vert_index + 1 + ((i + 1) % perimeter_verts); + } + + // tell renderer to mark all the data as dirty + geometry->markVertexDataDirty(); + geometry->markIndexDataDirty(); +} + +MaskLayer::MaskLayer(QQuickItem *parent) : QQuickItem(parent) +{ + setFlag(ItemHasContents, true); +} + +MaskLayer::~MaskLayer() = default; + +void MaskLayer::addItem(QQuickItem* item) +{ + if (!item || m_sourceItems.contains(item)) { + return; + } + + m_sourceItems.append(item); + + // we connect these signals so that any changes that affects the item's visual representation triggers an update + // we then store connections to be able to disconnect them later + auto& conns = m_connections[item]; + conns.append(QObject::connect(item, &QQuickItem::xChanged, this, &MaskLayer::scheduleUpdate)); + conns.append(QObject::connect(item, &QQuickItem::yChanged, this, &MaskLayer::scheduleUpdate)); + conns.append(QObject::connect(item, &QQuickItem::visibleChanged, this, &MaskLayer::scheduleUpdate)); + conns.append(QObject::connect(item, &QQuickItem::opacityChanged, this, &MaskLayer::scheduleUpdate)); + conns.append(QObject::connect(item, &QObject::destroyed, this, [this, item]() { + removeItem(item); + })); + + const QMetaObject* metaObject = item->metaObject(); + + // due to not being about to tell when the item's transform value changes + // we check for 'scaleAmountChanged()' to use as a sort of work around + int scaleAmountIndex = metaObject->indexOfProperty("scaleAmount"); + if (scaleAmountIndex != -1 && metaObject->property(scaleAmountIndex).hasNotifySignal()) { + conns.append(QObject::connect(item, SIGNAL(scaleAmountChanged()), this, SLOT(scheduleUpdate()))); + } + + // connect the parents signal changes, as this affects the final visible outcome + QQuickItem* currentParent = item->parentItem(); + while (currentParent) { + conns.append(QObject::connect(currentParent, &QQuickItem::xChanged, this, &MaskLayer::scheduleUpdate)); + conns.append(QObject::connect(currentParent, &QQuickItem::yChanged, this, &MaskLayer::scheduleUpdate)); + conns.append(QObject::connect(currentParent, &QQuickItem::opacityChanged, this, &MaskLayer::scheduleUpdate)); + + const QMetaObject* metaObject = currentParent->metaObject(); + + // check for 'scaleAmountChanged()' + int scaleAmountIndex = metaObject->indexOfProperty("scaleAmount"); + if (scaleAmountIndex != -1 && metaObject->property(scaleAmountIndex).hasNotifySignal()) { + conns.append(QObject::connect(currentParent, SIGNAL(scaleAmountChanged()), this, SLOT(scheduleUpdate()))); + } + + currentParent = currentParent->parentItem(); + } + + scheduleUpdate(); +} + +void MaskLayer::removeItem(QQuickItem* item) +{ + if (!item) return; + + disconnectItemSignals(item); + m_connections.remove(item); + m_sourceItems.removeAll(item); + scheduleUpdate(); +} + +void MaskLayer::disconnectItemSignals(QQuickItem* item) +{ + if (m_connections.contains(item)) { + for (const auto &conn : m_connections.value(item)) { + QObject::disconnect(conn); + } + } +} + +void MaskLayer::scheduleUpdate() +{ + // marks this item for an update. + // the renderer will call updatePaintNode before the next frame + update(); +} + +QSGNode *MaskLayer::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) +{ + // if oldNode is null, we need to create a new root node for our content + // otherwise, we can reuse it and manage its children + QSGNode *rootNode = oldNode; + if (!rootNode) { + rootNode = new QSGNode(); + } + + int currentChildIndex = 0; + + for (const QPointer& itemPtr : m_sourceItems) { + QQuickItem* item = itemPtr.data(); + // item was deleted + if (!item) { + continue; + } + + // calculate opacity and visibility + qreal accumulatedOpacity = item->opacity(); + bool isVisible = item->isVisible(); + QQuickItem* currentParent = item->parentItem(); + while (currentParent) { + if (!currentParent->isVisible()) { + isVisible = false; + break; + } + accumulatedOpacity *= currentParent->opacity(); + if (currentParent == this) break; + currentParent = currentParent->parentItem(); + } + + // skip this item if it is invisible or fully transparent + if (!isVisible || qFuzzyCompare(accumulatedOpacity, 0)) { + continue; + } + + // calculate position and size + bool transformOk = false; + const QTransform transform = item->itemTransform(this, &transformOk); + if (!transformOk) continue; + + qreal radius = item->property("radius").toReal(); + + QSGTransformNode *transformNode = nullptr; + QSGGeometryNode *geometryNode = nullptr; + + if (currentChildIndex < rootNode->childCount()) { + transformNode = static_cast(rootNode->childAtIndex(currentChildIndex)); + geometryNode = static_cast(transformNode->firstChild()); + } else { + transformNode = new QSGTransformNode(); + geometryNode = new QSGGeometryNode(); + + QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 0); + + geometryNode->setGeometry(geometry); + + QSGFlatColorMaterial *material = new QSGFlatColorMaterial(); + geometryNode->setMaterial(material); + geometryNode->setFlags(QSGNode::OwnsMaterial); + + transformNode->appendChildNode(geometryNode); + rootNode->appendChildNode(transformNode); + } + + transformNode->setMatrix(QMatrix4x4(transform)); + + QSGFlatColorMaterial *material = static_cast(geometryNode->material()); + QColor color = Qt::white; + color.setAlphaF(accumulatedOpacity); + if (material->color() != color) material->setColor(color); + + QRectF rect(0, 0, item->width(), item->height()); + createRoundedRectGeometry(geometryNode->geometry(), rect, radius); + geometryNode->markDirty(QSGNode::DirtyGeometry); + + + currentChildIndex++; + } + + // if we have more nodes than items this frame, remove the extras + if (currentChildIndex < rootNode->childCount()) { + for (int i = rootNode->childCount() - 1; i >= currentChildIndex; --i) { + QSGNode *nodeToRemove = rootNode->childAtIndex(i); + rootNode->removeChildNode(nodeToRemove); + delete nodeToRemove; + } + } + + return rootNode; +} diff --git a/components/mobileshell/masklayer/masklayer.h b/components/mobileshell/masklayer/masklayer.h new file mode 100644 index 00000000..137a8eb3 --- /dev/null +++ b/components/mobileshell/masklayer/masklayer.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2025 Micah Stanley +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +class QSGNode; + +class MaskLayer : public QQuickItem +{ + Q_OBJECT + QML_ELEMENT + +public: + explicit MaskLayer(QQuickItem *parent = nullptr); + ~MaskLayer() override; + + Q_INVOKABLE void addItem(QQuickItem* item); + Q_INVOKABLE void removeItem(QQuickItem* item); + +protected: + QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) override; + +private slots: + void scheduleUpdate(); + +private: + void disconnectItemSignals(QQuickItem* item); + + QVector> m_sourceItems; + QHash> m_connections; +}; diff --git a/components/mobileshell/masklayer/maskmanager.cpp b/components/mobileshell/masklayer/maskmanager.cpp new file mode 100644 index 00000000..26bef0e2 --- /dev/null +++ b/components/mobileshell/masklayer/maskmanager.cpp @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: 2025 Micah Stanley +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "maskmanager.h" +#include "masklayer.h" + +MaskManager::MaskManager(QQuickItem *parent) +: QQuickItem(parent), +m_maskLayer(new MaskLayer(this)) +{ +} + +MaskManager::~MaskManager() = default; + +void MaskManager::componentComplete() { + QQuickItem::componentComplete(); + // ensure the mask layers fill the dimensions + m_maskLayer->setX(0); + m_maskLayer->setY(0); + m_maskLayer->setWidth(width()); + m_maskLayer->setHeight(height()); + m_maskLayer->setZ(z()); + + connect(this, &QQuickItem::widthChanged, this, [this]() { + m_maskLayer->setWidth(width()); + }); + connect(this, &QQuickItem::heightChanged, this, [this]() { + m_maskLayer->setHeight(height()); + }); +} + +QQuickItem* MaskManager::maskLayer() const { + return m_maskLayer; +} + +void MaskManager::assignToMask(QQuickItem* item) { + if (!item) { + qWarning() << "Cannot assign a null item to a mask."; + return; + } + + m_maskLayer->addItem(item); +} diff --git a/components/mobileshell/masklayer/maskmanager.h b/components/mobileshell/masklayer/maskmanager.h new file mode 100644 index 00000000..59bab0c3 --- /dev/null +++ b/components/mobileshell/masklayer/maskmanager.h @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2025 Micah Stanley +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +class MaskLayer; + +class MaskManager : public QQuickItem +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(QQuickItem* maskLayer READ maskLayer CONSTANT) + +public: + explicit MaskManager(QQuickItem *parent = nullptr); + ~MaskManager() override; + + QQuickItem* maskLayer() const; + + Q_INVOKABLE void assignToMask(QQuickItem* item); + +protected: + void componentComplete() override; + +private: + MaskLayer* m_maskLayer; +}; diff --git a/components/mobileshell/qml/homescreen/BlurEffect.qml b/components/mobileshell/qml/homescreen/BlurEffect.qml new file mode 100644 index 00000000..163e0ebd --- /dev/null +++ b/components/mobileshell/qml/homescreen/BlurEffect.qml @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: 2023-2025 Devin Lin +// SPDX-FileCopyrightText: 2025 Micah Stanley +// SPDX-License-Identifier: LGPL-2.0-or-later + +import QtQuick +import QtQuick.Layouts +import Qt5Compat.GraphicalEffects + +Loader { + id: root + + property Item sourceLayer + property Item maskSourceLayer + // this value is used to switch between blurring the whole wallpaper or just behind the mask areas + property real fullBlur: 1 + // gets multiplied against the screen size to set the texture size + readonly property real blurTextureQuality: 0.5 + readonly property var textureSize: Qt.size(Math.round(root.width * root.blurTextureQuality), Math.round(root.height * root.blurTextureQuality)) + readonly property int fastBlurRadius: 42 + + sourceComponent: Item { + // only take samples from wallpaper when we need the blur for performance + ShaderEffectSource { + id: controlledWallpaperSource + anchors.fill: parent + + // this layer will be blurred, so it looks fine to have a lower texture quality to help with performance + textureSize: root.textureSize + + hideSource: false + opacity: root.fullBlur + visible: opacity > 0 + + // wallpaper blur + // we attempted to use MultiEffect in the past, but it had very poor performance on the PinePhone + sourceItem: FastBlur { + height: controlledWallpaperSource.textureSize.height + width: controlledWallpaperSource.textureSize.width + + cached: true + radius: root.fastBlurRadius + + source: ShaderEffectSource { + anchors.fill: parent + + textureSize: controlledWallpaperSource.textureSize + + sourceItem: root.sourceLayer + hideSource: false + } + } + } + + // load in the layer mask so we can utilize it with the OpacityMask + Item { + id: blurMask + anchors.fill: parent + layer.enabled: true + layer.smooth: true + opacity: 0 + + Loader { + asynchronous: true + active: root.maskSourceLayer != null && root.fullBlur != 1 + anchors.fill: parent + + sourceComponent: maskSource + + property Component maskSource: Item { + ShaderEffectSource { + anchors.fill: parent + + sourceItem: root.maskSourceLayer + hideSource: false + live: true + } + } + + } + } + + // here we utilize the mask on the blur layer so we can blur behind the some homescreen items + OpacityMask { + anchors.fill: parent + source: controlledWallpaperSource + maskSource: blurMask + visible: opacity > 0 && root.maskSourceLayer != null + } + } +} diff --git a/components/mobileshell/qml/homescreen/HomeScreen.qml b/components/mobileshell/qml/homescreen/HomeScreen.qml index 24d0eb82..78def76c 100644 --- a/components/mobileshell/qml/homescreen/HomeScreen.qml +++ b/components/mobileshell/qml/homescreen/HomeScreen.qml @@ -105,7 +105,7 @@ Item { } } -//END API implementation + //END API implementation Component.onCompleted: { // determine the margins used diff --git a/components/mobileshell/qml/homescreen/HomeScreenWallpaperBlur.qml b/components/mobileshell/qml/homescreen/HomeScreenWallpaperBlur.qml deleted file mode 100644 index 04b73620..00000000 --- a/components/mobileshell/qml/homescreen/HomeScreenWallpaperBlur.qml +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-FileCopyrightText: 2023-2025 Devin Lin -// SPDX-License-Identifier: LGPL-2.0-or-later - -import QtQuick -import QtQuick.Layouts -import Qt5Compat.GraphicalEffects - -Loader { - id: root - property real blurOpacity - property Item wallpaperItem - - sourceComponent: Item { - id: wallpaper - anchors.fill: parent - - // only take samples from wallpaper when we need the blur for performance - ShaderEffectSource { - id: controlledWallpaperSource - anchors.fill: parent - - live: blur.visible - hideSource: false - visible: false - sourceItem: root.wallpaperItem - } - - // wallpaper blur - // we attempted to use MultiEffect in the past, but it had very poor performance on the PinePhone - FastBlur { - id: blur - radius: 50 - cached: true - source: controlledWallpaperSource - anchors.fill: parent - visible: opacity > 0 - opacity: root.blurOpacity - } - } -} \ No newline at end of file diff --git a/components/mobileshell/qml/homescreen/WallpaperSelector.qml b/components/mobileshell/qml/homescreen/WallpaperSelector.qml index 8bee788e..ad51c246 100644 --- a/components/mobileshell/qml/homescreen/WallpaperSelector.qml +++ b/components/mobileshell/qml/homescreen/WallpaperSelector.qml @@ -11,11 +11,14 @@ import org.kde.kirigami 2.20 as Kirigami import org.kde.plasma.wallpapers.image 2.0 as Wallpaper import org.kde.kquickcontrolsaddons 2.0 as Addons import org.kde.plasma.private.mobileshell.wallpaperimageplugin as WallpaperImagePlugin +import org.kde.plasma.private.mobileshell as MobileShell Controls.Drawer { id: imageWallpaperDrawer dragMargin: 0 + property MobileShell.MaskManager maskManager + required property bool horizontal signal wallpaperSettingsRequested() @@ -51,8 +54,8 @@ Controls.Drawer { header: Controls.ItemDelegate { id: openSettings - width: imageWallpaperDrawer.horizontal ? parent.width : height * (imageWallpaperDrawer.width / imageWallpaperDrawer.Screen.height) - height: imageWallpaperDrawer.horizontal ? width / (imageWallpaperDrawer.Screen.width / imageWallpaperDrawer.Screen.height) : parent.height + width: imageWallpaperDrawer.horizontal ? wallpapersView.width : height * (imageWallpaperDrawer.width / imageWallpaperDrawer.Screen.height) + height: imageWallpaperDrawer.horizontal ? width / (imageWallpaperDrawer.Screen.width / imageWallpaperDrawer.Screen.height) : wallpapersView.height padding: Kirigami.Units.gridUnit / 2 leftPadding: padding topPadding: padding @@ -62,6 +65,12 @@ Controls.Drawer { background: Rectangle { radius: Kirigami.Units.cornerRadius color: Qt.rgba(255, 255, 255, (openSettings.down || openSettings.highlighted) ? 0.3 : 0.2) + + Component.onCompleted: { + if (maskManager) { + maskManager.assignToMask(this) + } + } } contentItem: Item { @@ -81,11 +90,11 @@ Controls.Drawer { delegate: Controls.ItemDelegate { id: delegate - width: imageWallpaperDrawer.horizontal ? parent.width : height * (imageWallpaperDrawer.width / imageWallpaperDrawer.Screen.height) - height: imageWallpaperDrawer.horizontal ? width / (imageWallpaperDrawer.Screen.width / imageWallpaperDrawer.Screen.height) : (parent ? parent.height : 0) - padding: Kirigami.Units.largeSpacing - (ListView.isCurrentItem ? Kirigami.Units.smallSpacing : 0) - property real inset: ListView.isCurrentItem ? 0 : Kirigami.Units.smallSpacing - Behavior on inset { + width: imageWallpaperDrawer.horizontal ? wallpapersView.width : height * (imageWallpaperDrawer.width / imageWallpaperDrawer.Screen.height) + height: imageWallpaperDrawer.horizontal ? width / (imageWallpaperDrawer.Screen.width / imageWallpaperDrawer.Screen.height) : (wallpapersView ? wallpapersView.height : 0) + padding: Kirigami.Units.largeSpacing - (wallpapersView.currentIndex === index ? Kirigami.Units.smallSpacing : 0) + property real scaleAmount: wallpapersView.currentIndex === index ? 0 : Kirigami.Units.smallSpacing + Behavior on scaleAmount { NumberAnimation { duration: Kirigami.Units.longDuration easing.type: Easing.InOutQuad @@ -102,10 +111,10 @@ Controls.Drawer { topPadding: padding rightPadding: padding bottomPadding: padding - topInset: inset - bottomInset: inset - leftInset: inset - rightInset: inset + topInset: scaleAmount + bottomInset: scaleAmount + leftInset: scaleAmount + rightInset: scaleAmount property bool isCurrent: WallpaperImagePlugin.WallpaperPlugin.homescreenWallpaperPath == model.path onIsCurrentChanged: { @@ -143,6 +152,12 @@ Controls.Drawer { background: Rectangle { color: Qt.rgba(255, 255, 255, (delegate.down || delegate.highlighted) ? 0.4 : 0.2) radius: Kirigami.Units.cornerRadius + + Component.onCompleted: { + if (maskManager) { + maskManager.assignToMask(this) + } + } } } } diff --git a/containments/homescreens/folio/foliosettings.cpp b/containments/homescreens/folio/foliosettings.cpp index 9faeb751..19735aad 100644 --- a/containments/homescreens/folio/foliosettings.cpp +++ b/containments/homescreens/folio/foliosettings.cpp @@ -18,12 +18,12 @@ const QString CFG_KEY_LOCK_LAYOUT = QStringLiteral("lockLayout"); const QString CFG_KEY_DELEGATE_ICON_SIZE = QStringLiteral("delegateIconSize"); const QString CFG_KEY_SHOW_FAVORITES_BAR_BACKGROUND = QStringLiteral("showFavoritesBarBackground"); const QString CFG_KEY_PAGE_TRANSITION_EFFECT = QStringLiteral("pageTransitionEffect"); -const QString CFG_KEY_SHOW_WALLPAPER_BLUR = QStringLiteral("showWallpaperBlur"); +const QString CFG_KEY_SHOW_WALLPAPER_BLUR = QStringLiteral("wallpaperBlurEffect"); const QString CFG_KEY_DOUBLE_TAP_TO_LOCK = QStringLiteral("doubleTapToLock"); FolioSettings::FolioSettings(HomeScreen *parent) - : QObject{parent} - , m_homeScreen{parent} +: QObject{parent} +, m_homeScreen{parent} { } @@ -140,16 +140,16 @@ void FolioSettings::setPageTransitionEffect(PageTransitionEffect pageTransitionE } } -bool FolioSettings::showWallpaperBlur() const +FolioSettings::WallpaperBlurEffect FolioSettings::wallpaperBlurEffect() const { - return m_showWallpaperBlur; + return m_wallpaperBlurEffect; } -void FolioSettings::setShowWallpaperBlur(bool showWallpaperBlur) +void FolioSettings::setWallpaperBlurEffect(WallpaperBlurEffect wallpaperBlurEffect) { - if (m_showWallpaperBlur != showWallpaperBlur) { - m_showWallpaperBlur = showWallpaperBlur; - Q_EMIT showWallpaperBlurChanged(); + if (m_wallpaperBlurEffect != wallpaperBlurEffect) { + m_wallpaperBlurEffect = wallpaperBlurEffect; + Q_EMIT wallpaperBlurEffectChanged(); save(); } } @@ -182,7 +182,7 @@ void FolioSettings::save() m_homeScreen->config().writeEntry(CFG_KEY_DELEGATE_ICON_SIZE, m_delegateIconSize); m_homeScreen->config().writeEntry(CFG_KEY_SHOW_FAVORITES_BAR_BACKGROUND, m_showFavouritesBarBackground); m_homeScreen->config().writeEntry(CFG_KEY_PAGE_TRANSITION_EFFECT, (int)m_pageTransitionEffect); - m_homeScreen->config().writeEntry(CFG_KEY_SHOW_WALLPAPER_BLUR, m_showWallpaperBlur); + m_homeScreen->config().writeEntry(CFG_KEY_SHOW_WALLPAPER_BLUR, (int)m_wallpaperBlurEffect); m_homeScreen->config().writeEntry(CFG_KEY_DOUBLE_TAP_TO_LOCK, m_doubleTapToLock); Q_EMIT m_homeScreen->configNeedsSaving(); @@ -202,7 +202,7 @@ void FolioSettings::load() m_delegateIconSize = m_homeScreen->config().readEntry(CFG_KEY_DELEGATE_ICON_SIZE, 48); m_showFavouritesBarBackground = m_homeScreen->config().readEntry(CFG_KEY_SHOW_FAVORITES_BAR_BACKGROUND, true); m_pageTransitionEffect = static_cast(m_homeScreen->config().readEntry(CFG_KEY_PAGE_TRANSITION_EFFECT, (int)SlideTransition)); - m_showWallpaperBlur = m_homeScreen->config().readEntry(CFG_KEY_SHOW_WALLPAPER_BLUR, true); + m_wallpaperBlurEffect = static_cast(m_homeScreen->config().readEntry(CFG_KEY_SHOW_WALLPAPER_BLUR, (int)Full)); m_doubleTapToLock = m_homeScreen->config().readEntry(CFG_KEY_DOUBLE_TAP_TO_LOCK, true); Q_EMIT homeScreenRowsChanged(); @@ -211,7 +211,7 @@ void FolioSettings::load() Q_EMIT showFavouritesAppLabelsChanged(); Q_EMIT lockLayoutChanged(); Q_EMIT delegateIconSizeChanged(); - Q_EMIT showWallpaperBlurChanged(); + Q_EMIT wallpaperBlurEffectChanged(); Q_EMIT doubleTapToLockChanged(); } diff --git a/containments/homescreens/folio/foliosettings.h b/containments/homescreens/folio/foliosettings.h index f3743010..74371d2e 100644 --- a/containments/homescreens/folio/foliosettings.h +++ b/containments/homescreens/folio/foliosettings.h @@ -23,7 +23,7 @@ class FolioSettings : public QObject Q_PROPERTY(bool showFavouritesBarBackground READ showFavouritesBarBackground WRITE setShowFavouritesBarBackground NOTIFY showFavouritesBarBackgroundChanged) Q_PROPERTY( FolioSettings::PageTransitionEffect pageTransitionEffect READ pageTransitionEffect WRITE setPageTransitionEffect NOTIFY pageTransitionEffectChanged) - Q_PROPERTY(bool showWallpaperBlur READ showWallpaperBlur WRITE setShowWallpaperBlur NOTIFY showWallpaperBlurChanged) + Q_PROPERTY(FolioSettings::WallpaperBlurEffect wallpaperBlurEffect READ wallpaperBlurEffect WRITE setWallpaperBlurEffect NOTIFY wallpaperBlurEffectChanged) Q_PROPERTY(bool doubleTapToLock READ doubleTapToLock WRITE setDoubleTapToLock NOTIFY doubleTapToLockChanged) public: @@ -39,6 +39,13 @@ public: }; Q_ENUM(PageTransitionEffect) + enum WallpaperBlurEffect { + None = 0, + Simple = 1, + Full = 2, + }; + Q_ENUM(WallpaperBlurEffect) + // number of rows and columns in the config for the homescreen // NOTE: use HomeScreenState.pageRows() instead in UI logic since we may have the rows and // columns swapped (in landscape layouts) @@ -66,8 +73,8 @@ public: PageTransitionEffect pageTransitionEffect() const; void setPageTransitionEffect(PageTransitionEffect pageTransitionEffect); - bool showWallpaperBlur() const; - void setShowWallpaperBlur(bool showWallpaperBlur); + WallpaperBlurEffect wallpaperBlurEffect() const; + void setWallpaperBlurEffect(WallpaperBlurEffect wallpaperBlurEffect); bool doubleTapToLock() const; void setDoubleTapToLock(bool doubleTapToLock); @@ -86,7 +93,7 @@ Q_SIGNALS: void delegateIconSizeChanged(); void showFavouritesBarBackgroundChanged(); void pageTransitionEffectChanged(); - void showWallpaperBlurChanged(); + void wallpaperBlurEffectChanged(); void doubleTapToLockChanged(); private: @@ -102,6 +109,6 @@ private: qreal m_delegateIconSize{48}; bool m_showFavouritesBarBackground{false}; PageTransitionEffect m_pageTransitionEffect{SlideTransition}; - bool m_showWallpaperBlur{false}; + WallpaperBlurEffect m_wallpaperBlurEffect{Full}; bool m_doubleTapToLock{false}; }; diff --git a/containments/homescreens/folio/package/contents/ui/DelegateDragItem.qml b/containments/homescreens/folio/package/contents/ui/DelegateDragItem.qml index 5caae129..24d8cfb0 100644 --- a/containments/homescreens/folio/package/contents/ui/DelegateDragItem.qml +++ b/containments/homescreens/folio/package/contents/ui/DelegateDragItem.qml @@ -6,12 +6,14 @@ import QtQuick.Layouts import org.kde.kirigami 2.20 as Kirigami import org.kde.private.mobile.homescreen.folio 1.0 as Folio +import org.kde.plasma.private.mobileshell as MobileShell import "./delegate" Item { id: root property Folio.HomeScreen folio + property MobileShell.MaskManager maskManager property Folio.FolioDelegate delegate width: folio.HomeScreenState.pageCellWidth @@ -34,7 +36,7 @@ Item { } // animate drop x - XAnimator on x { + NumberAnimation on x { id: dragXAnim running: false duration: Kirigami.Units.longDuration @@ -46,7 +48,7 @@ Item { } // animate drop y - YAnimator on y { + NumberAnimation on y { id: dragYAnim running: false duration: Kirigami.Units.longDuration @@ -140,7 +142,7 @@ Item { // scale animation if we are creating, or inserting into a folder scaleAnim.restart(); - } + } } // if the drop has been abandoned, just hide @@ -158,6 +160,7 @@ Item { DelegateIconLoader { id: loader folio: root.folio + maskManager: root.maskManager Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom Layout.minimumWidth: folio.FolioSettings.delegateIconSize Layout.minimumHeight: folio.FolioSettings.delegateIconSize diff --git a/containments/homescreens/folio/package/contents/ui/FavouritesBar.qml b/containments/homescreens/folio/package/contents/ui/FavouritesBar.qml index f8a75cc0..0feb4e49 100644 --- a/containments/homescreens/folio/package/contents/ui/FavouritesBar.qml +++ b/containments/homescreens/folio/package/contents/ui/FavouritesBar.qml @@ -17,6 +17,7 @@ import "./delegate" MouseArea { id: root property Folio.HomeScreen folio + property MobileShell.MaskManager maskManager property var homeScreen @@ -53,11 +54,11 @@ MouseArea { readonly property var dragState: folio.HomeScreenState.dragState readonly property bool isDropPositionThis: dragState.candidateDropPosition.location === Folio.DelegateDragPosition.Favourites && - dragState.candidateDropPosition.favouritesPosition === delegate.index + dragState.candidateDropPosition.favouritesPosition === delegate.index readonly property bool isAppHoveredOver: folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate && - dragState.dropDelegate && - dragState.dropDelegate.type === Folio.FolioDelegate.Application && - isDropPositionThis + dragState.dropDelegate && + dragState.dropDelegate.type === Folio.FolioDelegate.Application && + isDropPositionThis readonly property bool isLocationBottom: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Bottom @@ -111,6 +112,7 @@ MouseArea { AppDelegate { id: appDelegate folio: root.folio + maskManager: root.maskManager application: delegate.delegateModel.application name: folio.FolioSettings.showFavouritesAppLabels ? delegate.delegateModel.application.name : "" shadow: true @@ -184,14 +186,15 @@ MouseArea { AppFolderDelegate { id: appFolderDelegate folio: root.folio + maskManager: root.maskManager shadow: true folder: delegate.delegateModel.folder name: folio.FolioSettings.showFavouritesAppLabels ? delegate.delegateModel.folder.name : "" // do not show if the drop animation is running to this delegate, and the drop delegate is a folder visible: !(root.homeScreen.dropAnimationRunning && - delegate.isDropPositionThis && - delegate.dragState.dropDelegate.type === Folio.FolioDelegate.Folder) + delegate.isDropPositionThis && + delegate.dragState.dropDelegate.type === Folio.FolioDelegate.Folder) appHoveredOver: delegate.isAppHoveredOver diff --git a/containments/homescreens/folio/package/contents/ui/HomeScreen.qml b/containments/homescreens/folio/package/contents/ui/HomeScreen.qml index cb07b95f..4098c477 100644 --- a/containments/homescreens/folio/package/contents/ui/HomeScreen.qml +++ b/containments/homescreens/folio/package/contents/ui/HomeScreen.qml @@ -20,6 +20,7 @@ import "./settings" Item { id: root property Folio.HomeScreen folio + property MobileShell.MaskManager maskManager property Folio.HomeScreenState homeScreenState: folio.HomeScreenState property real topMargin: 0 @@ -43,6 +44,8 @@ Item { onLeftMarginChanged: folio.HomeScreenState.viewLeftPadding = root.leftMargin onRightMarginChanged: folio.HomeScreenState.viewRightPadding = root.rightMargin + signal wallpaperSelectorTriggered() + // called by any delegates when starting drag // returns the mapped coordinates to be used in the home screen state function prepareStartDelegateDrag(delegate, item, skipSwipeThreshold) { @@ -107,10 +110,10 @@ Item { interactive: root.interactive && settings.homeScreenInteractive && (appDrawer.flickable.contentY <= 10 || // disable the swipe area when we are swiping in the app drawer, and not in drag-and-drop - folio.HomeScreenState.swipeState === Folio.HomeScreenState.AwaitingDraggingDelegate || - folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate || - folio.HomeScreenState.swipeState === Folio.HomeScreenState.SwipingAppDrawerGrid || - folio.HomeScreenState.viewState !== Folio.HomeScreenState.AppDrawerView) + folio.HomeScreenState.swipeState === Folio.HomeScreenState.AwaitingDraggingDelegate || + folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate || + folio.HomeScreenState.swipeState === Folio.HomeScreenState.SwipingAppDrawerGrid || + folio.HomeScreenState.viewState !== Folio.HomeScreenState.AppDrawerView) onSwipeStarted: (currentPos, startPos) => { const deltaX = currentPos.x - startPos.x; @@ -182,6 +185,7 @@ Item { HomeScreenPages { id: homeScreenPages folio: root.folio + maskManager: root.maskManager homeScreen: root anchors.topMargin: root.topMargin @@ -247,6 +251,8 @@ Item { id: favouritesBarScrim color: Qt.rgba(255, 255, 255, 0.2) + Component.onCompleted: maskManager.assignToMask(this) + // don't show in settings mode opacity: 1 - folio.HomeScreenState.settingsOpenProgress visible: folio.FolioSettings.showFavouritesBarBackground @@ -266,6 +272,7 @@ Item { FavouritesBar { id: favouritesBar folio: root.folio + maskManager: root.maskManager homeScreen: root // don't show in settings mode @@ -399,18 +406,6 @@ Item { transform: Translate { y: folderView.opacity > 0 ? 0 : folderView.height } } - // drag and drop component - DelegateDragItem { - id: delegateDragItem - folio: root.folio - } - - // drag and drop for widgets - WidgetDragItem { - id: widgetDragItem - folio: root.folio - } - // bottom app drawer AppDrawer { id: appDrawer diff --git a/containments/homescreens/folio/package/contents/ui/HomeScreenPage.qml b/containments/homescreens/folio/package/contents/ui/HomeScreenPage.qml index 9eb8218a..e03df5fe 100644 --- a/containments/homescreens/folio/package/contents/ui/HomeScreenPage.qml +++ b/containments/homescreens/folio/package/contents/ui/HomeScreenPage.qml @@ -18,6 +18,7 @@ import "./private" Item { id: root property Folio.HomeScreen folio + property MobileShell.MaskManager maskManager property int pageNum @@ -50,10 +51,10 @@ Item { // only show if it is an empty spot on this page visible: folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate && - dropPosition.location === Folio.DelegateDragPosition.Pages && - dropPosition.page === root.pageNum && - !dropDelegateIsWidget && - folio.HomeScreenState.getPageDelegateAt(root.pageNum, dropPosition.pageRow, dropPosition.pageColumn) === null + dropPosition.location === Folio.DelegateDragPosition.Pages && + dropPosition.page === root.pageNum && + !dropDelegateIsWidget && + folio.HomeScreenState.getPageDelegateAt(root.pageNum, dropPosition.pageRow, dropPosition.pageColumn) === null x: dropPosition.pageColumn * folio.HomeScreenState.pageCellWidth y: dropPosition.pageRow * folio.HomeScreenState.pageCellHeight @@ -71,10 +72,10 @@ Item { // only show if the widget can be placed here visible: folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate && - dropPosition.location === Folio.DelegateDragPosition.Pages && - dropPosition.page === root.pageNum && - dropDelegateIsWidget && - pageModel.canAddDelegate(dropPosition.pageRow, dropPosition.pageColumn, dropDelegate) + dropPosition.location === Folio.DelegateDragPosition.Pages && + dropPosition.page === root.pageNum && + dropDelegateIsWidget && + pageModel.canAddDelegate(dropPosition.pageRow, dropPosition.pageColumn, dropDelegate) radius: Kirigami.Units.cornerRadius color: Qt.rgba(255, 255, 255, 0.3) @@ -100,14 +101,14 @@ Item { property var dragState: folio.HomeScreenState.dragState property bool isDropPositionThis: dragState.candidateDropPosition.location === Folio.DelegateDragPosition.Pages && - dragState.candidateDropPosition.page === root.pageNum && - dragState.candidateDropPosition.pageRow === delegate.pageDelegate.row && - dragState.candidateDropPosition.pageColumn === delegate.pageDelegate.column + dragState.candidateDropPosition.page === root.pageNum && + dragState.candidateDropPosition.pageRow === delegate.pageDelegate.row && + dragState.candidateDropPosition.pageColumn === delegate.pageDelegate.column property bool isAppHoveredOver: folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate && - dragState.dropDelegate && - dragState.dropDelegate.type === Folio.FolioDelegate.Application && - isDropPositionThis + dragState.dropDelegate && + dragState.dropDelegate.type === Folio.FolioDelegate.Application && + isDropPositionThis implicitWidth: loader.item ? loader.item.implicitWidth : 0 implicitHeight: loader.item ? loader.item.implicitHeight : 0 @@ -118,7 +119,7 @@ Item { y: row * folio.HomeScreenState.pageCellHeight visible: row >= 0 && row < folio.HomeScreenState.pageRows && - column >= 0 && column < folio.HomeScreenState.pageColumns + column >= 0 && column < folio.HomeScreenState.pageColumns // called when we want to delete this delegate function removeSelf() { @@ -161,6 +162,7 @@ Item { AppDelegate { id: appDelegate folio: root.folio + maskManager: root.maskManager name: folio.FolioSettings.showPagesAppLabels ? delegate.pageDelegate.application.name : "" application: delegate.pageDelegate.application turnToFolder: delegate.isAppHoveredOver @@ -238,6 +240,7 @@ Item { AppFolderDelegate { id: appFolderDelegate folio: root.folio + maskManager: root.maskManager name: folio.FolioSettings.showPagesAppLabels ? delegate.pageDelegate.folder.name : "" folder: delegate.pageDelegate.folder @@ -248,8 +251,8 @@ Item { // do not show if the drop animation is running to this delegate, and the drop delegate is a folder visible: !(root.homeScreen.dropAnimationRunning && - delegate.isDropPositionThis && - delegate.dragState.dropDelegate.type === Folio.FolioDelegate.Folder) + delegate.isDropPositionThis && + delegate.dragState.dropDelegate.type === Folio.FolioDelegate.Folder) // don't show label in drag and drop mode labelOpacity: delegate.opacity @@ -334,7 +337,7 @@ Item { // background: there is only one "visual" instance of the widget, once this delegate loads // it will reparent it to here (but we don't want it to happen while the drop animation is running) property bool suppressAppletReparent: (root.homeScreen.currentlyDraggedWidget === delegate.pageDelegate.widget) - && delegate.isDropPositionThis + && delegate.isDropPositionThis visible: !suppressAppletReparent widget: suppressAppletReparent ? null : delegate.pageDelegate.widget diff --git a/containments/homescreens/folio/package/contents/ui/HomeScreenPages.qml b/containments/homescreens/folio/package/contents/ui/HomeScreenPages.qml index 0493fbc1..b0670ea9 100644 --- a/containments/homescreens/folio/package/contents/ui/HomeScreenPages.qml +++ b/containments/homescreens/folio/package/contents/ui/HomeScreenPages.qml @@ -13,6 +13,7 @@ import org.kde.private.mobile.homescreen.folio 1.0 as Folio MouseArea { id: root property Folio.HomeScreen folio + property MobileShell.MaskManager maskManager property var homeScreen @@ -44,6 +45,7 @@ MouseArea { delegate: HomeScreenPage { id: homeScreenPage folio: root.folio + maskManager: root.maskManager pageNum: model.index pageModel: model.delegate homeScreen: root.homeScreen @@ -64,7 +66,7 @@ MouseArea { switch (folio.FolioSettings.pageTransitionEffect) { case Folio.FolioSettings.StackTransition: return (positionX < 0) ? progressToCenter : - ((progressToCenter < 0.3) ? 0 : ((1 / 0.7) * (progressToCenter - 0.3))) + ((progressToCenter < 0.3) ? 0 : ((1 / 0.7) * (progressToCenter - 0.3))) default: return progressToCenter; } @@ -109,8 +111,8 @@ MouseArea { Rotation { id: cubeTransitionRotation origin.x: (positionX < 0) ? - (folio.HomeScreenState.pageWidth / 2) * homeScreenPage.progressToCenter : - (folio.HomeScreenState.pageWidth / 2) + (folio.HomeScreenState.pageWidth / 2) * (1 - homeScreenPage.progressToCenter); + (folio.HomeScreenState.pageWidth / 2) * homeScreenPage.progressToCenter : + (folio.HomeScreenState.pageWidth / 2) + (folio.HomeScreenState.pageWidth / 2) * (1 - homeScreenPage.progressToCenter); origin.y: folio.HomeScreenState.pageHeight / 2; axis { x: 0; y: 1; z: 0 } angle: { @@ -121,8 +123,8 @@ MouseArea { Rotation { id: rotationTransitionRotation origin.x: (positionX < 0) ? - (folio.HomeScreenState.pageWidth / 2) * homeScreenPage.progressToCenter : - (folio.HomeScreenState.pageWidth / 2) + (folio.HomeScreenState.pageWidth / 2) * (1 - homeScreenPage.progressToCenter); + (folio.HomeScreenState.pageWidth / 2) * homeScreenPage.progressToCenter : + (folio.HomeScreenState.pageWidth / 2) + (folio.HomeScreenState.pageWidth / 2) * (1 - homeScreenPage.progressToCenter); origin.y: 0 axis { x: -0.2; y: 0.3; z: 0.5 } angle: { diff --git a/containments/homescreens/folio/package/contents/ui/delegate/AbstractDelegate.qml b/containments/homescreens/folio/package/contents/ui/delegate/AbstractDelegate.qml index 02768960..593499f4 100644 --- a/containments/homescreens/folio/package/contents/ui/delegate/AbstractDelegate.qml +++ b/containments/homescreens/folio/package/contents/ui/delegate/AbstractDelegate.qml @@ -18,6 +18,7 @@ import org.kde.plasma.private.mobileshell as MobileShell Folio.DelegateTouchArea { id: root property Folio.HomeScreen folio + property MobileShell.MaskManager maskManager property string name property bool shadow: false @@ -29,10 +30,10 @@ Folio.DelegateTouchArea { signal afterClickAnimation() // grow/shrink animation - property real zoomScale: 1 + property real scaleAmount: 1 property bool clickRequested: false - NumberAnimation on zoomScale { + NumberAnimation on scaleAmount { id: shrinkAnim running: false duration: ShellSettings.Settings.animationsEnabled ? 80 : 1 @@ -44,7 +45,7 @@ Folio.DelegateTouchArea { } } - NumberAnimation on zoomScale { + NumberAnimation on scaleAmount { id: growAnim running: false duration: ShellSettings.Settings.animationsEnabled ? 80 : 1 @@ -86,8 +87,8 @@ Folio.DelegateTouchArea { transform: Scale { origin.x: root.width / 2; origin.y: root.height / 2; - xScale: root.zoomScale - yScale: root.zoomScale + xScale: root.scaleAmount + yScale: root.scaleAmount } MobileShell.BaseItem { diff --git a/containments/homescreens/folio/package/contents/ui/delegate/AppDelegate.qml b/containments/homescreens/folio/package/contents/ui/delegate/AppDelegate.qml index f8ee9a93..7c810597 100644 --- a/containments/homescreens/folio/package/contents/ui/delegate/AppDelegate.qml +++ b/containments/homescreens/folio/package/contents/ui/delegate/AppDelegate.qml @@ -62,6 +62,12 @@ AbstractDelegate { color: Qt.rgba(255, 255, 255, 0.3) anchors.fill: parent + Component.onCompleted: { + if (maskManager) { + maskManager.assignToMask(this) + } + } + opacity: root.turnToFolder ? 1 : 0 property real scaleAmount: root.turnToFolder ? 1.2 : 1.0 diff --git a/containments/homescreens/folio/package/contents/ui/delegate/AppFolderDelegate.qml b/containments/homescreens/folio/package/contents/ui/delegate/AppFolderDelegate.qml index a63d1e78..f34d187b 100644 --- a/containments/homescreens/folio/package/contents/ui/delegate/AppFolderDelegate.qml +++ b/containments/homescreens/folio/package/contents/ui/delegate/AppFolderDelegate.qml @@ -19,6 +19,7 @@ AbstractDelegate { contentItem: DelegateFolderIcon { folio: root.folio + maskManager: root.maskManager folder: root.folder expandBackground: root.appHoveredOver } diff --git a/containments/homescreens/folio/package/contents/ui/delegate/DelegateFolderIcon.qml b/containments/homescreens/folio/package/contents/ui/delegate/DelegateFolderIcon.qml index 172e02e8..f1ee13ae 100644 --- a/containments/homescreens/folio/package/contents/ui/delegate/DelegateFolderIcon.qml +++ b/containments/homescreens/folio/package/contents/ui/delegate/DelegateFolderIcon.qml @@ -9,10 +9,12 @@ import QtQuick.Effects import org.kde.kirigami 2.20 as Kirigami import org.kde.private.mobile.homescreen.folio 1.0 as Folio +import org.kde.plasma.private.mobileshell as MobileShell Item { id: root property Folio.HomeScreen folio + property MobileShell.MaskManager maskManager property Folio.FolioApplicationFolder folder @@ -27,6 +29,12 @@ Item { color: Qt.rgba(255, 255, 255, 0.3) anchors.fill: parent + Component.onCompleted: { + if (maskManager) { + maskManager.assignToMask(this) + } + } + property real scaleAmount: root.expandBackground ? 1.2 : 1.0 Behavior on scaleAmount { NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad } } diff --git a/containments/homescreens/folio/package/contents/ui/delegate/DelegateIconLoader.qml b/containments/homescreens/folio/package/contents/ui/delegate/DelegateIconLoader.qml index 2681af2e..9a7eaf3b 100644 --- a/containments/homescreens/folio/package/contents/ui/delegate/DelegateIconLoader.qml +++ b/containments/homescreens/folio/package/contents/ui/delegate/DelegateIconLoader.qml @@ -9,10 +9,12 @@ import QtQuick.Effects import org.kde.kirigami 2.20 as Kirigami import org.kde.private.mobile.homescreen.folio 1.0 as Folio +import org.kde.plasma.private.mobileshell as MobileShell Loader { id: root property Folio.HomeScreen folio + property MobileShell.MaskManager maskManager height: folio.FolioSettings.delegateIconSize width: folio.FolioSettings.delegateIconSize @@ -50,6 +52,7 @@ Loader { DelegateFolderIcon { folio: root.folio + maskManager: root.maskManager folder: delegate.folder } } diff --git a/containments/homescreens/folio/package/contents/ui/main.qml b/containments/homescreens/folio/package/contents/ui/main.qml index f12002f2..9a24cdda 100644 --- a/containments/homescreens/folio/package/contents/ui/main.qml +++ b/containments/homescreens/folio/package/contents/ui/main.qml @@ -18,6 +18,8 @@ import org.kde.private.mobile.homescreen.folio 1.0 as Folio import org.kde.plasma.private.mobileshell.windowplugin as WindowPlugin import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings +import "./private" + ContainmentItem { id: root property Folio.HomeScreen folio: root.plasmoid @@ -31,19 +33,31 @@ ContainmentItem { forceActiveFocus(); } - MobileShell.HomeScreenWallpaperBlur { - id: wallpaperBlur - active: folio.FolioSettings.showWallpaperBlur - anchors.fill: parent - wallpaperItem: Plasmoid.wallpaperGraphicsObject + property MobileShell.MaskManager maskManager: MobileShell.MaskManager { + height: root.height + width: root.width + } - blurOpacity: Math.min(1, - Math.max( - 1 - homeScreen.contentOpacity, - folio.HomeScreenState.appDrawerOpenProgress * 2, // blur faster during swipe - folio.HomeScreenState.searchWidgetOpenProgress * 1.5, // blur faster during swipe - folio.HomeScreenState.folderOpenProgress - ) + property MobileShell.MaskManager frontMaskManager: MobileShell.MaskManager { + height: root.height + width: root.width + } + + // wallpaper blur layer + MobileShell.BlurEffect { + id: wallpaperBlur + active: folio.FolioSettings.wallpaperBlurEffect > 0 + anchors.fill: parent + sourceLayer: Plasmoid.wallpaperGraphicsObject + maskSourceLayer: folio.FolioSettings.wallpaperBlurEffect > 1 ? maskManager.maskLayer : null + + fullBlur: Math.min(1, + Math.max( + 1 - homeScreen.contentOpacity, + folio.HomeScreenState.appDrawerOpenProgress * 2, // blur faster during swipe + folio.HomeScreenState.searchWidgetOpenProgress * 1.5, // blur faster during swipe + folio.HomeScreenState.folderOpenProgress + ) ) } @@ -144,12 +158,94 @@ ContainmentItem { HomeScreen { id: folioHomeScreen folio: root.folio + maskManager: root.maskManager anchors.fill: parent topMargin: homeScreen.topMargin bottomMargin: homeScreen.bottomMargin leftMargin: homeScreen.leftMargin rightMargin: homeScreen.rightMargin + + onWallpaperSelectorTriggered: wallpaperSelectorLoader.active = true + } + } + } + + // top blur layer for items on top of the base homescreen + MobileShell.BlurEffect { + id: homescreenBlur + anchors.fill: parent + active: folio.FolioSettings.wallpaperBlurEffect > 1 && ((delegateDragItem.visible && folio.HomeScreenState.dragState.dropDelegate.type === Folio.FolioDelegate.Folder) || wallpaperSelectorLoader.active) + visible: active + fullBlur: 0 + + sourceLayer: homeScreenLayer + maskSourceLayer: frontMaskManager.maskLayer + + // stacking both wallpaper and homescreen layers so we can blur them in one pass + Item { + id: homeScreenLayer + anchors.fill: parent + opacity: 0 + + // wallpaper blur + ShaderEffectSource { + anchors.fill: parent + + textureSize: homescreenBlur.textureSize + sourceItem: Plasmoid.wallpaperGraphicsObject + hideSource: false + } + + // homescreen blur + ShaderEffectSource { + anchors.fill: parent + + textureSize: homescreenBlur.textureSize + sourceItem: homeScreen + hideSource: false + } + } + } + + // drag and drop component + DelegateDragItem { + id: delegateDragItem + folio: root.folio + maskManager: root.frontMaskManager + } + + // drag and drop for widgets + WidgetDragItem { + id: widgetDragItem + folio: root.folio + } + + // loader for wallpaper selector + Loader { + id: wallpaperSelectorLoader + anchors.fill: parent + asynchronous: true + active: false + + onLoaded: { + wallpaperSelectorLoader.item.open(); + } + + sourceComponent: MobileShell.WallpaperSelector { + maskManager: root.frontMaskManager + horizontal: root.width > root.height + edge: horizontal ? Qt.LeftEdge : Qt.BottomEdge + bottomMargin: horizontal ? 0 : folioHomeScreen.bottomMargin + leftMargin: horizontal ? folioHomeScreen.leftMargin : 0 + rightMargin: horizontal ? folioHomeScreen.rightMargin : 0 + onClosed: { + wallpaperSelectorLoader.active = false; + } + + onWallpaperSettingsRequested: { + close(); + folioHomeScreen.openConfigure(); } } } diff --git a/containments/homescreens/folio/package/contents/ui/settings/SettingsComponent.qml b/containments/homescreens/folio/package/contents/ui/settings/SettingsComponent.qml index 059b9653..89b4eb70 100644 --- a/containments/homescreens/folio/package/contents/ui/settings/SettingsComponent.qml +++ b/containments/homescreens/folio/package/contents/ui/settings/SettingsComponent.qml @@ -85,7 +85,7 @@ Item { } onClicked: { - wallpaperSelectorLoader.active = true; + homeScreen.wallpaperSelectorTriggered(); folio.HomeScreenState.closeSettingsView(); } } @@ -247,30 +247,4 @@ Item { homeScreen.openConfigure() } } - - Loader { - id: wallpaperSelectorLoader - asynchronous: true - active: false - - onLoaded: { - wallpaperSelectorLoader.item.open(); - } - - sourceComponent: MobileShell.WallpaperSelector { - horizontal: root.width > root.height - edge: horizontal ? Qt.LeftEdge : Qt.BottomEdge - bottomMargin: root.homeScreen.bottomMargin - leftMargin: root.homeScreen.leftMargin - rightMargin: root.homeScreen.rightMargin - onClosed: { - wallpaperSelectorLoader.active = false; - } - - onWallpaperSettingsRequested: { - close(); - homeScreen.openConfigure(); - } - } - } } diff --git a/containments/homescreens/folio/package/contents/ui/settings/SettingsWindow.qml b/containments/homescreens/folio/package/contents/ui/settings/SettingsWindow.qml index 6bdda6a5..376a1b22 100644 --- a/containments/homescreens/folio/package/contents/ui/settings/SettingsWindow.qml +++ b/containments/homescreens/folio/package/contents/ui/settings/SettingsWindow.qml @@ -116,7 +116,7 @@ Window { FormCard.FormCard { id: iconsCard readonly property bool isVerticalOrientation: folio.HomeScreenState.pageOrientation === Folio.HomeScreenState.RegularPosition || - folio.HomeScreenState.pageOrientation === Folio.HomeScreenState.RotateUpsideDown + folio.HomeScreenState.pageOrientation === Folio.HomeScreenState.RotateUpsideDown readonly property string numOfRowsText: i18n("Number of rows") readonly property string numOfColumnsText: i18n("Number of columns") @@ -269,15 +269,24 @@ Window { } FormCard.FormCard { - FormCard.FormSwitchDelegate { - id: showWallpaperBlur - text: i18nc("@option:check", "Show wallpaper blur effect") - checked: folio.FolioSettings.showWallpaperBlur - onCheckedChanged: { - if (checked != folio.FolioSettings.showWallpaperBlur) { - folio.FolioSettings.showWallpaperBlur = checked; - } + FormCard.FormComboBoxDelegate { + id: wallpaperBlurCombobox + text: i18n("Wallpaper blur effect") + + model: [ + {"name": i18nc("Wallpaper blur effect", "None"), "value": 0}, + {"name": i18nc("Wallpaper blur effect", "Simple"), "value": 1}, + {"name": i18nc("Wallpaper blur effect", "Full"), "value": 2} + ] + + textRole: "name" + valueRole: "value" + + Component.onCompleted: { + currentIndex = indexOfValue(folio.FolioSettings.wallpaperBlurEffect); + dialog.parent = root; } + onCurrentValueChanged: folio.FolioSettings.wallpaperBlurEffect = currentValue } } @@ -323,8 +332,7 @@ Window { onAccepted: { console.log('saving layout to ' + selectedFile); if (selectedFile) { - let status = folio.FolioSettings.saveLayoutToFile(selectedFile); - if (status) { + let status = folio.FolioSettings.saveLayoutToFile(selectedFile); if (status) { exportedSuccessfullyPrompt.open(); } else { exportFailedPrompt.open(); diff --git a/containments/homescreens/halcyon/halcyonsettings.cpp b/containments/homescreens/halcyon/halcyonsettings.cpp index f0608ebc..7f1b7287 100644 --- a/containments/homescreens/halcyon/halcyonsettings.cpp +++ b/containments/homescreens/halcyon/halcyonsettings.cpp @@ -3,26 +3,26 @@ #include "halcyonsettings.h" -const QString CFG_KEY_SHOW_WALLPAPER_BLUR = QStringLiteral("showWallpaperBlur"); +const QString CFG_KEY_SHOW_WALLPAPER_BLUR = QStringLiteral("wallpaperBlurEffect"); const QString CFG_KEY_DOUBLE_TAP_TO_LOCK = QStringLiteral("doubleTapToLock"); HalcyonSettings::HalcyonSettings(QObject *parent, KConfigGroup config) - : QObject{parent} - , m_config{config} +: QObject{parent} +, m_config{config} { load(); } -bool HalcyonSettings::showWallpaperBlur() const +HalcyonSettings::WallpaperBlurEffect HalcyonSettings::wallpaperBlurEffect() const { - return m_showWallpaperBlur; + return m_wallpaperBlurEffect; } -void HalcyonSettings::setShowWallpaperBlur(bool showWallpaperBlur) +void HalcyonSettings::setWallpaperBlurEffect(WallpaperBlurEffect wallpaperBlurEffect) { - if (m_showWallpaperBlur != showWallpaperBlur) { - m_showWallpaperBlur = showWallpaperBlur; - Q_EMIT showWallpaperBlurChanged(); + if (m_wallpaperBlurEffect != wallpaperBlurEffect) { + m_wallpaperBlurEffect = wallpaperBlurEffect; + Q_EMIT wallpaperBlurEffectChanged(); save(); } } @@ -43,7 +43,7 @@ void HalcyonSettings::setDoubleTapToLock(bool doubleTapToLock) void HalcyonSettings::save() { - m_config.writeEntry(CFG_KEY_SHOW_WALLPAPER_BLUR, m_showWallpaperBlur); + m_config.writeEntry(CFG_KEY_SHOW_WALLPAPER_BLUR, (int)m_wallpaperBlurEffect); m_config.writeEntry(CFG_KEY_DOUBLE_TAP_TO_LOCK, m_doubleTapToLock); m_config.sync(); @@ -51,6 +51,9 @@ void HalcyonSettings::save() void HalcyonSettings::load() { - m_showWallpaperBlur = m_config.readEntry(CFG_KEY_SHOW_WALLPAPER_BLUR, false); + m_wallpaperBlurEffect = static_cast(m_config.readEntry(CFG_KEY_SHOW_WALLPAPER_BLUR, (int)Full)); m_doubleTapToLock = m_config.readEntry(CFG_KEY_DOUBLE_TAP_TO_LOCK, true); -} \ No newline at end of file + + Q_EMIT doubleTapToLockChanged(); + Q_EMIT wallpaperBlurEffectChanged(); +} diff --git a/containments/homescreens/halcyon/halcyonsettings.h b/containments/homescreens/halcyon/halcyonsettings.h index 5fa13869..9863c564 100644 --- a/containments/homescreens/halcyon/halcyonsettings.h +++ b/containments/homescreens/halcyon/halcyonsettings.h @@ -10,28 +10,35 @@ class HalcyonSettings : public QObject { Q_OBJECT - Q_PROPERTY(bool showWallpaperBlur READ showWallpaperBlur WRITE setShowWallpaperBlur NOTIFY showWallpaperBlurChanged) + Q_PROPERTY(HalcyonSettings::WallpaperBlurEffect wallpaperBlurEffect READ wallpaperBlurEffect WRITE setWallpaperBlurEffect NOTIFY wallpaperBlurEffectChanged) Q_PROPERTY(bool doubleTapToLock READ doubleTapToLock WRITE setDoubleTapToLock NOTIFY doubleTapToLockChanged) public: HalcyonSettings(QObject *parent = nullptr, KConfigGroup config = {}); - bool showWallpaperBlur() const; - void setShowWallpaperBlur(bool blurWallpaper); + enum WallpaperBlurEffect { + None = 0, + Simple = 1, + Full = 2, + }; + Q_ENUM(WallpaperBlurEffect) + + WallpaperBlurEffect wallpaperBlurEffect() const; + void setWallpaperBlurEffect(WallpaperBlurEffect wallpaperBlurEffect); bool doubleTapToLock() const; void setDoubleTapToLock(bool doubleTapToLock); Q_SIGNALS: - void showWallpaperBlurChanged(); + void wallpaperBlurEffectChanged(); void doubleTapToLockChanged(); private: void save(); void load(); - bool m_showWallpaperBlur{false}; + WallpaperBlurEffect m_wallpaperBlurEffect{Full}; bool m_doubleTapToLock{true}; KConfigGroup m_config; -}; \ No newline at end of file +}; diff --git a/containments/homescreens/halcyon/package/contents/ui/FavoritesAppDelegate.qml b/containments/homescreens/halcyon/package/contents/ui/FavoritesAppDelegate.qml index 2b15683c..c552a3f4 100644 --- a/containments/homescreens/halcyon/package/contents/ui/FavoritesAppDelegate.qml +++ b/containments/homescreens/halcyon/package/contents/ui/FavoritesAppDelegate.qml @@ -20,6 +20,7 @@ import org.kde.kirigami 2.19 as Kirigami Item { id: delegate + property MobileShell.MaskManager maskManager property int visualIndex: 0 property real dragFolderAnimationProgress: 0 @@ -77,7 +78,7 @@ Item { } function launchAppWithAnim(x: int, y: int, source, title: string, storageId: string) { - if (source !== "") { + if (source !== "") { MobileShellState.ShellDBusClient.openAppLaunchAnimationWithPosition( Plasmoid.screen, source, @@ -265,6 +266,12 @@ Item { color: Qt.rgba(255, 255, 255, 0.2) radius: Kirigami.Units.cornerRadius opacity: delegate.dragFolderAnimationProgress + + Component.onCompleted: { + if (maskManager) { + maskManager.assignToMask(this) + } + } } Kirigami.Icon { @@ -319,6 +326,12 @@ Item { xScale: 1 + delegate.dragFolderAnimationProgress * 0.5 yScale: 1 + delegate.dragFolderAnimationProgress * 0.5 } + + Component.onCompleted: { + if (maskManager) { + maskManager.assignToMask(this) + } + } } Grid { diff --git a/containments/homescreens/halcyon/package/contents/ui/FavoritesGrid.qml b/containments/homescreens/halcyon/package/contents/ui/FavoritesGrid.qml index 2da8113b..e49acabd 100644 --- a/containments/homescreens/halcyon/package/contents/ui/FavoritesGrid.qml +++ b/containments/homescreens/halcyon/package/contents/ui/FavoritesGrid.qml @@ -15,6 +15,7 @@ import org.kde.private.mobile.homescreen.halcyon as Halcyon MobileShell.GridView { id: root + property MobileShell.MaskManager maskManager required property var searchWidget // don't set anchors.margins since we want everywhere to be draggable @@ -207,6 +208,7 @@ MobileShell.GridView { // actual visual delegate FavoritesAppDelegate { id: appDelegate + maskManager: root.maskManager visualIndex: delegateRoot.visualIndex isFolder: model.isFolder diff --git a/containments/homescreens/halcyon/package/contents/ui/FavoritesView.qml b/containments/homescreens/halcyon/package/contents/ui/FavoritesView.qml index cab175c5..43d46c37 100644 --- a/containments/homescreens/halcyon/package/contents/ui/FavoritesView.qml +++ b/containments/homescreens/halcyon/package/contents/ui/FavoritesView.qml @@ -16,6 +16,7 @@ import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings Item { id: root layer.enabled: true + property MobileShell.MaskManager maskManager required property bool interactive required property var searchWidget @@ -67,6 +68,7 @@ Item { property real openFolderProgress: 0 anchors.fill: parent + maskManager: root.maskManager interactive: root.interactive searchWidget: root.searchWidget diff --git a/containments/homescreens/halcyon/package/contents/ui/HomeScreen.qml b/containments/homescreens/halcyon/package/contents/ui/HomeScreen.qml index 0d400393..173baffb 100644 --- a/containments/homescreens/halcyon/package/contents/ui/HomeScreen.qml +++ b/containments/homescreens/halcyon/package/contents/ui/HomeScreen.qml @@ -19,6 +19,7 @@ import "settings" as Settings Item { id: root + property MobileShell.MaskManager maskManager required property real topMargin required property real bottomMargin @@ -33,6 +34,8 @@ Item { property bool settingsOpen: false property real settingsOpenFactor: settingsOpen ? 1 : 0 + signal wallpaperSelectorTriggered() + Behavior on settingsOpenFactor { NumberAnimation { duration: 200 } } @@ -121,6 +124,7 @@ Item { FavoritesView { id: favoritesView anchors.fill: parent + maskManager: root.maskManager searchWidget: root.searchWidget interactive: root.interactive && swipeView.contentItem.contentX === 0 onOpenConfigureRequested: root.openConfigure() diff --git a/containments/homescreens/halcyon/package/contents/ui/main.qml b/containments/homescreens/halcyon/package/contents/ui/main.qml index adcb2f88..5dddb3d8 100644 --- a/containments/homescreens/halcyon/package/contents/ui/main.qml +++ b/containments/homescreens/halcyon/package/contents/ui/main.qml @@ -56,26 +56,42 @@ ContainmentItem { screenGeometry: Plasmoid.containment.screenGeometry } - MobileShell.HomeScreenWallpaperBlur { - id: wallpaperBlur - active: Plasmoid.settings.showWallpaperBlur - anchors.fill: parent - wallpaperItem: Plasmoid.wallpaperGraphicsObject + property MobileShell.MaskManager maskManager: MobileShell.MaskManager { + height: root.height + width: root.width + } - blurOpacity: Math.min(1, - Math.max(1 - homeScreen.contentOpacity, - halcyonHomeScreen.settingsOpenFactor - ) + property MobileShell.MaskManager frontMaskManager: MobileShell.MaskManager { + height: root.height + width: root.width + } + + // wallpaper blur layer + MobileShell.BlurEffect { + id: wallpaperBlur + active: Plasmoid.settings.wallpaperBlurEffect > 0 + anchors.fill: parent + sourceLayer: Plasmoid.wallpaperGraphicsObject + maskSourceLayer: Plasmoid.settings.wallpaperBlurEffect > 1 ? maskManager.maskLayer : null + + fullBlur: Math.min(1, + Math.max(1 - homeScreen.contentOpacity, + halcyonHomeScreen.settingsOpenFactor, + root.darkenBackgroundFactor, + search.openFactor + ) ) } + property real darkenBackgroundFactor: halcyonHomeScreen.page == 1 ? 1 : 0 + Behavior on darkenBackgroundFactor { + NumberAnimation { duration: Kirigami.Units.longDuration } + } + Rectangle { id: darkenBackground - color: (halcyonHomeScreen.page == 1 ? Qt.rgba(0, 0, 0, 0.7) : Qt.rgba(0, 0, 0, 0.2)) + color: Qt.rgba(0, 0, 0, 0.2 + (0.5 * root.darkenBackgroundFactor)) anchors.fill: parent - Behavior on color { - ColorAnimation { duration: Kirigami.Units.longDuration } - } } Rectangle { @@ -106,6 +122,7 @@ ContainmentItem { HomeScreen { id: halcyonHomeScreen anchors.fill: parent + maskManager: root.maskManager topMargin: homeScreen.topMargin bottomMargin: homeScreen.bottomMargin @@ -114,6 +131,8 @@ ContainmentItem { searchWidget: search interactive: true + + onWallpaperSelectorTriggered: wallpaperSelectorLoader.active = true } // search component @@ -131,6 +150,72 @@ ContainmentItem { } } } + + // top blur layer for items on top of the base homescreen + MobileShell.BlurEffect { + id: homescreenBlur + anchors.fill: parent + active: Plasmoid.settings.wallpaperBlurEffect > 1 && wallpaperSelectorLoader.active + visible: active + fullBlur: 0 + + sourceLayer: homeScreenLayer + maskSourceLayer: frontMaskManager.maskLayer + + // stacking both wallpaper and homescreen layers so we can blur them in one pass + Item { + id: homeScreenLayer + anchors.fill: parent + opacity: 0 + + // wallpaper blur + ShaderEffectSource { + anchors.fill: parent + + textureSize: homescreenBlur.textureSize + sourceItem: Plasmoid.wallpaperGraphicsObject + hideSource: false + } + + // homescreen blur + ShaderEffectSource { + anchors.fill: parent + + textureSize: homescreenBlur.textureSize + sourceItem: homeScreen + hideSource: false + } + } + } + + // loader for wallpaper selector + Loader { + id: wallpaperSelectorLoader + anchors.fill: parent + asynchronous: true + active: false + + onLoaded: { + wallpaperSelectorLoader.item.open(); + } + + sourceComponent: MobileShell.WallpaperSelector { + maskManager: root.frontMaskManager + horizontal: root.width > root.height + edge: horizontal ? Qt.LeftEdge : Qt.BottomEdge + bottomMargin: horizontal ? 0 : halcyonHomeScreen.bottomMargin + leftMargin: horizontal ? halcyonHomeScreen.leftMargin : 0 + rightMargin: horizontal ? halcyonHomeScreen.rightMargin : 0 + onClosed: { + wallpaperSelectorLoader.active = false; + } + + onWallpaperSettingsRequested: { + close(); + halcyonHomeScreen.openContainmentSettings(); + } + } + } } diff --git a/containments/homescreens/halcyon/package/contents/ui/settings/SettingsScreen.qml b/containments/homescreens/halcyon/package/contents/ui/settings/SettingsScreen.qml index 8b284062..8cf5142c 100644 --- a/containments/homescreens/halcyon/package/contents/ui/settings/SettingsScreen.qml +++ b/containments/homescreens/halcyon/package/contents/ui/settings/SettingsScreen.qml @@ -75,7 +75,7 @@ Item { onClicked: { root.homeScreen.settingsOpen = false; - wallpaperSelectorLoader.active = true; + root.homeScreen.wallpaperSelectorTriggered(); } } @@ -135,30 +135,4 @@ Item { } } } - - // Only load wallpaper selector when visible - Loader { - id: wallpaperSelectorLoader - asynchronous: true - active: false - - onLoaded: { - wallpaperSelectorLoader.item.open(); - } - - sourceComponent: MobileShell.WallpaperSelector { - horizontal: root.width > root.height - edge: horizontal ? Qt.LeftEdge : Qt.BottomEdge - bottomMargin: root.bottomMargin - leftMargin: root.leftMargin - rightMargin: root.rightMargin - onClosed: { - wallpaperSelectorLoader.active = false; - } - onWallpaperSettingsRequested: { - close(); - root.homeScreen.openContainmentSettings(); - } - } - } } diff --git a/containments/homescreens/halcyon/package/contents/ui/settings/SettingsWindow.qml b/containments/homescreens/halcyon/package/contents/ui/settings/SettingsWindow.qml index 987b8c46..28b35bf4 100644 --- a/containments/homescreens/halcyon/package/contents/ui/settings/SettingsWindow.qml +++ b/containments/homescreens/halcyon/package/contents/ui/settings/SettingsWindow.qml @@ -87,18 +87,27 @@ Window { } FormCard.FormCard { - FormCard.FormSwitchDelegate { - id: showWallpaperBlur - text: i18nc("@option:check", "Show wallpaper blur effect") - checked: Plasmoid.settings.showWallpaperBlur - onCheckedChanged: { - if (checked != Plasmoid.settings.showWallpaperBlur) { - Plasmoid.settings.showWallpaperBlur = checked; - } + FormCard.FormComboBoxDelegate { + id: wallpaperBlurCombobox + text: i18n("Wallpaper blur effect") + + model: [ + {"name": i18nc("Wallpaper blur effect", "None"), "value": 0}, + {"name": i18nc("Wallpaper blur effect", "Simple"), "value": 1}, + {"name": i18nc("Wallpaper blur effect", "Full"), "value": 2} + ] + + textRole: "name" + valueRole: "value" + + Component.onCompleted: { + currentIndex = indexOfValue(Plasmoid.settings.wallpaperBlurEffect); + dialog.parent = root; } + onCurrentValueChanged: Plasmoid.settings.wallpaperBlurEffect = currentValue } - FormCard.FormDelegateSeparator { above: showWallpaperBlur; below: doubleTapToSleepSwitch } + FormCard.FormDelegateSeparator { above: wallpaperBlurCombobox; below: doubleTapToSleepSwitch } FormCard.FormSwitchDelegate { id: doubleTapToSleepSwitch