homescreens/halcyon: Add settings page

Add a settings page to halcyon, which allows for toggling whether the
wallpaper is blurred and double tapping on the homescreen to lock the
device. This also does a bit of refactoring for folio and halcyon to
share the same wallpaper blurring item.
This commit is contained in:
Devin Lin 2025-06-25 19:22:58 -04:00
parent 5d33295443
commit 1d3ceb5801
11 changed files with 344 additions and 41 deletions

View file

@ -0,0 +1,40 @@
// 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

@ -31,36 +31,13 @@ ContainmentItem {
forceActiveFocus(); forceActiveFocus();
} }
Loader { MobileShell.HomeScreenWallpaperBlur {
id: wallpaperBlurLoader id: wallpaperBlur
active: folio.FolioSettings.showWallpaperBlur active: folio.FolioSettings.showWallpaperBlur
anchors.fill: parent anchors.fill: parent
wallpaperItem: Plasmoid.wallpaperGraphicsObject
sourceComponent: Item { blurOpacity: Math.min(1,
id: wallpaper
anchors.fill: parent
// only take samples from wallpaper when we need the blur for performance
ShaderEffectSource {
id: controlledWallpaperSource
anchors.fill: parent
sourceItem: Plasmoid.wallpaperGraphicsObject
live: blur.visible
hideSource: false
visible: false
}
// 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: Math.min(1,
Math.max( Math.max(
1 - homeScreen.contentOpacity, 1 - homeScreen.contentOpacity,
folio.HomeScreenState.appDrawerOpenProgress * 2, // blur faster during swipe folio.HomeScreenState.appDrawerOpenProgress * 2, // blur faster during swipe
@ -69,8 +46,6 @@ ContainmentItem {
) )
) )
} }
}
}
WindowPlugin.WindowMaximizedTracker { WindowPlugin.WindowMaximizedTracker {
id: windowMaximizedTracker id: windowMaximizedTracker

View file

@ -5,6 +5,7 @@ add_definitions(-DTRANSLATION_DOMAIN=\"plasma_applet_org.kde.plasma.mobile.homes
set(homescreen_SRCS set(homescreen_SRCS
homescreen.cpp homescreen.cpp
halcyonsettings.cpp
) )
add_library(org.kde.plasma.mobile.homescreen.halcyon MODULE ${homescreen_SRCS}) add_library(org.kde.plasma.mobile.homescreen.halcyon MODULE ${homescreen_SRCS})
@ -14,11 +15,11 @@ target_link_libraries(org.kde.plasma.mobile.homescreen.halcyon
Qt::Qml Qt::Qml
Qt::Quick Qt::Quick
Plasma::Plasma Plasma::Plasma
Plasma::KWaylandClient
KF6::I18n KF6::I18n
KF6::Service KF6::Service
KF6::KIOGui KF6::KIOGui
KF6::Notifications KF6::Notifications
Plasma::KWaylandClient
KF6::WindowSystem KF6::WindowSystem
) )

View file

@ -0,0 +1,56 @@
// SPDX-FileCopyrightText: 2025 Devin Lin <devin@kde.org>
// SPDX-License-Identifier: GPL-2.0-or-later
#include "halcyonsettings.h"
const QString CFG_KEY_SHOW_WALLPAPER_BLUR = QStringLiteral("showWallpaperBlur");
const QString CFG_KEY_DOUBLE_TAP_TO_SLEEP = QStringLiteral("doubleTapToSleep");
HalcyonSettings::HalcyonSettings(QObject *parent, KConfigGroup config)
: QObject{parent}
, m_config{config}
{
load();
}
bool HalcyonSettings::showWallpaperBlur() const
{
return m_showWallpaperBlur;
}
void HalcyonSettings::setShowWallpaperBlur(bool showWallpaperBlur)
{
if (m_showWallpaperBlur != showWallpaperBlur) {
m_showWallpaperBlur = showWallpaperBlur;
Q_EMIT showWallpaperBlurChanged();
save();
}
}
bool HalcyonSettings::doubleTapToSleep() const
{
return m_doubleTapToSleep;
}
void HalcyonSettings::setDoubleTapToSleep(bool doubleTapToSleep)
{
if (m_doubleTapToSleep != doubleTapToSleep) {
m_doubleTapToSleep = doubleTapToSleep;
Q_EMIT doubleTapToSleepChanged();
save();
}
}
void HalcyonSettings::save()
{
m_config.writeEntry(CFG_KEY_SHOW_WALLPAPER_BLUR, m_showWallpaperBlur);
m_config.writeEntry(CFG_KEY_DOUBLE_TAP_TO_SLEEP, m_doubleTapToSleep);
m_config.sync();
}
void HalcyonSettings::load()
{
m_showWallpaperBlur = m_config.readEntry(CFG_KEY_SHOW_WALLPAPER_BLUR, false);
m_doubleTapToSleep = m_config.readEntry(CFG_KEY_DOUBLE_TAP_TO_SLEEP, true);
}

View file

@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: 2025 Devin Lin <devin@kde.org>
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QObject>
#include <KConfigGroup>
class HalcyonSettings : public QObject
{
Q_OBJECT
Q_PROPERTY(bool showWallpaperBlur READ showWallpaperBlur WRITE setShowWallpaperBlur NOTIFY showWallpaperBlurChanged)
Q_PROPERTY(bool doubleTapToSleep READ doubleTapToSleep WRITE setDoubleTapToSleep NOTIFY doubleTapToSleepChanged)
public:
HalcyonSettings(QObject *parent = nullptr, KConfigGroup config = {});
bool showWallpaperBlur() const;
void setShowWallpaperBlur(bool blurWallpaper);
bool doubleTapToSleep() const;
void setDoubleTapToSleep(bool doubleTapToSleep);
Q_SIGNALS:
void showWallpaperBlurChanged();
void doubleTapToSleepChanged();
private:
void save();
void load();
bool m_showWallpaperBlur{false};
bool m_doubleTapToSleep{true};
KConfigGroup m_config;
};

View file

@ -11,12 +11,18 @@
HomeScreen::HomeScreen(QObject *parent, const KPluginMetaData &data, const QVariantList &args) HomeScreen::HomeScreen(QObject *parent, const KPluginMetaData &data, const QVariantList &args)
: Plasma::Containment{parent, data, args} : Plasma::Containment{parent, data, args}
, m_settings{new HalcyonSettings{this, config()}}
{ {
setHasConfigurationInterface(true); setHasConfigurationInterface(true);
} }
HomeScreen::~HomeScreen() = default; HomeScreen::~HomeScreen() = default;
HalcyonSettings *HomeScreen::settings() const
{
return m_settings;
}
K_PLUGIN_CLASS(HomeScreen) K_PLUGIN_CLASS(HomeScreen)
#include "homescreen.moc" #include "homescreen.moc"

View file

@ -5,14 +5,22 @@
#include <Plasma/Containment> #include <Plasma/Containment>
#include "halcyonsettings.h"
class HomeScreen : public Plasma::Containment class HomeScreen : public Plasma::Containment
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(HalcyonSettings *settings READ settings CONSTANT)
public: public:
HomeScreen(QObject *parent, const KPluginMetaData &data, const QVariantList &args); HomeScreen(QObject *parent, const KPluginMetaData &data, const QVariantList &args);
~HomeScreen() override; ~HomeScreen() override;
HalcyonSettings *settings() const;
Q_SIGNALS: Q_SIGNALS:
void showingDesktopChanged(bool showingDesktop); void showingDesktopChanged(bool showingDesktop);
private:
HalcyonSettings *m_settings{nullptr};
}; };

View file

@ -13,6 +13,9 @@ import org.kde.draganddrop as DragDrop
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.plasma.private.mobileshell.state as MobileShellState import org.kde.plasma.private.mobileshell.state as MobileShellState
import org.kde.plasma.private.mobileshell.windowplugin as WindowPlugin import org.kde.plasma.private.mobileshell.windowplugin as WindowPlugin
import org.kde.plasma.private.mobileshell as MobileShell
import "settings" as Settings
Item { Item {
id: root id: root
@ -62,7 +65,7 @@ Item {
} }
} }
SettingsScreen { Settings.SettingsScreen {
id: settings id: settings
bottomMargin: root.bottomMargin bottomMargin: root.bottomMargin
anchors.fill: parent anchors.fill: parent
@ -104,6 +107,15 @@ Item {
} }
onLongPressed: root.openConfigure() onLongPressed: root.openConfigure()
onDoubleTapped: {
if (Plasmoid.settings.doubleTapToSleep) {
doubleTapToSleep.doubleTapped();
}
}
}
MobileShell.DoubleTapToSleep {
id: doubleTapToSleep
} }
FavoritesView { FavoritesView {

View file

@ -56,11 +56,23 @@ ContainmentItem {
screenGeometry: Plasmoid.containment.screenGeometry screenGeometry: Plasmoid.containment.screenGeometry
} }
MobileShell.HomeScreenWallpaperBlur {
id: wallpaperBlur
active: Plasmoid.settings.showWallpaperBlur
anchors.fill: parent
wallpaperItem: Plasmoid.wallpaperGraphicsObject
blurOpacity: Math.min(1,
Math.max(1 - homeScreen.contentOpacity,
halcyonHomeScreen.settingsOpenFactor
)
)
}
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: (halcyonHomeScreen.page == 1 ? Qt.rgba(0, 0, 0, 0.7) : Qt.rgba(0, 0, 0, 0.2))
anchors.fill: parent anchors.fill: parent
z: -1
Behavior on color { Behavior on color {
ColorAnimation { duration: Kirigami.Units.longDuration } ColorAnimation { duration: Kirigami.Units.longDuration }
} }
@ -71,7 +83,6 @@ ContainmentItem {
color: Qt.rgba(0, 0, 0, 0.7) color: Qt.rgba(0, 0, 0, 0.7)
opacity: halcyonHomeScreen.settingsOpenFactor opacity: halcyonHomeScreen.settingsOpenFactor
anchors.fill: parent anchors.fill: parent
z: -1
Behavior on color { Behavior on color {
ColorAnimation { duration: Kirigami.Units.longDuration } ColorAnimation { duration: Kirigami.Units.longDuration }
} }

View file

@ -103,12 +103,40 @@ Item {
onClicked: { onClicked: {
root.homeScreen.settingsOpen = false; root.homeScreen.settingsOpen = false;
root.homeScreen.openContainmentSettings();
if (settingsWindowLoader.active) {
// Ensure that if the window is already opened, it gets raised to the top
settingsWindowLoader.item.hide();
settingsWindowLoader.item.showMaximized();
} else {
settingsWindowLoader.active = true;
}
} }
} }
} }
} }
// Only load settings window when visible
Loader {
id: settingsWindowLoader
asynchronous: true
active: false
onLoaded: item.showMaximized();
sourceComponent: SettingsWindow {
onVisibleChanged: {
if (!visible) {
settingsWindowLoader.active = false;
}
}
onRequestConfigureMenu: {
root.homeScreen.openContainmentSettings();
}
}
}
// Only load wallpaper selector when visible
Loader { Loader {
id: wallpaperSelectorLoader id: wallpaperSelectorLoader
asynchronous: true asynchronous: true

View file

@ -0,0 +1,129 @@
// SPDX-FileCopyrightText: 2023-2025 Devin Lin <devin@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
import QtQuick
import QtQuick.Window
import QtQuick.Layouts
import QtQuick.Dialogs
import QtQuick.Controls as QQC2
import org.kde.plasma.plasmoid
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard
Window {
id: root
flags: Qt.FramelessWindowHint
color: 'transparent'
onVisibleChanged: {
if (visible) {
opacityAnim.to = 1;
opacityAnim.restart();
}
}
onClosing: (close) => {
if (applicationItem.opacity !== 0) {
close.accepted = false;
opacityAnim.to = 0;
opacityAnim.restart();
}
}
signal requestConfigureMenu()
Kirigami.ApplicationItem {
id: applicationItem
anchors.fill: parent
opacity: 0
NumberAnimation on opacity {
id: opacityAnim
duration: 200
easing.type: Easing.OutCubic
onFinished: {
if (applicationItem.opacity === 0) {
root.close();
}
}
}
scale: 0.7 + 0.3 * applicationItem.opacity
pageStack.globalToolBar.style: Kirigami.ApplicationHeaderStyle.ToolBar
pageStack.globalToolBar.showNavigationButtons: Kirigami.ApplicationHeaderStyle.NoNavigationButtons;
pageStack.initialPage: Kirigami.ScrollablePage {
id: page
opacity: applicationItem.opacity
titleDelegate: RowLayout {
QQC2.ToolButton {
Layout.leftMargin: -Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing
icon.name: "arrow-left"
onClicked: root.close()
}
Kirigami.Heading {
level: 1
text: page.title
}
}
title: i18n("Homescreen Settings")
topPadding: 0
bottomPadding: 0
leftPadding: 0
rightPadding: 0
ColumnLayout {
FormCard.FormHeader {
title: i18n("Homescreen")
}
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.FormDelegateSeparator { above: showWallpaperBlur; below: doubleTapToSleepSwitch }
FormCard.FormSwitchDelegate {
id: doubleTapToSleepSwitch
text: i18n("Double tap to lock device")
checked: Plasmoid.settings.doubleTapToSleep
onCheckedChanged: {
if (checked != Plasmoid.settings.doubleTapToSleep) {
Plasmoid.settings.doubleTapToSleep = checked;
}
}
}
}
FormCard.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.bottomMargin: Kirigami.Units.gridUnit
FormCard.FormButtonDelegate {
id: containmentSettings
text: i18nc("@action:button", "Switch between homescreens and more wallpaper options")
icon.name: 'settings-configure'
onClicked: root.requestConfigureMenu()
}
}
}
}
}
}