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/notificationthumbnailer.cpp
notifications/notificationfilemenu.cpp notifications/notificationfilemenu.cpp
notifications/notificationfileinfo.cpp notifications/notificationfileinfo.cpp
masklayer/masklayer.cpp
masklayer/maskmanager.cpp
) )
target_include_directories(mobileshellplugin PRIVATE components) target_include_directories(mobileshellplugin PRIVATE components)
target_include_directories(mobileshellplugin PRIVATE notifications) target_include_directories(mobileshellplugin PRIVATE notifications)
target_include_directories(mobileshellplugin PRIVATE masklayer)
target_sources(mobileshellplugin PRIVATE ${mobileshellplugin_SRCS}) target_sources(mobileshellplugin PRIVATE ${mobileshellplugin_SRCS})
# Singleton declarations # 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

@ -105,7 +105,7 @@ Item {
} }
} }
//END API implementation //END API implementation
Component.onCompleted: { Component.onCompleted: {
// determine the margins used // determine the margins used

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.plasma.wallpapers.image 2.0 as Wallpaper
import org.kde.kquickcontrolsaddons 2.0 as Addons import org.kde.kquickcontrolsaddons 2.0 as Addons
import org.kde.plasma.private.mobileshell.wallpaperimageplugin as WallpaperImagePlugin import org.kde.plasma.private.mobileshell.wallpaperimageplugin as WallpaperImagePlugin
import org.kde.plasma.private.mobileshell as MobileShell
Controls.Drawer { Controls.Drawer {
id: imageWallpaperDrawer id: imageWallpaperDrawer
dragMargin: 0 dragMargin: 0
property MobileShell.MaskManager maskManager
required property bool horizontal required property bool horizontal
signal wallpaperSettingsRequested() signal wallpaperSettingsRequested()
@ -51,8 +54,8 @@ Controls.Drawer {
header: Controls.ItemDelegate { header: Controls.ItemDelegate {
id: openSettings id: openSettings
width: imageWallpaperDrawer.horizontal ? parent.width : height * (imageWallpaperDrawer.width / imageWallpaperDrawer.Screen.height) width: imageWallpaperDrawer.horizontal ? wallpapersView.width : height * (imageWallpaperDrawer.width / imageWallpaperDrawer.Screen.height)
height: imageWallpaperDrawer.horizontal ? width / (imageWallpaperDrawer.Screen.width / imageWallpaperDrawer.Screen.height) : parent.height height: imageWallpaperDrawer.horizontal ? width / (imageWallpaperDrawer.Screen.width / imageWallpaperDrawer.Screen.height) : wallpapersView.height
padding: Kirigami.Units.gridUnit / 2 padding: Kirigami.Units.gridUnit / 2
leftPadding: padding leftPadding: padding
topPadding: padding topPadding: padding
@ -62,6 +65,12 @@ Controls.Drawer {
background: Rectangle { background: Rectangle {
radius: Kirigami.Units.cornerRadius radius: Kirigami.Units.cornerRadius
color: Qt.rgba(255, 255, 255, (openSettings.down || openSettings.highlighted) ? 0.3 : 0.2) color: Qt.rgba(255, 255, 255, (openSettings.down || openSettings.highlighted) ? 0.3 : 0.2)
Component.onCompleted: {
if (maskManager) {
maskManager.assignToMask(this)
}
}
} }
contentItem: Item { contentItem: Item {
@ -81,11 +90,11 @@ Controls.Drawer {
delegate: Controls.ItemDelegate { delegate: Controls.ItemDelegate {
id: delegate id: delegate
width: imageWallpaperDrawer.horizontal ? parent.width : height * (imageWallpaperDrawer.width / imageWallpaperDrawer.Screen.height) width: imageWallpaperDrawer.horizontal ? wallpapersView.width : height * (imageWallpaperDrawer.width / imageWallpaperDrawer.Screen.height)
height: imageWallpaperDrawer.horizontal ? width / (imageWallpaperDrawer.Screen.width / imageWallpaperDrawer.Screen.height) : (parent ? parent.height : 0) height: imageWallpaperDrawer.horizontal ? width / (imageWallpaperDrawer.Screen.width / imageWallpaperDrawer.Screen.height) : (wallpapersView ? wallpapersView.height : 0)
padding: Kirigami.Units.largeSpacing - (ListView.isCurrentItem ? Kirigami.Units.smallSpacing : 0) padding: Kirigami.Units.largeSpacing - (wallpapersView.currentIndex === index ? Kirigami.Units.smallSpacing : 0)
property real inset: ListView.isCurrentItem ? 0 : Kirigami.Units.smallSpacing property real scaleAmount: wallpapersView.currentIndex === index ? 0 : Kirigami.Units.smallSpacing
Behavior on inset { Behavior on scaleAmount {
NumberAnimation { NumberAnimation {
duration: Kirigami.Units.longDuration duration: Kirigami.Units.longDuration
easing.type: Easing.InOutQuad easing.type: Easing.InOutQuad
@ -102,10 +111,10 @@ Controls.Drawer {
topPadding: padding topPadding: padding
rightPadding: padding rightPadding: padding
bottomPadding: padding bottomPadding: padding
topInset: inset topInset: scaleAmount
bottomInset: inset bottomInset: scaleAmount
leftInset: inset leftInset: scaleAmount
rightInset: inset rightInset: scaleAmount
property bool isCurrent: WallpaperImagePlugin.WallpaperPlugin.homescreenWallpaperPath == model.path property bool isCurrent: WallpaperImagePlugin.WallpaperPlugin.homescreenWallpaperPath == model.path
onIsCurrentChanged: { onIsCurrentChanged: {
@ -143,6 +152,12 @@ Controls.Drawer {
background: Rectangle { background: Rectangle {
color: Qt.rgba(255, 255, 255, (delegate.down || delegate.highlighted) ? 0.4 : 0.2) color: Qt.rgba(255, 255, 255, (delegate.down || delegate.highlighted) ? 0.4 : 0.2)
radius: Kirigami.Units.cornerRadius radius: Kirigami.Units.cornerRadius
Component.onCompleted: {
if (maskManager) {
maskManager.assignToMask(this)
}
}
} }
} }
} }

View file

@ -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_DELEGATE_ICON_SIZE = QStringLiteral("delegateIconSize");
const QString CFG_KEY_SHOW_FAVORITES_BAR_BACKGROUND = QStringLiteral("showFavoritesBarBackground"); const QString CFG_KEY_SHOW_FAVORITES_BAR_BACKGROUND = QStringLiteral("showFavoritesBarBackground");
const QString CFG_KEY_PAGE_TRANSITION_EFFECT = QStringLiteral("pageTransitionEffect"); 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"); const QString CFG_KEY_DOUBLE_TAP_TO_LOCK = QStringLiteral("doubleTapToLock");
FolioSettings::FolioSettings(HomeScreen *parent) FolioSettings::FolioSettings(HomeScreen *parent)
: QObject{parent} : QObject{parent}
, m_homeScreen{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) { if (m_wallpaperBlurEffect != wallpaperBlurEffect) {
m_showWallpaperBlur = showWallpaperBlur; m_wallpaperBlurEffect = wallpaperBlurEffect;
Q_EMIT showWallpaperBlurChanged(); Q_EMIT wallpaperBlurEffectChanged();
save(); 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_DELEGATE_ICON_SIZE, m_delegateIconSize);
m_homeScreen->config().writeEntry(CFG_KEY_SHOW_FAVORITES_BAR_BACKGROUND, m_showFavouritesBarBackground); 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_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); m_homeScreen->config().writeEntry(CFG_KEY_DOUBLE_TAP_TO_LOCK, m_doubleTapToLock);
Q_EMIT m_homeScreen->configNeedsSaving(); 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_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_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_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); m_doubleTapToLock = m_homeScreen->config().readEntry(CFG_KEY_DOUBLE_TAP_TO_LOCK, true);
Q_EMIT homeScreenRowsChanged(); Q_EMIT homeScreenRowsChanged();
@ -211,7 +211,7 @@ void FolioSettings::load()
Q_EMIT showFavouritesAppLabelsChanged(); Q_EMIT showFavouritesAppLabelsChanged();
Q_EMIT lockLayoutChanged(); Q_EMIT lockLayoutChanged();
Q_EMIT delegateIconSizeChanged(); Q_EMIT delegateIconSizeChanged();
Q_EMIT showWallpaperBlurChanged(); Q_EMIT wallpaperBlurEffectChanged();
Q_EMIT doubleTapToLockChanged(); 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(bool showFavouritesBarBackground READ showFavouritesBarBackground WRITE setShowFavouritesBarBackground NOTIFY showFavouritesBarBackgroundChanged)
Q_PROPERTY( Q_PROPERTY(
FolioSettings::PageTransitionEffect pageTransitionEffect READ pageTransitionEffect WRITE setPageTransitionEffect NOTIFY pageTransitionEffectChanged) 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) Q_PROPERTY(bool doubleTapToLock READ doubleTapToLock WRITE setDoubleTapToLock NOTIFY doubleTapToLockChanged)
public: public:
@ -39,6 +39,13 @@ public:
}; };
Q_ENUM(PageTransitionEffect) 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 // 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 // NOTE: use HomeScreenState.pageRows() instead in UI logic since we may have the rows and
// columns swapped (in landscape layouts) // columns swapped (in landscape layouts)
@ -66,8 +73,8 @@ public:
PageTransitionEffect pageTransitionEffect() const; PageTransitionEffect pageTransitionEffect() const;
void setPageTransitionEffect(PageTransitionEffect pageTransitionEffect); void setPageTransitionEffect(PageTransitionEffect pageTransitionEffect);
bool showWallpaperBlur() const; WallpaperBlurEffect wallpaperBlurEffect() const;
void setShowWallpaperBlur(bool showWallpaperBlur); void setWallpaperBlurEffect(WallpaperBlurEffect wallpaperBlurEffect);
bool doubleTapToLock() const; bool doubleTapToLock() const;
void setDoubleTapToLock(bool doubleTapToLock); void setDoubleTapToLock(bool doubleTapToLock);
@ -86,7 +93,7 @@ Q_SIGNALS:
void delegateIconSizeChanged(); void delegateIconSizeChanged();
void showFavouritesBarBackgroundChanged(); void showFavouritesBarBackgroundChanged();
void pageTransitionEffectChanged(); void pageTransitionEffectChanged();
void showWallpaperBlurChanged(); void wallpaperBlurEffectChanged();
void doubleTapToLockChanged(); void doubleTapToLockChanged();
private: private:
@ -102,6 +109,6 @@ private:
qreal m_delegateIconSize{48}; qreal m_delegateIconSize{48};
bool m_showFavouritesBarBackground{false}; bool m_showFavouritesBarBackground{false};
PageTransitionEffect m_pageTransitionEffect{SlideTransition}; PageTransitionEffect m_pageTransitionEffect{SlideTransition};
bool m_showWallpaperBlur{false}; WallpaperBlurEffect m_wallpaperBlurEffect{Full};
bool m_doubleTapToLock{false}; bool m_doubleTapToLock{false};
}; };

View file

@ -6,12 +6,14 @@ import QtQuick.Layouts
import org.kde.kirigami 2.20 as Kirigami import org.kde.kirigami 2.20 as Kirigami
import org.kde.private.mobile.homescreen.folio 1.0 as Folio import org.kde.private.mobile.homescreen.folio 1.0 as Folio
import org.kde.plasma.private.mobileshell as MobileShell
import "./delegate" import "./delegate"
Item { Item {
id: root id: root
property Folio.HomeScreen folio property Folio.HomeScreen folio
property MobileShell.MaskManager maskManager
property Folio.FolioDelegate delegate property Folio.FolioDelegate delegate
width: folio.HomeScreenState.pageCellWidth width: folio.HomeScreenState.pageCellWidth
@ -34,7 +36,7 @@ Item {
} }
// animate drop x // animate drop x
XAnimator on x { NumberAnimation on x {
id: dragXAnim id: dragXAnim
running: false running: false
duration: Kirigami.Units.longDuration duration: Kirigami.Units.longDuration
@ -46,7 +48,7 @@ Item {
} }
// animate drop y // animate drop y
YAnimator on y { NumberAnimation on y {
id: dragYAnim id: dragYAnim
running: false running: false
duration: Kirigami.Units.longDuration duration: Kirigami.Units.longDuration
@ -140,7 +142,7 @@ Item {
// scale animation if we are creating, or inserting into a folder // scale animation if we are creating, or inserting into a folder
scaleAnim.restart(); scaleAnim.restart();
} }
} }
// if the drop has been abandoned, just hide // if the drop has been abandoned, just hide
@ -158,6 +160,7 @@ Item {
DelegateIconLoader { DelegateIconLoader {
id: loader id: loader
folio: root.folio folio: root.folio
maskManager: root.maskManager
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
Layout.minimumWidth: folio.FolioSettings.delegateIconSize Layout.minimumWidth: folio.FolioSettings.delegateIconSize
Layout.minimumHeight: folio.FolioSettings.delegateIconSize Layout.minimumHeight: folio.FolioSettings.delegateIconSize

View file

@ -17,6 +17,7 @@ import "./delegate"
MouseArea { MouseArea {
id: root id: root
property Folio.HomeScreen folio property Folio.HomeScreen folio
property MobileShell.MaskManager maskManager
property var homeScreen property var homeScreen
@ -53,11 +54,11 @@ MouseArea {
readonly property var dragState: folio.HomeScreenState.dragState readonly property var dragState: folio.HomeScreenState.dragState
readonly property bool isDropPositionThis: dragState.candidateDropPosition.location === Folio.DelegateDragPosition.Favourites && 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 && readonly property bool isAppHoveredOver: folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate &&
dragState.dropDelegate && dragState.dropDelegate &&
dragState.dropDelegate.type === Folio.FolioDelegate.Application && dragState.dropDelegate.type === Folio.FolioDelegate.Application &&
isDropPositionThis isDropPositionThis
readonly property bool isLocationBottom: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Bottom readonly property bool isLocationBottom: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Bottom
@ -111,6 +112,7 @@ MouseArea {
AppDelegate { AppDelegate {
id: appDelegate id: appDelegate
folio: root.folio folio: root.folio
maskManager: root.maskManager
application: delegate.delegateModel.application application: delegate.delegateModel.application
name: folio.FolioSettings.showFavouritesAppLabels ? delegate.delegateModel.application.name : "" name: folio.FolioSettings.showFavouritesAppLabels ? delegate.delegateModel.application.name : ""
shadow: true shadow: true
@ -184,14 +186,15 @@ MouseArea {
AppFolderDelegate { AppFolderDelegate {
id: appFolderDelegate id: appFolderDelegate
folio: root.folio folio: root.folio
maskManager: root.maskManager
shadow: true shadow: true
folder: delegate.delegateModel.folder folder: delegate.delegateModel.folder
name: folio.FolioSettings.showFavouritesAppLabels ? delegate.delegateModel.folder.name : "" 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 // do not show if the drop animation is running to this delegate, and the drop delegate is a folder
visible: !(root.homeScreen.dropAnimationRunning && visible: !(root.homeScreen.dropAnimationRunning &&
delegate.isDropPositionThis && delegate.isDropPositionThis &&
delegate.dragState.dropDelegate.type === Folio.FolioDelegate.Folder) delegate.dragState.dropDelegate.type === Folio.FolioDelegate.Folder)
appHoveredOver: delegate.isAppHoveredOver appHoveredOver: delegate.isAppHoveredOver

View file

@ -20,6 +20,7 @@ import "./settings"
Item { Item {
id: root id: root
property Folio.HomeScreen folio property Folio.HomeScreen folio
property MobileShell.MaskManager maskManager
property Folio.HomeScreenState homeScreenState: folio.HomeScreenState property Folio.HomeScreenState homeScreenState: folio.HomeScreenState
property real topMargin: 0 property real topMargin: 0
@ -43,6 +44,8 @@ Item {
onLeftMarginChanged: folio.HomeScreenState.viewLeftPadding = root.leftMargin onLeftMarginChanged: folio.HomeScreenState.viewLeftPadding = root.leftMargin
onRightMarginChanged: folio.HomeScreenState.viewRightPadding = root.rightMargin onRightMarginChanged: folio.HomeScreenState.viewRightPadding = root.rightMargin
signal wallpaperSelectorTriggered()
// called by any delegates when starting drag // called by any delegates when starting drag
// returns the mapped coordinates to be used in the home screen state // returns the mapped coordinates to be used in the home screen state
function prepareStartDelegateDrag(delegate, item, skipSwipeThreshold) { function prepareStartDelegateDrag(delegate, item, skipSwipeThreshold) {
@ -107,10 +110,10 @@ Item {
interactive: root.interactive && interactive: root.interactive &&
settings.homeScreenInteractive && settings.homeScreenInteractive &&
(appDrawer.flickable.contentY <= 10 || // disable the swipe area when we are swiping in the app drawer, and not in drag-and-drop (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.AwaitingDraggingDelegate ||
folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate || folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate ||
folio.HomeScreenState.swipeState === Folio.HomeScreenState.SwipingAppDrawerGrid || folio.HomeScreenState.swipeState === Folio.HomeScreenState.SwipingAppDrawerGrid ||
folio.HomeScreenState.viewState !== Folio.HomeScreenState.AppDrawerView) folio.HomeScreenState.viewState !== Folio.HomeScreenState.AppDrawerView)
onSwipeStarted: (currentPos, startPos) => { onSwipeStarted: (currentPos, startPos) => {
const deltaX = currentPos.x - startPos.x; const deltaX = currentPos.x - startPos.x;
@ -182,6 +185,7 @@ Item {
HomeScreenPages { HomeScreenPages {
id: homeScreenPages id: homeScreenPages
folio: root.folio folio: root.folio
maskManager: root.maskManager
homeScreen: root homeScreen: root
anchors.topMargin: root.topMargin anchors.topMargin: root.topMargin
@ -247,6 +251,8 @@ Item {
id: favouritesBarScrim id: favouritesBarScrim
color: Qt.rgba(255, 255, 255, 0.2) color: Qt.rgba(255, 255, 255, 0.2)
Component.onCompleted: maskManager.assignToMask(this)
// don't show in settings mode // don't show in settings mode
opacity: 1 - folio.HomeScreenState.settingsOpenProgress opacity: 1 - folio.HomeScreenState.settingsOpenProgress
visible: folio.FolioSettings.showFavouritesBarBackground visible: folio.FolioSettings.showFavouritesBarBackground
@ -266,6 +272,7 @@ Item {
FavouritesBar { FavouritesBar {
id: favouritesBar id: favouritesBar
folio: root.folio folio: root.folio
maskManager: root.maskManager
homeScreen: root homeScreen: root
// don't show in settings mode // don't show in settings mode
@ -399,18 +406,6 @@ Item {
transform: Translate { y: folderView.opacity > 0 ? 0 : folderView.height } 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 // bottom app drawer
AppDrawer { AppDrawer {
id: appDrawer id: appDrawer

View file

@ -18,6 +18,7 @@ import "./private"
Item { Item {
id: root id: root
property Folio.HomeScreen folio property Folio.HomeScreen folio
property MobileShell.MaskManager maskManager
property int pageNum property int pageNum
@ -50,10 +51,10 @@ Item {
// only show if it is an empty spot on this page // only show if it is an empty spot on this page
visible: folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate && visible: folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate &&
dropPosition.location === Folio.DelegateDragPosition.Pages && dropPosition.location === Folio.DelegateDragPosition.Pages &&
dropPosition.page === root.pageNum && dropPosition.page === root.pageNum &&
!dropDelegateIsWidget && !dropDelegateIsWidget &&
folio.HomeScreenState.getPageDelegateAt(root.pageNum, dropPosition.pageRow, dropPosition.pageColumn) === null folio.HomeScreenState.getPageDelegateAt(root.pageNum, dropPosition.pageRow, dropPosition.pageColumn) === null
x: dropPosition.pageColumn * folio.HomeScreenState.pageCellWidth x: dropPosition.pageColumn * folio.HomeScreenState.pageCellWidth
y: dropPosition.pageRow * folio.HomeScreenState.pageCellHeight y: dropPosition.pageRow * folio.HomeScreenState.pageCellHeight
@ -71,10 +72,10 @@ Item {
// only show if the widget can be placed here // only show if the widget can be placed here
visible: folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate && visible: folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate &&
dropPosition.location === Folio.DelegateDragPosition.Pages && dropPosition.location === Folio.DelegateDragPosition.Pages &&
dropPosition.page === root.pageNum && dropPosition.page === root.pageNum &&
dropDelegateIsWidget && dropDelegateIsWidget &&
pageModel.canAddDelegate(dropPosition.pageRow, dropPosition.pageColumn, dropDelegate) pageModel.canAddDelegate(dropPosition.pageRow, dropPosition.pageColumn, dropDelegate)
radius: Kirigami.Units.cornerRadius radius: Kirigami.Units.cornerRadius
color: Qt.rgba(255, 255, 255, 0.3) color: Qt.rgba(255, 255, 255, 0.3)
@ -100,14 +101,14 @@ Item {
property var dragState: folio.HomeScreenState.dragState property var dragState: folio.HomeScreenState.dragState
property bool isDropPositionThis: dragState.candidateDropPosition.location === Folio.DelegateDragPosition.Pages && property bool isDropPositionThis: dragState.candidateDropPosition.location === Folio.DelegateDragPosition.Pages &&
dragState.candidateDropPosition.page === root.pageNum && dragState.candidateDropPosition.page === root.pageNum &&
dragState.candidateDropPosition.pageRow === delegate.pageDelegate.row && dragState.candidateDropPosition.pageRow === delegate.pageDelegate.row &&
dragState.candidateDropPosition.pageColumn === delegate.pageDelegate.column dragState.candidateDropPosition.pageColumn === delegate.pageDelegate.column
property bool isAppHoveredOver: folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate && property bool isAppHoveredOver: folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate &&
dragState.dropDelegate && dragState.dropDelegate &&
dragState.dropDelegate.type === Folio.FolioDelegate.Application && dragState.dropDelegate.type === Folio.FolioDelegate.Application &&
isDropPositionThis isDropPositionThis
implicitWidth: loader.item ? loader.item.implicitWidth : 0 implicitWidth: loader.item ? loader.item.implicitWidth : 0
implicitHeight: loader.item ? loader.item.implicitHeight : 0 implicitHeight: loader.item ? loader.item.implicitHeight : 0
@ -118,7 +119,7 @@ Item {
y: row * folio.HomeScreenState.pageCellHeight y: row * folio.HomeScreenState.pageCellHeight
visible: row >= 0 && row < folio.HomeScreenState.pageRows && 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 // called when we want to delete this delegate
function removeSelf() { function removeSelf() {
@ -161,6 +162,7 @@ Item {
AppDelegate { AppDelegate {
id: appDelegate id: appDelegate
folio: root.folio folio: root.folio
maskManager: root.maskManager
name: folio.FolioSettings.showPagesAppLabels ? delegate.pageDelegate.application.name : "" name: folio.FolioSettings.showPagesAppLabels ? delegate.pageDelegate.application.name : ""
application: delegate.pageDelegate.application application: delegate.pageDelegate.application
turnToFolder: delegate.isAppHoveredOver turnToFolder: delegate.isAppHoveredOver
@ -238,6 +240,7 @@ Item {
AppFolderDelegate { AppFolderDelegate {
id: appFolderDelegate id: appFolderDelegate
folio: root.folio folio: root.folio
maskManager: root.maskManager
name: folio.FolioSettings.showPagesAppLabels ? delegate.pageDelegate.folder.name : "" name: folio.FolioSettings.showPagesAppLabels ? delegate.pageDelegate.folder.name : ""
folder: delegate.pageDelegate.folder 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 // do not show if the drop animation is running to this delegate, and the drop delegate is a folder
visible: !(root.homeScreen.dropAnimationRunning && visible: !(root.homeScreen.dropAnimationRunning &&
delegate.isDropPositionThis && delegate.isDropPositionThis &&
delegate.dragState.dropDelegate.type === Folio.FolioDelegate.Folder) delegate.dragState.dropDelegate.type === Folio.FolioDelegate.Folder)
// don't show label in drag and drop mode // don't show label in drag and drop mode
labelOpacity: delegate.opacity labelOpacity: delegate.opacity
@ -334,7 +337,7 @@ Item {
// background: there is only one "visual" instance of the widget, once this delegate loads // 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) // 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) property bool suppressAppletReparent: (root.homeScreen.currentlyDraggedWidget === delegate.pageDelegate.widget)
&& delegate.isDropPositionThis && delegate.isDropPositionThis
visible: !suppressAppletReparent visible: !suppressAppletReparent
widget: suppressAppletReparent ? null : delegate.pageDelegate.widget widget: suppressAppletReparent ? null : delegate.pageDelegate.widget

View file

@ -13,6 +13,7 @@ import org.kde.private.mobile.homescreen.folio 1.0 as Folio
MouseArea { MouseArea {
id: root id: root
property Folio.HomeScreen folio property Folio.HomeScreen folio
property MobileShell.MaskManager maskManager
property var homeScreen property var homeScreen
@ -44,6 +45,7 @@ MouseArea {
delegate: HomeScreenPage { delegate: HomeScreenPage {
id: homeScreenPage id: homeScreenPage
folio: root.folio folio: root.folio
maskManager: root.maskManager
pageNum: model.index pageNum: model.index
pageModel: model.delegate pageModel: model.delegate
homeScreen: root.homeScreen homeScreen: root.homeScreen
@ -64,7 +66,7 @@ MouseArea {
switch (folio.FolioSettings.pageTransitionEffect) { switch (folio.FolioSettings.pageTransitionEffect) {
case Folio.FolioSettings.StackTransition: case Folio.FolioSettings.StackTransition:
return (positionX < 0) ? progressToCenter : 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: default:
return progressToCenter; return progressToCenter;
} }
@ -109,8 +111,8 @@ MouseArea {
Rotation { Rotation {
id: cubeTransitionRotation id: cubeTransitionRotation
origin.x: (positionX < 0) ? origin.x: (positionX < 0) ?
(folio.HomeScreenState.pageWidth / 2) * homeScreenPage.progressToCenter : (folio.HomeScreenState.pageWidth / 2) * homeScreenPage.progressToCenter :
(folio.HomeScreenState.pageWidth / 2) + (folio.HomeScreenState.pageWidth / 2) * (1 - homeScreenPage.progressToCenter); (folio.HomeScreenState.pageWidth / 2) + (folio.HomeScreenState.pageWidth / 2) * (1 - homeScreenPage.progressToCenter);
origin.y: folio.HomeScreenState.pageHeight / 2; origin.y: folio.HomeScreenState.pageHeight / 2;
axis { x: 0; y: 1; z: 0 } axis { x: 0; y: 1; z: 0 }
angle: { angle: {
@ -121,8 +123,8 @@ MouseArea {
Rotation { Rotation {
id: rotationTransitionRotation id: rotationTransitionRotation
origin.x: (positionX < 0) ? origin.x: (positionX < 0) ?
(folio.HomeScreenState.pageWidth / 2) * homeScreenPage.progressToCenter : (folio.HomeScreenState.pageWidth / 2) * homeScreenPage.progressToCenter :
(folio.HomeScreenState.pageWidth / 2) + (folio.HomeScreenState.pageWidth / 2) * (1 - homeScreenPage.progressToCenter); (folio.HomeScreenState.pageWidth / 2) + (folio.HomeScreenState.pageWidth / 2) * (1 - homeScreenPage.progressToCenter);
origin.y: 0 origin.y: 0
axis { x: -0.2; y: 0.3; z: 0.5 } axis { x: -0.2; y: 0.3; z: 0.5 }
angle: { angle: {

View file

@ -18,6 +18,7 @@ import org.kde.plasma.private.mobileshell as MobileShell
Folio.DelegateTouchArea { Folio.DelegateTouchArea {
id: root id: root
property Folio.HomeScreen folio property Folio.HomeScreen folio
property MobileShell.MaskManager maskManager
property string name property string name
property bool shadow: false property bool shadow: false
@ -29,10 +30,10 @@ Folio.DelegateTouchArea {
signal afterClickAnimation() signal afterClickAnimation()
// grow/shrink animation // grow/shrink animation
property real zoomScale: 1 property real scaleAmount: 1
property bool clickRequested: false property bool clickRequested: false
NumberAnimation on zoomScale { NumberAnimation on scaleAmount {
id: shrinkAnim id: shrinkAnim
running: false running: false
duration: ShellSettings.Settings.animationsEnabled ? 80 : 1 duration: ShellSettings.Settings.animationsEnabled ? 80 : 1
@ -44,7 +45,7 @@ Folio.DelegateTouchArea {
} }
} }
NumberAnimation on zoomScale { NumberAnimation on scaleAmount {
id: growAnim id: growAnim
running: false running: false
duration: ShellSettings.Settings.animationsEnabled ? 80 : 1 duration: ShellSettings.Settings.animationsEnabled ? 80 : 1
@ -86,8 +87,8 @@ Folio.DelegateTouchArea {
transform: Scale { transform: Scale {
origin.x: root.width / 2; origin.x: root.width / 2;
origin.y: root.height / 2; origin.y: root.height / 2;
xScale: root.zoomScale xScale: root.scaleAmount
yScale: root.zoomScale yScale: root.scaleAmount
} }
MobileShell.BaseItem { MobileShell.BaseItem {

View file

@ -62,6 +62,12 @@ AbstractDelegate {
color: Qt.rgba(255, 255, 255, 0.3) color: Qt.rgba(255, 255, 255, 0.3)
anchors.fill: parent anchors.fill: parent
Component.onCompleted: {
if (maskManager) {
maskManager.assignToMask(this)
}
}
opacity: root.turnToFolder ? 1 : 0 opacity: root.turnToFolder ? 1 : 0
property real scaleAmount: root.turnToFolder ? 1.2 : 1.0 property real scaleAmount: root.turnToFolder ? 1.2 : 1.0

View file

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

View file

@ -9,10 +9,12 @@ import QtQuick.Effects
import org.kde.kirigami 2.20 as Kirigami import org.kde.kirigami 2.20 as Kirigami
import org.kde.private.mobile.homescreen.folio 1.0 as Folio import org.kde.private.mobile.homescreen.folio 1.0 as Folio
import org.kde.plasma.private.mobileshell as MobileShell
Item { Item {
id: root id: root
property Folio.HomeScreen folio property Folio.HomeScreen folio
property MobileShell.MaskManager maskManager
property Folio.FolioApplicationFolder folder property Folio.FolioApplicationFolder folder
@ -27,6 +29,12 @@ Item {
color: Qt.rgba(255, 255, 255, 0.3) color: Qt.rgba(255, 255, 255, 0.3)
anchors.fill: parent anchors.fill: parent
Component.onCompleted: {
if (maskManager) {
maskManager.assignToMask(this)
}
}
property real scaleAmount: root.expandBackground ? 1.2 : 1.0 property real scaleAmount: root.expandBackground ? 1.2 : 1.0
Behavior on scaleAmount { NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad } } 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.kirigami 2.20 as Kirigami
import org.kde.private.mobile.homescreen.folio 1.0 as Folio import org.kde.private.mobile.homescreen.folio 1.0 as Folio
import org.kde.plasma.private.mobileshell as MobileShell
Loader { Loader {
id: root id: root
property Folio.HomeScreen folio property Folio.HomeScreen folio
property MobileShell.MaskManager maskManager
height: folio.FolioSettings.delegateIconSize height: folio.FolioSettings.delegateIconSize
width: folio.FolioSettings.delegateIconSize width: folio.FolioSettings.delegateIconSize
@ -50,6 +52,7 @@ Loader {
DelegateFolderIcon { DelegateFolderIcon {
folio: root.folio folio: root.folio
maskManager: root.maskManager
folder: delegate.folder 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.windowplugin as WindowPlugin
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
import "./private"
ContainmentItem { ContainmentItem {
id: root id: root
property Folio.HomeScreen folio: root.plasmoid property Folio.HomeScreen folio: root.plasmoid
@ -31,19 +33,31 @@ ContainmentItem {
forceActiveFocus(); forceActiveFocus();
} }
MobileShell.HomeScreenWallpaperBlur { property MobileShell.MaskManager maskManager: MobileShell.MaskManager {
id: wallpaperBlur height: root.height
active: folio.FolioSettings.showWallpaperBlur width: root.width
anchors.fill: parent }
wallpaperItem: Plasmoid.wallpaperGraphicsObject
blurOpacity: Math.min(1, property MobileShell.MaskManager frontMaskManager: MobileShell.MaskManager {
Math.max( height: root.height
1 - homeScreen.contentOpacity, width: root.width
folio.HomeScreenState.appDrawerOpenProgress * 2, // blur faster during swipe }
folio.HomeScreenState.searchWidgetOpenProgress * 1.5, // blur faster during swipe
folio.HomeScreenState.folderOpenProgress // 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 { HomeScreen {
id: folioHomeScreen id: folioHomeScreen
folio: root.folio folio: root.folio
maskManager: root.maskManager
anchors.fill: parent anchors.fill: parent
topMargin: homeScreen.topMargin topMargin: homeScreen.topMargin
bottomMargin: homeScreen.bottomMargin bottomMargin: homeScreen.bottomMargin
leftMargin: homeScreen.leftMargin leftMargin: homeScreen.leftMargin
rightMargin: homeScreen.rightMargin 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: { onClicked: {
wallpaperSelectorLoader.active = true; homeScreen.wallpaperSelectorTriggered();
folio.HomeScreenState.closeSettingsView(); folio.HomeScreenState.closeSettingsView();
} }
} }
@ -247,30 +247,4 @@ Item {
homeScreen.openConfigure() 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

@ -116,7 +116,7 @@ Window {
FormCard.FormCard { FormCard.FormCard {
id: iconsCard id: iconsCard
readonly property bool isVerticalOrientation: folio.HomeScreenState.pageOrientation === Folio.HomeScreenState.RegularPosition || 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 numOfRowsText: i18n("Number of rows")
readonly property string numOfColumnsText: i18n("Number of columns") readonly property string numOfColumnsText: i18n("Number of columns")
@ -269,15 +269,24 @@ Window {
} }
FormCard.FormCard { FormCard.FormCard {
FormCard.FormSwitchDelegate { FormCard.FormComboBoxDelegate {
id: showWallpaperBlur id: wallpaperBlurCombobox
text: i18nc("@option:check", "Show wallpaper blur effect") text: i18n("Wallpaper blur effect")
checked: folio.FolioSettings.showWallpaperBlur
onCheckedChanged: { model: [
if (checked != folio.FolioSettings.showWallpaperBlur) { {"name": i18nc("Wallpaper blur effect", "None"), "value": 0},
folio.FolioSettings.showWallpaperBlur = checked; {"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: { onAccepted: {
console.log('saving layout to ' + selectedFile); console.log('saving layout to ' + selectedFile);
if (selectedFile) { if (selectedFile) {
let status = folio.FolioSettings.saveLayoutToFile(selectedFile); let status = folio.FolioSettings.saveLayoutToFile(selectedFile); if (status) {
if (status) {
exportedSuccessfullyPrompt.open(); exportedSuccessfullyPrompt.open();
} else { } else {
exportFailedPrompt.open(); exportFailedPrompt.open();

View file

@ -3,26 +3,26 @@
#include "halcyonsettings.h" #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"); const QString CFG_KEY_DOUBLE_TAP_TO_LOCK = QStringLiteral("doubleTapToLock");
HalcyonSettings::HalcyonSettings(QObject *parent, KConfigGroup config) HalcyonSettings::HalcyonSettings(QObject *parent, KConfigGroup config)
: QObject{parent} : QObject{parent}
, m_config{config} , m_config{config}
{ {
load(); 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) { if (m_wallpaperBlurEffect != wallpaperBlurEffect) {
m_showWallpaperBlur = showWallpaperBlur; m_wallpaperBlurEffect = wallpaperBlurEffect;
Q_EMIT showWallpaperBlurChanged(); Q_EMIT wallpaperBlurEffectChanged();
save(); save();
} }
} }
@ -43,7 +43,7 @@ void HalcyonSettings::setDoubleTapToLock(bool doubleTapToLock)
void HalcyonSettings::save() 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.writeEntry(CFG_KEY_DOUBLE_TAP_TO_LOCK, m_doubleTapToLock);
m_config.sync(); m_config.sync();
@ -51,6 +51,9 @@ void HalcyonSettings::save()
void HalcyonSettings::load() 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); m_doubleTapToLock = m_config.readEntry(CFG_KEY_DOUBLE_TAP_TO_LOCK, true);
}
Q_EMIT doubleTapToLockChanged();
Q_EMIT wallpaperBlurEffectChanged();
}

View file

@ -10,28 +10,35 @@
class HalcyonSettings : public QObject class HalcyonSettings : public QObject
{ {
Q_OBJECT 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) Q_PROPERTY(bool doubleTapToLock READ doubleTapToLock WRITE setDoubleTapToLock NOTIFY doubleTapToLockChanged)
public: public:
HalcyonSettings(QObject *parent = nullptr, KConfigGroup config = {}); HalcyonSettings(QObject *parent = nullptr, KConfigGroup config = {});
bool showWallpaperBlur() const; enum WallpaperBlurEffect {
void setShowWallpaperBlur(bool blurWallpaper); None = 0,
Simple = 1,
Full = 2,
};
Q_ENUM(WallpaperBlurEffect)
WallpaperBlurEffect wallpaperBlurEffect() const;
void setWallpaperBlurEffect(WallpaperBlurEffect wallpaperBlurEffect);
bool doubleTapToLock() const; bool doubleTapToLock() const;
void setDoubleTapToLock(bool doubleTapToLock); void setDoubleTapToLock(bool doubleTapToLock);
Q_SIGNALS: Q_SIGNALS:
void showWallpaperBlurChanged(); void wallpaperBlurEffectChanged();
void doubleTapToLockChanged(); void doubleTapToLockChanged();
private: private:
void save(); void save();
void load(); void load();
bool m_showWallpaperBlur{false}; WallpaperBlurEffect m_wallpaperBlurEffect{Full};
bool m_doubleTapToLock{true}; bool m_doubleTapToLock{true};
KConfigGroup m_config; KConfigGroup m_config;
}; };

View file

@ -20,6 +20,7 @@ import org.kde.kirigami 2.19 as Kirigami
Item { Item {
id: delegate id: delegate
property MobileShell.MaskManager maskManager
property int visualIndex: 0 property int visualIndex: 0
property real dragFolderAnimationProgress: 0 property real dragFolderAnimationProgress: 0
@ -77,7 +78,7 @@ Item {
} }
function launchAppWithAnim(x: int, y: int, source, title: string, storageId: string) { function launchAppWithAnim(x: int, y: int, source, title: string, storageId: string) {
if (source !== "") { if (source !== "") {
MobileShellState.ShellDBusClient.openAppLaunchAnimationWithPosition( MobileShellState.ShellDBusClient.openAppLaunchAnimationWithPosition(
Plasmoid.screen, Plasmoid.screen,
source, source,
@ -265,6 +266,12 @@ Item {
color: Qt.rgba(255, 255, 255, 0.2) color: Qt.rgba(255, 255, 255, 0.2)
radius: Kirigami.Units.cornerRadius radius: Kirigami.Units.cornerRadius
opacity: delegate.dragFolderAnimationProgress opacity: delegate.dragFolderAnimationProgress
Component.onCompleted: {
if (maskManager) {
maskManager.assignToMask(this)
}
}
} }
Kirigami.Icon { Kirigami.Icon {
@ -319,6 +326,12 @@ Item {
xScale: 1 + delegate.dragFolderAnimationProgress * 0.5 xScale: 1 + delegate.dragFolderAnimationProgress * 0.5
yScale: 1 + delegate.dragFolderAnimationProgress * 0.5 yScale: 1 + delegate.dragFolderAnimationProgress * 0.5
} }
Component.onCompleted: {
if (maskManager) {
maskManager.assignToMask(this)
}
}
} }
Grid { Grid {

View file

@ -15,6 +15,7 @@ import org.kde.private.mobile.homescreen.halcyon as Halcyon
MobileShell.GridView { MobileShell.GridView {
id: root id: root
property MobileShell.MaskManager maskManager
required property var searchWidget required property var searchWidget
// don't set anchors.margins since we want everywhere to be draggable // don't set anchors.margins since we want everywhere to be draggable
@ -207,6 +208,7 @@ MobileShell.GridView {
// actual visual delegate // actual visual delegate
FavoritesAppDelegate { FavoritesAppDelegate {
id: appDelegate id: appDelegate
maskManager: root.maskManager
visualIndex: delegateRoot.visualIndex visualIndex: delegateRoot.visualIndex
isFolder: model.isFolder isFolder: model.isFolder

View file

@ -16,6 +16,7 @@ import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
Item { Item {
id: root id: root
layer.enabled: true layer.enabled: true
property MobileShell.MaskManager maskManager
required property bool interactive required property bool interactive
required property var searchWidget required property var searchWidget
@ -67,6 +68,7 @@ Item {
property real openFolderProgress: 0 property real openFolderProgress: 0
anchors.fill: parent anchors.fill: parent
maskManager: root.maskManager
interactive: root.interactive interactive: root.interactive
searchWidget: root.searchWidget searchWidget: root.searchWidget

View file

@ -19,6 +19,7 @@ import "settings" as Settings
Item { Item {
id: root id: root
property MobileShell.MaskManager maskManager
required property real topMargin required property real topMargin
required property real bottomMargin required property real bottomMargin
@ -33,6 +34,8 @@ Item {
property bool settingsOpen: false property bool settingsOpen: false
property real settingsOpenFactor: settingsOpen ? 1 : 0 property real settingsOpenFactor: settingsOpen ? 1 : 0
signal wallpaperSelectorTriggered()
Behavior on settingsOpenFactor { Behavior on settingsOpenFactor {
NumberAnimation { duration: 200 } NumberAnimation { duration: 200 }
} }
@ -121,6 +124,7 @@ Item {
FavoritesView { FavoritesView {
id: favoritesView id: favoritesView
anchors.fill: parent anchors.fill: parent
maskManager: root.maskManager
searchWidget: root.searchWidget searchWidget: root.searchWidget
interactive: root.interactive && swipeView.contentItem.contentX === 0 interactive: root.interactive && swipeView.contentItem.contentX === 0
onOpenConfigureRequested: root.openConfigure() onOpenConfigureRequested: root.openConfigure()

View file

@ -56,26 +56,42 @@ ContainmentItem {
screenGeometry: Plasmoid.containment.screenGeometry screenGeometry: Plasmoid.containment.screenGeometry
} }
MobileShell.HomeScreenWallpaperBlur { property MobileShell.MaskManager maskManager: MobileShell.MaskManager {
id: wallpaperBlur height: root.height
active: Plasmoid.settings.showWallpaperBlur width: root.width
anchors.fill: parent }
wallpaperItem: Plasmoid.wallpaperGraphicsObject
blurOpacity: Math.min(1, property MobileShell.MaskManager frontMaskManager: MobileShell.MaskManager {
Math.max(1 - homeScreen.contentOpacity, height: root.height
halcyonHomeScreen.settingsOpenFactor 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 { Rectangle {
id: darkenBackground 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 anchors.fill: parent
Behavior on color {
ColorAnimation { duration: Kirigami.Units.longDuration }
}
} }
Rectangle { Rectangle {
@ -106,6 +122,7 @@ ContainmentItem {
HomeScreen { HomeScreen {
id: halcyonHomeScreen id: halcyonHomeScreen
anchors.fill: parent anchors.fill: parent
maskManager: root.maskManager
topMargin: homeScreen.topMargin topMargin: homeScreen.topMargin
bottomMargin: homeScreen.bottomMargin bottomMargin: homeScreen.bottomMargin
@ -114,6 +131,8 @@ ContainmentItem {
searchWidget: search searchWidget: search
interactive: true interactive: true
onWallpaperSelectorTriggered: wallpaperSelectorLoader.active = true
} }
// search component // 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: { onClicked: {
root.homeScreen.settingsOpen = false; 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.FormCard {
FormCard.FormSwitchDelegate { FormCard.FormComboBoxDelegate {
id: showWallpaperBlur id: wallpaperBlurCombobox
text: i18nc("@option:check", "Show wallpaper blur effect") text: i18n("Wallpaper blur effect")
checked: Plasmoid.settings.showWallpaperBlur
onCheckedChanged: { model: [
if (checked != Plasmoid.settings.showWallpaperBlur) { {"name": i18nc("Wallpaper blur effect", "None"), "value": 0},
Plasmoid.settings.showWallpaperBlur = checked; {"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 { FormCard.FormSwitchDelegate {
id: doubleTapToSleepSwitch id: doubleTapToSleepSwitch