mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-08-07 05:58:28 -04:00
wallpaper: optimize blurred wallpaper layer
port 1.5
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Effects
|
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
import qs.Common
|
import qs.Common
|
||||||
@@ -57,60 +56,96 @@ Variants {
|
|||||||
}
|
}
|
||||||
|
|
||||||
property string source: SessionData.getMonitorWallpaper(modelData.name) || ""
|
property string source: SessionData.getMonitorWallpaper(modelData.name) || ""
|
||||||
property bool isColorSource: source.startsWith("#")
|
readonly property bool isColorSource: source.startsWith("#")
|
||||||
|
readonly property string displaySource: {
|
||||||
|
if (!source || isColorSource)
|
||||||
|
return "";
|
||||||
|
return source.startsWith("file://") ? source : encodeFileUrl(source);
|
||||||
|
}
|
||||||
property bool contentReady: false
|
property bool contentReady: false
|
||||||
property bool surfaceBounce: false
|
property bool surfaceBounce: false
|
||||||
|
|
||||||
Connections {
|
// Live stack is captured into frozenLayer once stable, then unloaded; if the capture stalls the stack just stays live.
|
||||||
target: SessionData
|
property bool liveActive: false
|
||||||
function onIsLightModeChanged() {
|
property bool frozenValid: false
|
||||||
if (SessionData.perModeWallpaper) {
|
property string _frozenSource: ""
|
||||||
var newSource = SessionData.getMonitorWallpaper(modelData.name) || "";
|
property bool loadFailed: false
|
||||||
if (newSource !== root.source) {
|
property int _freezeWaitFrames: 0
|
||||||
root.source = newSource;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFillMode(modeName) {
|
readonly property var backingWindow: Window.window
|
||||||
switch (modeName) {
|
readonly property bool renderActive: !source || liveActive || _freezeWaitFrames > 0
|
||||||
case "Stretch":
|
property int _settleFrames: 3
|
||||||
return Image.Stretch;
|
|
||||||
case "Fit":
|
readonly property int maxTextureSize: 8192
|
||||||
case "PreserveAspectFit":
|
readonly property int textureWidth: Math.min(modelData.width, maxTextureSize)
|
||||||
return Image.PreserveAspectFit;
|
readonly property int textureHeight: Math.min(modelData.height, maxTextureSize)
|
||||||
case "Fill":
|
|
||||||
case "PreserveAspectCrop":
|
|
||||||
return Image.PreserveAspectCrop;
|
|
||||||
case "Tile":
|
|
||||||
return Image.Tile;
|
|
||||||
case "TileVertically":
|
|
||||||
return Image.TileVertically;
|
|
||||||
case "TileHorizontally":
|
|
||||||
return Image.TileHorizontally;
|
|
||||||
case "Pad":
|
|
||||||
return Image.Pad;
|
|
||||||
default:
|
|
||||||
return Image.PreserveAspectCrop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
isInitialized = true;
|
if (!displaySource) {
|
||||||
if (!source || isColorSource) {
|
|
||||||
contentReady = true;
|
contentReady = true;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
liveActive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
property bool isInitialized: false
|
onDisplaySourceChanged: {
|
||||||
property real transitionProgress: 0
|
invalidate();
|
||||||
readonly property bool transitioning: transitionAnimation.running
|
loadFailed = false;
|
||||||
property bool effectActive: false
|
_freezeWaitFrames = 0;
|
||||||
property bool useNextForEffect: false
|
if (!displaySource) {
|
||||||
readonly property var backingWindow: Window.window
|
liveActive = false;
|
||||||
readonly property bool renderActive: !source || effectActive || currentWallpaper.status === Image.Loading || nextWallpaper.status === Image.Loading
|
frozenValid = false;
|
||||||
property int _settleFrames: 3
|
_frozenSource = "";
|
||||||
|
contentReady = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
liveActive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function regenerate() {
|
||||||
|
invalidate();
|
||||||
|
if (!displaySource)
|
||||||
|
return;
|
||||||
|
if (liveActive) {
|
||||||
|
scheduleFreeze();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
liveActive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDisplayable() {
|
||||||
|
contentReady = true;
|
||||||
|
if (liveLoader.item?.currentFailed) {
|
||||||
|
if (!frozenValid)
|
||||||
|
loadFailed = true;
|
||||||
|
liveActive = false;
|
||||||
|
}
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
function scheduleFreeze() {
|
||||||
|
if (!liveLoader.item?.stable)
|
||||||
|
return;
|
||||||
|
frozenLayer.scheduleUpdate();
|
||||||
|
_freezeWaitFrames = 3;
|
||||||
|
_settleFrames = 3;
|
||||||
|
// No wedge watchdog: an occluded surface may never produce frames, the freeze just waits
|
||||||
|
backingWindow?.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
function completeFreeze() {
|
||||||
|
const live = liveLoader.item;
|
||||||
|
if (!live || !live.stable)
|
||||||
|
return;
|
||||||
|
frozenValid = true;
|
||||||
|
_frozenSource = displaySource;
|
||||||
|
liveActive = false;
|
||||||
|
log.info("froze blur layer for", modelData.name);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
onTextureWidthChanged: regenerate()
|
||||||
|
onTextureHeightChanged: regenerate()
|
||||||
|
|
||||||
function invalidate() {
|
function invalidate() {
|
||||||
_settleFrames = 3;
|
_settleFrames = 3;
|
||||||
@@ -157,29 +192,35 @@ Variants {
|
|||||||
root._settleFrames--;
|
root._settleFrames--;
|
||||||
root._wedgeBounced = false;
|
root._wedgeBounced = false;
|
||||||
wedgeWatchdog.stop();
|
wedgeWatchdog.stop();
|
||||||
|
if (root._freezeWaitFrames > 0 && --root._freezeWaitFrames === 0)
|
||||||
|
root.completeFreeze();
|
||||||
}
|
}
|
||||||
function onVisibleChanged() {
|
function onVisibleChanged() {
|
||||||
root.invalidate();
|
root.invalidate();
|
||||||
}
|
}
|
||||||
function onWidthChanged() {
|
function onWidthChanged() {
|
||||||
root.invalidate();
|
root.regenerate();
|
||||||
}
|
}
|
||||||
function onHeightChanged() {
|
function onHeightChanged() {
|
||||||
root.invalidate();
|
root.regenerate();
|
||||||
|
}
|
||||||
|
function onResourcesLost() {
|
||||||
|
root.frozenValid = false;
|
||||||
|
root.regenerate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: Quickshell
|
target: Quickshell
|
||||||
function onScreensChanged() {
|
function onScreensChanged() {
|
||||||
root.invalidate();
|
root.regenerate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: SettingsData
|
target: SettingsData
|
||||||
function onWallpaperFillModeChanged() {
|
function onWallpaperFillModeChanged() {
|
||||||
root.invalidate();
|
root.regenerate();
|
||||||
}
|
}
|
||||||
function onEffectiveWallpaperBackgroundColorChanged() {
|
function onEffectiveWallpaperBackgroundColorChanged() {
|
||||||
root.invalidate();
|
root.invalidate();
|
||||||
@@ -188,18 +229,26 @@ Variants {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: SessionData
|
target: SessionData
|
||||||
|
function onIsLightModeChanged() {
|
||||||
|
if (SessionData.perModeWallpaper) {
|
||||||
|
var newSource = SessionData.getMonitorWallpaper(modelData.name) || "";
|
||||||
|
if (newSource !== root.source) {
|
||||||
|
root.source = newSource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
function onMonitorWallpaperFillModesChanged() {
|
function onMonitorWallpaperFillModesChanged() {
|
||||||
root.invalidate();
|
root.regenerate();
|
||||||
}
|
}
|
||||||
function onPerMonitorWallpaperChanged() {
|
function onPerMonitorWallpaperChanged() {
|
||||||
root.invalidate();
|
root.regenerate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Theme changes repaint DankBackdrop but nothing else wakes the render loop
|
// Theme changes repaint DankBackdrop but nothing else wakes the render loop
|
||||||
Connections {
|
Connections {
|
||||||
target: Theme
|
target: Theme
|
||||||
enabled: root.isColorSource || currentWallpaper.status === Image.Error
|
enabled: root.isColorSource || root.loadFailed
|
||||||
function onPrimaryChanged() {
|
function onPrimaryChanged() {
|
||||||
root.invalidate();
|
root.invalidate();
|
||||||
}
|
}
|
||||||
@@ -217,89 +266,23 @@ Variants {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTransitionLoadError(failedSource) {
|
Connections {
|
||||||
log.warn("failed to load candidate wallpaper for", modelData.name + ":", failedSource);
|
target: liveLoader.item
|
||||||
transitionDelayTimer.stop();
|
function onBecameDisplayable() {
|
||||||
transitionAnimation.stop();
|
root.handleDisplayable();
|
||||||
root.useNextForEffect = false;
|
|
||||||
root.effectActive = false;
|
|
||||||
root.transitionProgress = 0.0;
|
|
||||||
nextWallpaper.source = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
onSourceChanged: {
|
|
||||||
invalidate();
|
|
||||||
if (!source || source.startsWith("#")) {
|
|
||||||
setWallpaperImmediate("");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
function onStableChanged() {
|
||||||
const formattedSource = source.startsWith("file://") ? source : encodeFileUrl(source);
|
if (liveLoader.item.stable)
|
||||||
|
root.scheduleFreeze();
|
||||||
if (!isInitialized || !currentWallpaper.source) {
|
|
||||||
setWallpaperImmediate(formattedSource);
|
|
||||||
isInitialized = true;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (CompositorService.isNiri && SessionData.isSwitchingMode) {
|
function onTransitioningChanged() {
|
||||||
setWallpaperImmediate(formattedSource);
|
root.invalidate();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
changeWallpaper(formattedSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setWallpaperImmediate(newSource) {
|
|
||||||
transitionDelayTimer.stop();
|
|
||||||
transitionAnimation.stop();
|
|
||||||
root.transitionProgress = 0.0;
|
|
||||||
root.effectActive = false;
|
|
||||||
if (!newSource)
|
|
||||||
root.contentReady = true;
|
|
||||||
currentWallpaper.source = newSource;
|
|
||||||
nextWallpaper.source = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
function startTransition() {
|
|
||||||
root.useNextForEffect = true;
|
|
||||||
root.effectActive = true;
|
|
||||||
if (srcNext.scheduleUpdate)
|
|
||||||
srcNext.scheduleUpdate();
|
|
||||||
transitionDelayTimer.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: transitionDelayTimer
|
|
||||||
interval: 16
|
|
||||||
repeat: false
|
|
||||||
onTriggered: transitionAnimation.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeWallpaper(newPath) {
|
|
||||||
if (newPath === currentWallpaper.source)
|
|
||||||
return;
|
|
||||||
if (!newPath || newPath.startsWith("#"))
|
|
||||||
return;
|
|
||||||
if (root.transitioning) {
|
|
||||||
transitionAnimation.stop();
|
|
||||||
root.transitionProgress = 0;
|
|
||||||
root.effectActive = false;
|
|
||||||
currentWallpaper.source = nextWallpaper.source;
|
|
||||||
nextWallpaper.source = "";
|
|
||||||
}
|
|
||||||
if (!currentWallpaper.source) {
|
|
||||||
setWallpaperImmediate(newPath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nextWallpaper.source = newPath;
|
|
||||||
|
|
||||||
if (nextWallpaper.status === Image.Ready)
|
|
||||||
root.startTransition();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: !root.source || root.isColorSource || currentWallpaper.status === Image.Error
|
active: !root.source || root.isColorSource || root.loadFailed
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
|
|
||||||
sourceComponent: DankBackdrop {
|
sourceComponent: DankBackdrop {
|
||||||
@@ -307,126 +290,43 @@ Variants {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property int maxTextureSize: 8192
|
|
||||||
property int textureWidth: Math.min(modelData.width, maxTextureSize)
|
|
||||||
property int textureHeight: Math.min(modelData.height, maxTextureSize)
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: currentWallpaper
|
|
||||||
anchors.fill: parent
|
|
||||||
visible: false
|
|
||||||
opacity: 1
|
|
||||||
asynchronous: true
|
|
||||||
retainWhileLoading: true
|
|
||||||
smooth: true
|
|
||||||
cache: true
|
|
||||||
sourceSize: Qt.size(root.textureWidth, root.textureHeight)
|
|
||||||
fillMode: root.getFillMode(SessionData.isGreeterMode ? GreetdSettings.wallpaperFillMode : SessionData.getMonitorWallpaperFillMode(modelData.name))
|
|
||||||
|
|
||||||
onStatusChanged: {
|
|
||||||
if (status === Image.Error) {
|
|
||||||
log.warn("failed to load active wallpaper for", modelData.name + ":", source);
|
|
||||||
}
|
|
||||||
if (status === Image.Ready || status === Image.Error) {
|
|
||||||
root.contentReady = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: nextWallpaper
|
|
||||||
anchors.fill: parent
|
|
||||||
visible: false
|
|
||||||
opacity: 0
|
|
||||||
asynchronous: true
|
|
||||||
retainWhileLoading: true
|
|
||||||
smooth: true
|
|
||||||
cache: true
|
|
||||||
sourceSize: Qt.size(root.textureWidth, root.textureHeight)
|
|
||||||
fillMode: root.getFillMode(SessionData.isGreeterMode ? GreetdSettings.wallpaperFillMode : SessionData.getMonitorWallpaperFillMode(modelData.name))
|
|
||||||
|
|
||||||
onStatusChanged: {
|
|
||||||
if (status === Image.Error) {
|
|
||||||
root.handleTransitionLoadError(source);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (status !== Image.Ready)
|
|
||||||
return;
|
|
||||||
if (!root.transitioning) {
|
|
||||||
root.startTransition();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShaderEffectSource {
|
ShaderEffectSource {
|
||||||
id: srcNext
|
id: frozenLayer
|
||||||
sourceItem: root.effectActive ? nextWallpaper : null
|
anchors.fill: parent
|
||||||
hideSource: root.effectActive
|
sourceItem: liveContainer
|
||||||
live: root.effectActive
|
|
||||||
mipmap: false
|
|
||||||
recursive: false
|
|
||||||
textureSize: Qt.size(root.textureWidth, root.textureHeight)
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: dummyRect
|
|
||||||
width: 1
|
|
||||||
height: 1
|
|
||||||
visible: false
|
|
||||||
color: "transparent"
|
|
||||||
}
|
|
||||||
|
|
||||||
ShaderEffectSource {
|
|
||||||
id: srcDummy
|
|
||||||
sourceItem: dummyRect
|
|
||||||
hideSource: true
|
|
||||||
live: false
|
live: false
|
||||||
mipmap: false
|
mipmap: false
|
||||||
recursive: false
|
recursive: false
|
||||||
|
smooth: true
|
||||||
|
visible: root.frozenValid || root.liveActive
|
||||||
|
textureSize: Qt.size(root.textureWidth, root.textureHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: blurredLayer
|
id: liveContainer
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
visible: root.liveActive
|
||||||
|
|
||||||
MultiEffect {
|
Loader {
|
||||||
|
id: liveLoader
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
source: currentWallpaper
|
active: root.liveActive
|
||||||
visible: currentWallpaper.source !== ""
|
asynchronous: false
|
||||||
blurEnabled: true
|
|
||||||
blur: 0.8
|
|
||||||
blurMax: 75
|
|
||||||
opacity: 1 - root.transitionProgress
|
|
||||||
autoPaddingEnabled: false
|
|
||||||
}
|
|
||||||
|
|
||||||
MultiEffect {
|
// Cached images reach Ready synchronously during creation, before Connections retargets
|
||||||
anchors.fill: parent
|
onLoaded: {
|
||||||
source: root.useNextForEffect ? srcNext : srcDummy
|
if (item.displayableNow)
|
||||||
visible: nextWallpaper.source !== "" && root.useNextForEffect
|
root.handleDisplayable();
|
||||||
blurEnabled: true
|
if (item.stable)
|
||||||
blur: 0.8
|
root.scheduleFreeze();
|
||||||
blurMax: 75
|
}
|
||||||
opacity: root.transitionProgress
|
|
||||||
autoPaddingEnabled: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NumberAnimation {
|
sourceComponent: BlurredWallpaperLive {
|
||||||
id: transitionAnimation
|
wallpaperSource: root.displaySource
|
||||||
target: root
|
initialSource: root._frozenSource
|
||||||
property: "transitionProgress"
|
screenName: modelData.name
|
||||||
from: 0.0
|
blurTextureSize: Qt.size(root.textureWidth, root.textureHeight)
|
||||||
to: 1.0
|
}
|
||||||
duration: 1000
|
|
||||||
easing.type: Easing.InOutCubic
|
|
||||||
onFinished: {
|
|
||||||
if (nextWallpaper.source && nextWallpaper.status === Image.Ready)
|
|
||||||
currentWallpaper.source = nextWallpaper.source;
|
|
||||||
root.useNextForEffect = false;
|
|
||||||
nextWallpaper.source = "";
|
|
||||||
root.transitionProgress = 0.0;
|
|
||||||
root.effectActive = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,255 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property var log: Log.scoped("BlurredWallpaperLive")
|
||||||
|
|
||||||
|
required property string wallpaperSource
|
||||||
|
required property string initialSource
|
||||||
|
required property string screenName
|
||||||
|
required property size blurTextureSize
|
||||||
|
|
||||||
|
readonly property bool currentFailed: currentWallpaper.status === Image.Error
|
||||||
|
readonly property bool displayableNow: currentWallpaper.status === Image.Ready || currentWallpaper.status === Image.Error
|
||||||
|
readonly property bool stable: isInitialized && !effectActive && !transitionAnimation.running && !transitionDelayTimer.running && currentWallpaper.status === Image.Ready && currentWallpaper.source.toString() === wallpaperSource && !nextWallpaper.source.toString()
|
||||||
|
|
||||||
|
signal becameDisplayable
|
||||||
|
|
||||||
|
property bool isInitialized: false
|
||||||
|
property real transitionProgress: 0
|
||||||
|
readonly property bool transitioning: transitionAnimation.running
|
||||||
|
property bool effectActive: false
|
||||||
|
property bool useNextForEffect: false
|
||||||
|
|
||||||
|
function getFillMode(modeName) {
|
||||||
|
switch (modeName) {
|
||||||
|
case "Stretch":
|
||||||
|
return Image.Stretch;
|
||||||
|
case "Fit":
|
||||||
|
case "PreserveAspectFit":
|
||||||
|
return Image.PreserveAspectFit;
|
||||||
|
case "Fill":
|
||||||
|
case "PreserveAspectCrop":
|
||||||
|
return Image.PreserveAspectCrop;
|
||||||
|
case "Tile":
|
||||||
|
return Image.Tile;
|
||||||
|
case "TileVertically":
|
||||||
|
return Image.TileVertically;
|
||||||
|
case "TileHorizontally":
|
||||||
|
return Image.TileHorizontally;
|
||||||
|
case "Pad":
|
||||||
|
return Image.Pad;
|
||||||
|
default:
|
||||||
|
return Image.PreserveAspectCrop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (initialSource && initialSource !== wallpaperSource && !(CompositorService.isNiri && SessionData.isSwitchingMode)) {
|
||||||
|
currentWallpaper.source = initialSource;
|
||||||
|
isInitialized = true;
|
||||||
|
changeWallpaper(wallpaperSource);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentWallpaper.source = wallpaperSource;
|
||||||
|
isInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onWallpaperSourceChanged: {
|
||||||
|
if (!isInitialized)
|
||||||
|
return;
|
||||||
|
if (!wallpaperSource) {
|
||||||
|
setWallpaperImmediate("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!currentWallpaper.source.toString()) {
|
||||||
|
setWallpaperImmediate(wallpaperSource);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (CompositorService.isNiri && SessionData.isSwitchingMode) {
|
||||||
|
setWallpaperImmediate(wallpaperSource);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
changeWallpaper(wallpaperSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleTransitionLoadError(failedSource) {
|
||||||
|
log.warn("failed to load candidate wallpaper for", screenName + ":", failedSource);
|
||||||
|
transitionDelayTimer.stop();
|
||||||
|
transitionAnimation.stop();
|
||||||
|
useNextForEffect = false;
|
||||||
|
effectActive = false;
|
||||||
|
transitionProgress = 0.0;
|
||||||
|
nextWallpaper.source = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function setWallpaperImmediate(newSource) {
|
||||||
|
transitionDelayTimer.stop();
|
||||||
|
transitionAnimation.stop();
|
||||||
|
transitionProgress = 0.0;
|
||||||
|
effectActive = false;
|
||||||
|
currentWallpaper.source = newSource;
|
||||||
|
nextWallpaper.source = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function startTransition() {
|
||||||
|
useNextForEffect = true;
|
||||||
|
effectActive = true;
|
||||||
|
if (srcNext.scheduleUpdate)
|
||||||
|
srcNext.scheduleUpdate();
|
||||||
|
transitionDelayTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeWallpaper(newPath) {
|
||||||
|
if (!newPath)
|
||||||
|
return;
|
||||||
|
if (newPath === currentWallpaper.source.toString())
|
||||||
|
return;
|
||||||
|
if (transitioning) {
|
||||||
|
transitionAnimation.stop();
|
||||||
|
transitionProgress = 0;
|
||||||
|
effectActive = false;
|
||||||
|
currentWallpaper.source = nextWallpaper.source;
|
||||||
|
nextWallpaper.source = "";
|
||||||
|
}
|
||||||
|
if (!currentWallpaper.source.toString()) {
|
||||||
|
setWallpaperImmediate(newPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextWallpaper.source = newPath;
|
||||||
|
|
||||||
|
if (nextWallpaper.status === Image.Ready)
|
||||||
|
startTransition();
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: transitionDelayTimer
|
||||||
|
interval: 16
|
||||||
|
repeat: false
|
||||||
|
onTriggered: transitionAnimation.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: currentWallpaper
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: false
|
||||||
|
opacity: 1
|
||||||
|
asynchronous: true
|
||||||
|
retainWhileLoading: true
|
||||||
|
smooth: true
|
||||||
|
cache: true
|
||||||
|
sourceSize: root.blurTextureSize
|
||||||
|
fillMode: root.getFillMode(SessionData.isGreeterMode ? GreetdSettings.wallpaperFillMode : SessionData.getMonitorWallpaperFillMode(root.screenName))
|
||||||
|
|
||||||
|
onStatusChanged: {
|
||||||
|
if (status === Image.Error) {
|
||||||
|
root.log.warn("failed to load active wallpaper for", root.screenName + ":", source);
|
||||||
|
}
|
||||||
|
if (status === Image.Ready || status === Image.Error) {
|
||||||
|
root.becameDisplayable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: nextWallpaper
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: false
|
||||||
|
opacity: 0
|
||||||
|
asynchronous: true
|
||||||
|
retainWhileLoading: true
|
||||||
|
smooth: true
|
||||||
|
cache: true
|
||||||
|
sourceSize: root.blurTextureSize
|
||||||
|
fillMode: root.getFillMode(SessionData.isGreeterMode ? GreetdSettings.wallpaperFillMode : SessionData.getMonitorWallpaperFillMode(root.screenName))
|
||||||
|
|
||||||
|
onStatusChanged: {
|
||||||
|
if (status === Image.Error) {
|
||||||
|
root.handleTransitionLoadError(source);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (status !== Image.Ready)
|
||||||
|
return;
|
||||||
|
if (!root.transitioning) {
|
||||||
|
root.startTransition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderEffectSource {
|
||||||
|
id: srcNext
|
||||||
|
sourceItem: root.effectActive ? nextWallpaper : null
|
||||||
|
hideSource: root.effectActive
|
||||||
|
live: root.effectActive
|
||||||
|
mipmap: false
|
||||||
|
recursive: false
|
||||||
|
textureSize: root.blurTextureSize
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: dummyRect
|
||||||
|
width: 1
|
||||||
|
height: 1
|
||||||
|
visible: false
|
||||||
|
color: "transparent"
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderEffectSource {
|
||||||
|
id: srcDummy
|
||||||
|
sourceItem: dummyRect
|
||||||
|
hideSource: true
|
||||||
|
live: false
|
||||||
|
mipmap: false
|
||||||
|
recursive: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: blurredLayer
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
MultiEffect {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: currentWallpaper
|
||||||
|
visible: currentWallpaper.source !== ""
|
||||||
|
blurEnabled: true
|
||||||
|
blur: 0.8
|
||||||
|
blurMax: 75
|
||||||
|
opacity: 1 - root.transitionProgress
|
||||||
|
autoPaddingEnabled: false
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiEffect {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: root.useNextForEffect ? srcNext : srcDummy
|
||||||
|
visible: nextWallpaper.source !== "" && root.useNextForEffect
|
||||||
|
blurEnabled: true
|
||||||
|
blur: 0.8
|
||||||
|
blurMax: 75
|
||||||
|
opacity: root.transitionProgress
|
||||||
|
autoPaddingEnabled: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
id: transitionAnimation
|
||||||
|
target: root
|
||||||
|
property: "transitionProgress"
|
||||||
|
from: 0.0
|
||||||
|
to: 1.0
|
||||||
|
duration: 1000
|
||||||
|
easing.type: Easing.InOutCubic
|
||||||
|
onFinished: {
|
||||||
|
if (nextWallpaper.source && nextWallpaper.status === Image.Ready)
|
||||||
|
currentWallpaper.source = nextWallpaper.source;
|
||||||
|
root.useNextForEffect = false;
|
||||||
|
nextWallpaper.source = "";
|
||||||
|
root.transitionProgress = 0.0;
|
||||||
|
root.effectActive = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user