Folio/Halcyon: Expand Background Blur Effect using a MaskLayer

This merge request expands upon the folio and halcyon background blur effects, making the folio background blur include the backgrounds of folder icons, the favorites bar, and wallpaper selector, and for halcyon, it now includes the folder icons, app library, search, and wallpaper selector. To accomplish this, a mask layer plugin was created to easily attach to these elements. This way, we can use a `OpacityMask` to cut out from the existing blur layer, thus hopefully keeping the performance cost low. And with my limited testing, it does at least seems to run about the same on my oneplus 6t, though it is not really a low end device, so I can not fairly judge the impact for something slower (eg. PinePhone). To be on the safe side, a third option was also added to the folio settings, allowing for the ability to toggle back to the old functionality if needed.

![Screenshot_20250613_135521](/uploads/d5aa81d6589b61fbba675e4a6e621b55/Screenshot_20250613_135521.png)
![Screenshot_20250613_135536](/uploads/bd726459a131f736e2711ced3fe90d4f/Screenshot_20250613_135536.png)
![Screenshot_20250613_135505](/uploads/c603627b4e65d4b956a1e0b6463d28f3/Screenshot_20250613_135505.png)
![Screenshot_20250627_093729](/uploads/e5f1ad672361c2b9bae23e57905336eb/Screenshot_20250627_093729.png)
This commit is contained in:
Micah Stanley 2025-06-27 18:27:30 +00:00 committed by Devin Lin
parent d4f1c78d61
commit d4eaf693c6
33 changed files with 890 additions and 240 deletions

View file

@ -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

View file

@ -0,0 +1,268 @@
// SPDX-FileCopyrightText: 2025 Micah Stanley <stanleymicah@proton.me>
// SPDX-License-Identifier: GPL-2.0-or-later
#include "masklayer.h"
#include <QSGFlatColorMaterial>
// 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<QQuickItem>& 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<QSGTransformNode*>(rootNode->childAtIndex(currentChildIndex));
geometryNode = static_cast<QSGGeometryNode*>(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<QSGFlatColorMaterial*>(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;
}

View file

@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: 2025 Micah Stanley <stanleymicah@proton.me>
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QQuickItem>
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<QPointer<QQuickItem>> m_sourceItems;
QHash<QQuickItem*, QVector<QMetaObject::Connection>> m_connections;
};

View file

@ -0,0 +1,43 @@
// SPDX-FileCopyrightText: 2025 Micah Stanley <stanleymicah@proton.me>
// 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);
}

View file

@ -0,0 +1,29 @@
// SPDX-FileCopyrightText: 2025 Micah Stanley <stanleymicah@proton.me>
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QQuickItem>
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;
};

View file

@ -0,0 +1,90 @@
// SPDX-FileCopyrightText: 2023-2025 Devin Lin <devin@kde.org>
// SPDX-FileCopyrightText: 2025 Micah Stanley <stanleymicah@proton.me>
// 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
}
}
}

View file

@ -1,40 +0,0 @@
// SPDX-FileCopyrightText: 2023-2025 Devin Lin <devin@kde.org>
// 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
}
}
}

View file

@ -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)
}
}
}
}
}

View file

@ -18,7 +18,7 @@ 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)
@ -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<PageTransitionEffect>(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<WallpaperBlurEffect>(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();
}

View file

@ -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};
};

View file

@ -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
@ -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

View file

@ -17,6 +17,7 @@ import "./delegate"
MouseArea {
id: root
property Folio.HomeScreen folio
property MobileShell.MaskManager maskManager
property var homeScreen
@ -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,6 +186,7 @@ MouseArea {
AppFolderDelegate {
id: appFolderDelegate
folio: root.folio
maskManager: root.maskManager
shadow: true
folder: delegate.delegateModel.folder
name: folio.FolioSettings.showFavouritesAppLabels ? delegate.delegateModel.folder.name : ""

View file

@ -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) {
@ -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

View file

@ -18,6 +18,7 @@ import "./private"
Item {
id: root
property Folio.HomeScreen folio
property MobileShell.MaskManager maskManager
property int pageNum
@ -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

View file

@ -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

View file

@ -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 {

View file

@ -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

View file

@ -19,6 +19,7 @@ AbstractDelegate {
contentItem: DelegateFolderIcon {
folio: root.folio
maskManager: root.maskManager
folder: root.folder
expandBackground: root.appHoveredOver
}

View file

@ -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 } }

View file

@ -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
}
}

View file

@ -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,13 +33,25 @@ 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,
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
@ -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();
}
}
}

View file

@ -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();
}
}
}
}

View file

@ -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();

View file

@ -3,7 +3,7 @@
#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)
@ -13,16 +13,16 @@ HalcyonSettings::HalcyonSettings(QObject *parent, KConfigGroup 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<WallpaperBlurEffect>(m_config.readEntry(CFG_KEY_SHOW_WALLPAPER_BLUR, (int)Full));
m_doubleTapToLock = m_config.readEntry(CFG_KEY_DOUBLE_TAP_TO_LOCK, true);
Q_EMIT doubleTapToLockChanged();
Q_EMIT wallpaperBlurEffectChanged();
}

View file

@ -10,27 +10,34 @@
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;

View file

@ -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
@ -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 {

View file

@ -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

View file

@ -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

View file

@ -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()

View file

@ -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,
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
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();
}
}
}
}

View file

@ -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();
}
}
}
}

View file

@ -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