nosignal-shell/modules/background/Wallpaper.qml
2 * r + 2 * t 04f34034c3 feat: rework wallpaper to use stack instead of A/B
Also switch colour animation to SlowEffects
2026-04-23 01:00:35 +10:00

134 lines
3.7 KiB
QML

pragma ComponentBehavior: Bound
import QtQuick
import Caelestia.Config
import qs.components
import qs.components.filedialog
import qs.components.images
import qs.services
import qs.utils
Item {
id: root
property string source: Wallpapers.current
property CachingImage current
property bool completed
onSourceChanged: {
if (!source)
current = null;
else
current = imgComp.createObject(this, {
path: source
});
}
Component.onCompleted: {
if (source)
Qt.callLater(() => {
current = imgComp.createObject(this, {
path: source
});
completed = true;
});
}
Loader {
asynchronous: true
anchors.fill: parent
active: root.completed && !root.source
sourceComponent: StyledRect {
color: Colours.palette.m3surfaceContainer
Row {
anchors.centerIn: parent
spacing: Tokens.spacing.largeIncreased
MaterialIcon {
text: "sentiment_stressed"
color: Colours.palette.m3onSurfaceVariant
fontStyle: Tokens.font.icon.builders.extraLarge.scale(5).build()
}
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: Tokens.spacing.small
StyledText {
text: qsTr("Wallpaper missing?")
color: Colours.palette.m3onSurfaceVariant
font: Tokens.font.body.builders.large.size(28 * 2).weight(Font.Bold).build()
}
StyledRect {
implicitWidth: selectWallText.implicitWidth + Tokens.padding.extraLargeIncreased
implicitHeight: selectWallText.implicitHeight + Tokens.padding.small
radius: Tokens.rounding.full
color: Colours.palette.m3primary
FileDialog {
id: dialog
title: qsTr("Select a wallpaper")
filterLabel: qsTr("Image files")
filters: Images.validImageExtensions
onAccepted: path => Wallpapers.setWallpaper(path)
}
StateLayer {
radius: parent.radius
color: Colours.palette.m3onPrimary
onClicked: dialog.open()
}
StyledText {
id: selectWallText
anchors.centerIn: parent
text: qsTr("Set it now!")
color: Colours.palette.m3onPrimary
font: Tokens.font.body.large
}
}
}
}
}
}
Component {
id: imgComp
CachingImage {
id: img
anchors.fill: parent
opacity: 0
onStatusChanged: {
if (status === Image.Ready)
anim.start();
}
Anim on opacity {
id: anim
type: Anim.SlowEffects
running: false
from: 0
to: 1
}
Timer {
running: root.current !== img && root.current?.status === Image.Ready
interval: anim.duration
onTriggered: img.destroy()
}
}
}
}