mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-08-06 21:48:30 -04:00
wallpaper: more resilience to updatesEnabled missing expose/update
events
This commit is contained in:
@@ -68,6 +68,8 @@ func GetQtLoggingRules() string {
|
|||||||
level = "info"
|
level = "info"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// scene carries QML engine warnings (e.g. QQuickImage "Cannot open" cache
|
||||||
|
// probes); suppressed except at debug level
|
||||||
var rules []string
|
var rules []string
|
||||||
switch strings.ToLower(level) {
|
switch strings.ToLower(level) {
|
||||||
case "fatal":
|
case "fatal":
|
||||||
@@ -75,13 +77,13 @@ func GetQtLoggingRules() string {
|
|||||||
case "error":
|
case "error":
|
||||||
rules = []string{"*.debug=false", "*.info=false", "*.warning=false"}
|
rules = []string{"*.debug=false", "*.info=false", "*.warning=false"}
|
||||||
case "warn", "warning":
|
case "warn", "warning":
|
||||||
rules = []string{"*.debug=false", "*.info=false"}
|
rules = []string{"*.debug=false", "*.info=false", "scene.warning=false"}
|
||||||
case "info":
|
case "info":
|
||||||
rules = []string{"*.debug=false"}
|
rules = []string{"*.debug=false", "scene.warning=false"}
|
||||||
case "debug":
|
case "debug":
|
||||||
return ""
|
return ""
|
||||||
default:
|
default:
|
||||||
rules = []string{"*.debug=false"}
|
rules = []string{"*.debug=false", "scene.warning=false"}
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.Join(rules, ";")
|
return strings.Join(rules, ";")
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Effects
|
import Quickshell.Widgets
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
@@ -98,25 +98,25 @@ StyledRect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
property string _videoThumb: ""
|
property string _videoThumb: ""
|
||||||
|
property bool _thumbGenAttempted: false
|
||||||
|
|
||||||
|
// Probe the thumbnail optimistically; Image.Error triggers generation
|
||||||
onVideoThumbnailPathChanged: {
|
onVideoThumbnailPathChanged: {
|
||||||
_videoThumb = "";
|
_thumbGenAttempted = false;
|
||||||
if (!videoThumbnailPath)
|
_videoThumb = videoThumbnailPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateVideoThumbnail() {
|
||||||
|
if (_thumbGenAttempted)
|
||||||
return;
|
return;
|
||||||
|
_thumbGenAttempted = true;
|
||||||
|
_videoThumb = "";
|
||||||
const thumbPath = videoThumbnailPath;
|
const thumbPath = videoThumbnailPath;
|
||||||
const thumbDir = _xdgCacheHome + "/thumbnails/" + _thumbnailSize;
|
const thumbDir = _xdgCacheHome + "/thumbnails/" + _thumbnailSize;
|
||||||
const size = _thumbnailPx;
|
const script = "mkdir -p \"$1\" && ffmpegthumbnailer -i \"$2\" -o \"$3\" -s " + _thumbnailPx + " -f";
|
||||||
const fp = delegateRoot.filePath;
|
Proc.runCommand(null, ["sh", "-c", script, "thumb", thumbDir, delegateRoot.filePath, thumbPath], function (output, exitCode) {
|
||||||
Paths.mkdir(thumbDir);
|
if (exitCode === 0)
|
||||||
Proc.runCommand(null, ["test", "-f", thumbPath], function (output, exitCode) {
|
|
||||||
if (exitCode === 0) {
|
|
||||||
_videoThumb = thumbPath;
|
_videoThumb = thumbPath;
|
||||||
} else {
|
|
||||||
Proc.runCommand(null, ["ffmpegthumbnailer", "-i", fp, "-o", thumbPath, "-s", String(size), "-f"], function (output, exitCode) {
|
|
||||||
if (exitCode === 0)
|
|
||||||
_videoThumb = thumbPath;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,75 +160,52 @@ StyledRect {
|
|||||||
height: weMode ? 165 : (iconSizes[iconSizeIndex] - 8)
|
height: weMode ? 165 : (iconSizes[iconSizeIndex] - 8)
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
Image {
|
ClippingRectangle {
|
||||||
id: gridPreviewImage
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 2
|
anchors.margins: 2
|
||||||
property var weExtensions: [".jpg", ".jpeg", ".png", ".webp", ".gif", ".bmp", ".tga", ".jxl", ".avif", ".heif", ".exr"]
|
radius: Theme.cornerRadius
|
||||||
property int weExtIndex: 0
|
color: "transparent"
|
||||||
property string imagePath: {
|
|
||||||
if (weMode && delegateRoot.fileIsDir)
|
|
||||||
return delegateRoot.filePath + "/preview" + weExtensions[weExtIndex];
|
|
||||||
if (_videoThumb)
|
|
||||||
return _videoThumb;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
source: imagePath ? "file://" + imagePath.split('/').map(s => encodeURIComponent(s)).join('/') : ""
|
|
||||||
onStatusChanged: {
|
|
||||||
if (weMode && delegateRoot.fileIsDir && status === Image.Error) {
|
|
||||||
if (weExtIndex < weExtensions.length - 1) {
|
|
||||||
weExtIndex++;
|
|
||||||
} else {
|
|
||||||
imagePath = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fillMode: Image.PreserveAspectCrop
|
|
||||||
sourceSize.width: weMode ? 225 : iconSizes[iconSizeIndex]
|
|
||||||
sourceSize.height: weMode ? 225 : iconSizes[iconSizeIndex]
|
|
||||||
asynchronous: true
|
|
||||||
visible: false
|
|
||||||
}
|
|
||||||
|
|
||||||
CachingImage {
|
Image {
|
||||||
anchors.fill: parent
|
id: gridPreviewImage
|
||||||
anchors.margins: 2
|
|
||||||
imagePath: !delegateRoot.fileIsDir && isImage ? delegateRoot.filePath : ""
|
|
||||||
maxCacheSize: 256
|
|
||||||
visible: !delegateRoot.fileIsDir && isImage
|
|
||||||
layer.enabled: true
|
|
||||||
layer.effect: MultiEffect {
|
|
||||||
maskEnabled: true
|
|
||||||
maskSource: gridImageMask
|
|
||||||
maskThresholdMin: 0.5
|
|
||||||
maskSpreadAtMin: 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MultiEffect {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: 2
|
|
||||||
source: gridPreviewImage
|
|
||||||
maskEnabled: true
|
|
||||||
maskSource: gridImageMask
|
|
||||||
visible: gridPreviewImage.status === Image.Ready && ((!delegateRoot.fileIsDir && isVideo) || (weMode && delegateRoot.fileIsDir))
|
|
||||||
maskThresholdMin: 0.5
|
|
||||||
maskSpreadAtMin: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: gridImageMask
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: 2
|
|
||||||
layer.enabled: true
|
|
||||||
layer.smooth: true
|
|
||||||
visible: false
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: Theme.cornerRadius
|
property var weExtensions: [".jpg", ".jpeg", ".png", ".webp", ".gif", ".bmp", ".tga", ".jxl", ".avif", ".heif", ".exr"]
|
||||||
color: "black"
|
property int weExtIndex: 0
|
||||||
antialiasing: true
|
property string imagePath: {
|
||||||
|
if (weMode && delegateRoot.fileIsDir)
|
||||||
|
return delegateRoot.filePath + "/preview" + weExtensions[weExtIndex];
|
||||||
|
if (_videoThumb)
|
||||||
|
return _videoThumb;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
source: imagePath ? "file://" + imagePath.split('/').map(s => encodeURIComponent(s)).join('/') : ""
|
||||||
|
onStatusChanged: {
|
||||||
|
if (status !== Image.Error)
|
||||||
|
return;
|
||||||
|
if (weMode && delegateRoot.fileIsDir) {
|
||||||
|
if (weExtIndex < weExtensions.length - 1) {
|
||||||
|
weExtIndex++;
|
||||||
|
} else {
|
||||||
|
imagePath = "";
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_videoThumb)
|
||||||
|
generateVideoThumbnail();
|
||||||
|
}
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
sourceSize.width: weMode ? 225 : iconSizes[iconSizeIndex]
|
||||||
|
sourceSize.height: weMode ? 225 : iconSizes[iconSizeIndex]
|
||||||
|
asynchronous: true
|
||||||
|
visible: status === Image.Ready && ((!delegateRoot.fileIsDir && isVideo) || (weMode && delegateRoot.fileIsDir))
|
||||||
|
}
|
||||||
|
|
||||||
|
CachingImage {
|
||||||
|
anchors.fill: parent
|
||||||
|
imagePath: !delegateRoot.fileIsDir && isImage ? delegateRoot.filePath : ""
|
||||||
|
maxCacheSize: 256
|
||||||
|
animate: false
|
||||||
|
visible: !delegateRoot.fileIsDir && isImage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Effects
|
import Quickshell.Widgets
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
@@ -95,23 +95,25 @@ StyledRect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
property string _videoThumb: ""
|
property string _videoThumb: ""
|
||||||
|
property bool _thumbGenAttempted: false
|
||||||
|
|
||||||
|
// Probe the thumbnail optimistically; Image.Error triggers generation
|
||||||
onVideoThumbnailPathChanged: {
|
onVideoThumbnailPathChanged: {
|
||||||
_videoThumb = "";
|
_thumbGenAttempted = false;
|
||||||
if (!videoThumbnailPath)
|
_videoThumb = videoThumbnailPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateVideoThumbnail() {
|
||||||
|
if (_thumbGenAttempted)
|
||||||
return;
|
return;
|
||||||
|
_thumbGenAttempted = true;
|
||||||
|
_videoThumb = "";
|
||||||
const thumbPath = videoThumbnailPath;
|
const thumbPath = videoThumbnailPath;
|
||||||
const fp = listDelegateRoot.filePath;
|
const thumbDir = _xdgCacheHome + "/thumbnails/normal";
|
||||||
Paths.mkdir(_xdgCacheHome + "/thumbnails/normal");
|
const script = "mkdir -p \"$1\" && ffmpegthumbnailer -i \"$2\" -o \"$3\" -s 128 -f";
|
||||||
Proc.runCommand(null, ["test", "-f", thumbPath], function (output, exitCode) {
|
Proc.runCommand(null, ["sh", "-c", script, "thumb", thumbDir, listDelegateRoot.filePath, thumbPath], function (output, exitCode) {
|
||||||
if (exitCode === 0) {
|
if (exitCode === 0)
|
||||||
_videoThumb = thumbPath;
|
_videoThumb = thumbPath;
|
||||||
} else {
|
|
||||||
Proc.runCommand(null, ["ffmpegthumbnailer", "-i", fp, "-o", thumbPath, "-s", "128", "-f"], function (output, exitCode) {
|
|
||||||
if (exitCode === 0)
|
|
||||||
_videoThumb = thumbPath;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,54 +167,33 @@ StyledRect {
|
|||||||
height: 28
|
height: 28
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
Image {
|
ClippingRectangle {
|
||||||
id: listPreviewImage
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
property string imagePath: _videoThumb
|
radius: Theme.cornerRadius
|
||||||
source: imagePath ? "file://" + imagePath.split('/').map(s => encodeURIComponent(s)).join('/') : ""
|
color: "transparent"
|
||||||
fillMode: Image.PreserveAspectCrop
|
|
||||||
sourceSize.width: 32
|
|
||||||
sourceSize.height: 32
|
|
||||||
asynchronous: true
|
|
||||||
visible: false
|
|
||||||
}
|
|
||||||
|
|
||||||
CachingImage {
|
Image {
|
||||||
anchors.fill: parent
|
id: listPreviewImage
|
||||||
imagePath: !listDelegateRoot.fileIsDir && isImage ? listDelegateRoot.filePath : ""
|
|
||||||
maxCacheSize: 256
|
|
||||||
visible: !listDelegateRoot.fileIsDir && isImage
|
|
||||||
layer.enabled: true
|
|
||||||
layer.effect: MultiEffect {
|
|
||||||
maskEnabled: true
|
|
||||||
maskSource: listImageMask
|
|
||||||
maskThresholdMin: 0.5
|
|
||||||
maskSpreadAtMin: 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MultiEffect {
|
|
||||||
anchors.fill: parent
|
|
||||||
source: listPreviewImage
|
|
||||||
maskEnabled: true
|
|
||||||
maskSource: listImageMask
|
|
||||||
visible: listPreviewImage.status === Image.Ready && !listDelegateRoot.fileIsDir && isVideo
|
|
||||||
maskThresholdMin: 0.5
|
|
||||||
maskSpreadAtMin: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: listImageMask
|
|
||||||
anchors.fill: parent
|
|
||||||
layer.enabled: true
|
|
||||||
layer.smooth: true
|
|
||||||
visible: false
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: Theme.cornerRadius
|
property string imagePath: _videoThumb
|
||||||
color: "black"
|
source: imagePath ? "file://" + imagePath.split('/').map(s => encodeURIComponent(s)).join('/') : ""
|
||||||
antialiasing: true
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
sourceSize.width: 32
|
||||||
|
sourceSize.height: 32
|
||||||
|
asynchronous: true
|
||||||
|
visible: status === Image.Ready && !listDelegateRoot.fileIsDir && isVideo
|
||||||
|
onStatusChanged: {
|
||||||
|
if (status === Image.Error && _videoThumb)
|
||||||
|
generateVideoThumbnail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CachingImage {
|
||||||
|
anchors.fill: parent
|
||||||
|
imagePath: !listDelegateRoot.fileIsDir && isImage ? listDelegateRoot.filePath : ""
|
||||||
|
maxCacheSize: 256
|
||||||
|
animate: false
|
||||||
|
visible: !listDelegateRoot.fileIsDir && isImage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -108,16 +108,48 @@ Variants {
|
|||||||
function invalidate() {
|
function invalidate() {
|
||||||
_settleFrames = 3;
|
_settleFrames = 3;
|
||||||
backingWindow?.update();
|
backingWindow?.update();
|
||||||
|
if (!_wedgeBounced)
|
||||||
|
wedgeWatchdog.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
onRenderActiveChanged: invalidate()
|
onRenderActiveChanged: invalidate()
|
||||||
onBackingWindowChanged: invalidate()
|
onBackingWindowChanged: invalidate()
|
||||||
|
|
||||||
|
// Same wedge recovery as WallpaperBackground
|
||||||
|
property bool _wedgeBounced: false
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: wedgeWatchdog
|
||||||
|
interval: 3000
|
||||||
|
repeat: false
|
||||||
|
onTriggered: {
|
||||||
|
if (!root.backingWindow || !blurWallpaperWindow.visible || IdleService.isShellLocked)
|
||||||
|
return;
|
||||||
|
log.warn("no frame swapped on", modelData.name, "since last invalidate, re-attaching surface");
|
||||||
|
root._wedgeBounced = true;
|
||||||
|
surfaceReattach.restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: surfaceReattach
|
||||||
|
interval: 0
|
||||||
|
repeat: false
|
||||||
|
onTriggered: {
|
||||||
|
blurWallpaperWindow.visible = false;
|
||||||
|
Qt.callLater(() => {
|
||||||
|
blurWallpaperWindow.visible = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: root.backingWindow
|
target: root.backingWindow
|
||||||
function onFrameSwapped() {
|
function onFrameSwapped() {
|
||||||
if (root._settleFrames > 0)
|
if (root._settleFrames > 0)
|
||||||
root._settleFrames--;
|
root._settleFrames--;
|
||||||
|
root._wedgeBounced = false;
|
||||||
|
wedgeWatchdog.stop();
|
||||||
}
|
}
|
||||||
function onVisibleChanged() {
|
function onVisibleChanged() {
|
||||||
root.invalidate();
|
root.invalidate();
|
||||||
@@ -142,10 +174,29 @@ Variants {
|
|||||||
function onWallpaperFillModeChanged() {
|
function onWallpaperFillModeChanged() {
|
||||||
root.invalidate();
|
root.invalidate();
|
||||||
}
|
}
|
||||||
function onWallpaperBackgroundColorModeChanged() {
|
function onEffectiveWallpaperBackgroundColorChanged() {
|
||||||
root.invalidate();
|
root.invalidate();
|
||||||
}
|
}
|
||||||
function onWallpaperBackgroundCustomColorChanged() {
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: SessionData
|
||||||
|
function onMonitorWallpaperFillModesChanged() {
|
||||||
|
root.invalidate();
|
||||||
|
}
|
||||||
|
function onPerMonitorWallpaperChanged() {
|
||||||
|
root.invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Theme changes repaint DankBackdrop but nothing else wakes the render loop
|
||||||
|
Connections {
|
||||||
|
target: Theme
|
||||||
|
enabled: root.isColorSource || currentWallpaper.status === Image.Error
|
||||||
|
function onPrimaryChanged() {
|
||||||
|
root.invalidate();
|
||||||
|
}
|
||||||
|
function onBackgroundChanged() {
|
||||||
root.invalidate();
|
root.invalidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,6 +221,7 @@ Variants {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onSourceChanged: {
|
onSourceChanged: {
|
||||||
|
invalidate();
|
||||||
if (!source || source.startsWith("#")) {
|
if (!source || source.startsWith("#")) {
|
||||||
setWallpaperImmediate("");
|
setWallpaperImmediate("");
|
||||||
return;
|
return;
|
||||||
@@ -190,6 +242,7 @@ Variants {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setWallpaperImmediate(newSource) {
|
function setWallpaperImmediate(newSource) {
|
||||||
|
transitionDelayTimer.stop();
|
||||||
transitionAnimation.stop();
|
transitionAnimation.stop();
|
||||||
root.transitionProgress = 0.0;
|
root.transitionProgress = 0.0;
|
||||||
root.effectActive = false;
|
root.effectActive = false;
|
||||||
|
|||||||
@@ -141,16 +141,37 @@ Variants {
|
|||||||
function invalidate() {
|
function invalidate() {
|
||||||
_settleFrames = 3;
|
_settleFrames = 3;
|
||||||
backingWindow?.update();
|
backingWindow?.update();
|
||||||
|
if (!_wedgeBounced)
|
||||||
|
wedgeWatchdog.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
onRenderActiveChanged: invalidate()
|
onRenderActiveChanged: invalidate()
|
||||||
onBackingWindowChanged: invalidate()
|
onBackingWindowChanged: invalidate()
|
||||||
|
|
||||||
|
// No swap after a requested frame: dropped frame callback left the window
|
||||||
|
// unexposed (qtwayland mFrameCallbackTimedOut); only surface re-attach recovers.
|
||||||
|
property bool _wedgeBounced: false
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: wedgeWatchdog
|
||||||
|
interval: 3000
|
||||||
|
repeat: false
|
||||||
|
onTriggered: {
|
||||||
|
if (!root.backingWindow || !wallpaperWindow.visible || IdleService.isShellLocked)
|
||||||
|
return;
|
||||||
|
log.warn("no frame swapped on", modelData.name, "since last invalidate, re-attaching surface");
|
||||||
|
root._wedgeBounced = true;
|
||||||
|
surfaceReattach.restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: root.backingWindow
|
target: root.backingWindow
|
||||||
function onFrameSwapped() {
|
function onFrameSwapped() {
|
||||||
if (root._settleFrames > 0)
|
if (root._settleFrames > 0)
|
||||||
root._settleFrames--;
|
root._settleFrames--;
|
||||||
|
root._wedgeBounced = false;
|
||||||
|
wedgeWatchdog.stop();
|
||||||
}
|
}
|
||||||
function onVisibleChanged() {
|
function onVisibleChanged() {
|
||||||
root.invalidate();
|
root.invalidate();
|
||||||
@@ -176,10 +197,29 @@ Variants {
|
|||||||
function onWallpaperFillModeChanged() {
|
function onWallpaperFillModeChanged() {
|
||||||
root.invalidate();
|
root.invalidate();
|
||||||
}
|
}
|
||||||
function onWallpaperBackgroundColorModeChanged() {
|
function onEffectiveWallpaperBackgroundColorChanged() {
|
||||||
root.invalidate();
|
root.invalidate();
|
||||||
}
|
}
|
||||||
function onWallpaperBackgroundCustomColorChanged() {
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: SessionData
|
||||||
|
function onMonitorWallpaperFillModesChanged() {
|
||||||
|
root.invalidate();
|
||||||
|
}
|
||||||
|
function onPerMonitorWallpaperChanged() {
|
||||||
|
root.invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Theme changes repaint DankBackdrop but nothing else wakes the render loop
|
||||||
|
Connections {
|
||||||
|
target: Theme
|
||||||
|
enabled: root.isColorSource || currentWallpaper.status === Image.Error
|
||||||
|
function onPrimaryChanged() {
|
||||||
|
root.invalidate();
|
||||||
|
}
|
||||||
|
function onBackgroundChanged() {
|
||||||
root.invalidate();
|
root.invalidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -502,13 +542,13 @@ Variants {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onSourceChanged: {
|
onSourceChanged: {
|
||||||
|
invalidate();
|
||||||
if (!source || source.startsWith("#")) {
|
if (!source || source.startsWith("#")) {
|
||||||
setWallpaperImmediate("");
|
setWallpaperImmediate("");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
root.changePending = true;
|
root.changePending = true;
|
||||||
invalidate();
|
|
||||||
|
|
||||||
const formattedSource = source.startsWith("file://") ? source : encodeFileUrl(source);
|
const formattedSource = source.startsWith("file://") ? source : encodeFileUrl(source);
|
||||||
|
|
||||||
@@ -528,10 +568,14 @@ Variants {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setWallpaperImmediate(newSource) {
|
function setWallpaperImmediate(newSource) {
|
||||||
|
transitionDelayTimer.stop();
|
||||||
transitionAnimation.stop();
|
transitionAnimation.stop();
|
||||||
root.transitionProgress = 0.0;
|
root.transitionProgress = 0.0;
|
||||||
root.effectActive = false;
|
root.effectActive = false;
|
||||||
root.screenScale = CompositorService.getScreenScale(modelData);
|
root.screenScale = CompositorService.getScreenScale(modelData);
|
||||||
|
// No status change coming to clear the flag
|
||||||
|
if (!newSource || currentWallpaper.source.toString() === newSource)
|
||||||
|
root.changePending = false;
|
||||||
currentWallpaper.source = newSource;
|
currentWallpaper.source = newSource;
|
||||||
nextWallpaper.source = "";
|
nextWallpaper.source = "";
|
||||||
|
|
||||||
@@ -567,10 +611,14 @@ Variants {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function changeWallpaper(newPath, force) {
|
function changeWallpaper(newPath, force) {
|
||||||
if (!force && newPath === currentWallpaper.source)
|
if (!force && newPath === currentWallpaper.source.toString()) {
|
||||||
|
root.changePending = false;
|
||||||
return;
|
return;
|
||||||
if (!newPath || newPath.startsWith("#"))
|
}
|
||||||
|
if (!newPath || newPath.startsWith("#")) {
|
||||||
|
root.changePending = false;
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
root.screenScale = CompositorService.getScreenScale(modelData);
|
root.screenScale = CompositorService.getScreenScale(modelData);
|
||||||
if (root.transitioning || root.effectActive) {
|
if (root.transitioning || root.effectActive) {
|
||||||
root.pendingWallpaper = newPath;
|
root.pendingWallpaper = newPath;
|
||||||
|
|||||||
Reference in New Issue
Block a user