diff --git a/Commons/Settings.qml b/Commons/Settings.qml index df60967..59f78e0 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -170,6 +170,7 @@ Singleton { property int randomIntervalSec: 300 // 5 min property int transitionDuration: 1500 // 1500 ms property string transitionType: "random" + property real transitionEdgeSmoothness: 0.05 property list monitors: [] } diff --git a/Modules/Background/Background.qml b/Modules/Background/Background.qml index 780e17b..df13fc6 100644 --- a/Modules/Background/Background.qml +++ b/Modules/Background/Background.qml @@ -21,17 +21,20 @@ Variants { property bool firstWallpaper: true property string transitionType: 'fade' property bool transitioning: false - property real transitionProgress: 0.0 + property real transitionProgress: 0 + property real edgeSmoothness: Settings.data.wallpaper.transitionEdgeSmoothness readonly property var allTransitions: WallpaperService.allTransitions // Wipe direction: 0=left, 1=right, 2=up, 3=down property real wipeDirection: 0 - property real wipeSmoothness: 0.05 // Disc property real discCenterX: 0.5 property real discCenterY: 0.5 - property real discSmoothness: 0.05 + + // Stripe + property real stripesCount: 16 + property real stripesAngle: 0 // External state management property string servicedWallpaper: WallpaperService.getWallpaper(modelData.name) @@ -58,7 +61,8 @@ Variants { transitionType = 'fade' } - //Logger.log("Background", "Using transition:", transitionType) + Logger.log("Background", "New wallpaper: ", servicedWallpaper, "On:", modelData.name, "Transition:", transitionType) + switch (transitionType) { case "none": setWallpaperImmediate(servicedWallpaper) @@ -84,6 +88,11 @@ Variants { discCenterY = Math.random() setWallpaperWithTransition(servicedWallpaper) break + case "stripes": + stripesCount = Math.round(Math.random() * 24 + 2) + stripesAngle = Math.random() * 360 + setWallpaperWithTransition(servicedWallpaper) + break default: setWallpaperWithTransition(servicedWallpaper) break @@ -134,7 +143,7 @@ Variants { property variant source1: currentWallpaper property variant source2: nextWallpaper - property real fade: transitionProgress + property real progress: root.transitionProgress fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/wp_fade.frag.qsb") } @@ -146,9 +155,10 @@ Variants { property variant source1: currentWallpaper property variant source2: nextWallpaper - property real progress: transitionProgress - property real direction: wipeDirection - property real smoothness: wipeSmoothness + property real progress: root.transitionProgress + property real smoothness: root.edgeSmoothness + property real direction: root.wipeDirection + fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/wp_wipe.frag.qsb") } @@ -160,14 +170,32 @@ Variants { property variant source1: currentWallpaper property variant source2: nextWallpaper - property real progress: transitionProgress - property real centerX: discCenterX - property real centerY: discCenterY - property real smoothness: discSmoothness - property real aspectRatio: width / height + property real progress: root.transitionProgress + property real smoothness: root.edgeSmoothness + property real aspectRatio: root.width / root.height + property real centerX: root.discCenterX + property real centerY: root.discCenterY + fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/wp_disc.frag.qsb") } + // Diagonal stripes transition shader + ShaderEffect { + id: stripesShader + anchors.fill: parent + visible: transitionType === 'stripes' + + property variant source1: currentWallpaper + property variant source2: nextWallpaper + property real progress: root.transitionProgress + property real smoothness: root.edgeSmoothness + property real aspectRatio: root.width / root.height + property real stripeCount: root.stripesCount + property real angle: root.stripesAngle + + fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/wp_stripes.frag.qsb") + } + // Animation for the transition progress NumberAnimation { id: transitionAnimation @@ -176,7 +204,7 @@ Variants { from: 0.0 to: 1.0 duration: Settings.data.wallpaper.transitionDuration ?? 1000 - easing.type: transitionType.startsWith('wipe_') ? Easing.InOutCubic : Easing.OutQuad + easing.type: Easing.InOutCubic //transitionType.startsWith('wipe_') ? Easing.InOutCubic : Easing.OutQuad onFinished: { // Swap images after transition completes currentWallpaper.source = nextWallpaper.source diff --git a/Modules/SettingsPanel/Tabs/WallpaperTab.qml b/Modules/SettingsPanel/Tabs/WallpaperTab.qml index 546a4ca..71dd82e 100644 --- a/Modules/SettingsPanel/Tabs/WallpaperTab.qml +++ b/Modules/SettingsPanel/Tabs/WallpaperTab.qml @@ -88,47 +88,14 @@ ColumnLayout { // Random Wallpaper NToggle { label: "Random Wallpaper" - description: "Automatically select random wallpapers from the folder." + description: "Schedule random wallpaper changes at regular intervals." checked: Settings.data.wallpaper.randomEnabled onToggled: checked => Settings.data.wallpaper.randomEnabled = checked } - // Transition Type - NComboBox { - label: "Transition Type" - description: "Animation type when switching between wallpapers." - model: WallpaperService.transitionsModel - currentKey: Settings.data.wallpaper.transitionType - onSelected: key => Settings.data.wallpaper.transitionType = key - } - - // Transition Duration - ColumnLayout { - NLabel { - label: "Transition Duration" - description: "Duration of transition animations in seconds." - } - - RowLayout { - spacing: Style.marginL * scaling - NSlider { - Layout.fillWidth: true - from: 100 - to: 5000 - stepSize: 100 - value: Settings.data.wallpaper.transitionDuration - onMoved: Settings.data.wallpaper.transitionDuration = value - cutoutColor: Color.mSurface - } - NText { - text: (Settings.data.wallpaper.transitionDuration / 1000).toFixed(2) + "s" - Layout.alignment: Qt.AlignVCenter | Qt.AlignRight - } - } - } - - // Interval (slider + H:M inputs) + // Interval ColumnLayout { + visible: Settings.data.wallpaper.randomEnabled RowLayout { NLabel { label: "Wallpaper Interval" @@ -224,6 +191,66 @@ ColumnLayout { } } } + + // Transition Type + NComboBox { + label: "Transition Type" + description: "Animation type when switching between wallpapers." + model: WallpaperService.transitionsModel + currentKey: Settings.data.wallpaper.transitionType + onSelected: key => Settings.data.wallpaper.transitionType = key + } + + // Transition Duration + ColumnLayout { + NLabel { + label: "Transition Duration" + description: "Duration of transition animations in seconds." + } + + RowLayout { + spacing: Style.marginL * scaling + NSlider { + Layout.fillWidth: true + from: 100 + to: 5000 + stepSize: 100 + value: Settings.data.wallpaper.transitionDuration + onMoved: Settings.data.wallpaper.transitionDuration = value + cutoutColor: Color.mSurface + } + NText { + text: (Settings.data.wallpaper.transitionDuration / 1000).toFixed(2) + "s" + Layout.alignment: Qt.AlignVCenter | Qt.AlignRight + } + } + } + + // Edge Smoothness + ColumnLayout { + NLabel { + label: "Transition Edge Smoothness" + description: "Duration of transition animations in seconds." + } + + RowLayout { + spacing: Style.marginL * scaling + NSlider { + Layout.fillWidth: true + from: 0.0 + to: 1.0 + value: Settings.data.wallpaper.transitionEdgeSmoothness + onMoved: Settings.data.wallpaper.transitionEdgeSmoothness = value + cutoutColor: Color.mSurface + } + NText { + text: Math.round(Settings.data.wallpaper.transitionEdgeSmoothness * 100) + "%" + Layout.alignment: Qt.AlignVCenter | Qt.AlignRight + } + } + } + + } // Reusable component for interval preset chips diff --git a/Services/WallpaperService.qml b/Services/WallpaperService.qml index 472e317..403153a 100644 --- a/Services/WallpaperService.qml +++ b/Services/WallpaperService.qml @@ -31,6 +31,10 @@ Singleton { key: "disc" name: "Disc" } + ListElement { + key: "stripes" + name: "Stripes" + } ListElement { key: "wipe_left" name: "Wipe Left" @@ -149,7 +153,7 @@ Singleton { return } - Logger.log("Wallpaper", "setWallpaper on", screenName, ": ", path) + //Logger.log("Wallpaper", "setWallpaper on", screenName, ": ", path) var wallpaperChanged = false diff --git a/Shaders/frag/wp_disc.frag b/Shaders/frag/wp_disc.frag index 292ce59..4bde549 100644 --- a/Shaders/frag/wp_disc.frag +++ b/Shaders/frag/wp_disc.frag @@ -1,3 +1,4 @@ +// ===== wp_disc.frag ===== #version 450 layout(location = 0) in vec2 qt_TexCoord0; @@ -12,7 +13,7 @@ layout(std140, binding = 0) uniform buf { float progress; // Transition progress (0.0 to 1.0) float centerX; // X coordinate of disc center (0.0 to 1.0) float centerY; // Y coordinate of disc center (0.0 to 1.0) - float smoothness; // Edge smoothness (0.01 to 0.5, default 0.05) + float smoothness; // Edge smoothness (0.0 to 1.0, 0=sharp, 1=very smooth) float aspectRatio; // Width / Height of the screen } ubuf; @@ -21,6 +22,10 @@ void main() { vec4 color1 = texture(source1, uv); // Current (old) wallpaper vec4 color2 = texture(source2, uv); // Next (new) wallpaper + // Map smoothness from 0.0-1.0 to 0.001-0.5 range + // Using a non-linear mapping for better control + float mappedSmoothness = mix(0.001, 0.5, ubuf.smoothness * ubuf.smoothness); + // Adjust UV coordinates to compensate for aspect ratio // This makes distances circular instead of elliptical vec2 adjustedUV = vec2(uv.x * ubuf.aspectRatio, uv.y); @@ -39,7 +44,7 @@ void main() { // Scale progress to cover the maximum distance // Add extra range for smoothness to ensure complete coverage // Adjust smoothness for aspect ratio to maintain consistent visual appearance - float adjustedSmoothness = ubuf.smoothness * max(1.0, ubuf.aspectRatio); + float adjustedSmoothness = mappedSmoothness * max(1.0, ubuf.aspectRatio); float radius = ubuf.progress * (maxDist + adjustedSmoothness); // Use smoothstep for a smooth edge transition @@ -49,4 +54,4 @@ void main() { fragColor = mix(color2, color1, factor); fragColor *= ubuf.qt_Opacity; -} \ No newline at end of file +} diff --git a/Shaders/frag/wp_fade.frag b/Shaders/frag/wp_fade.frag index 10c3a63..24917a5 100644 --- a/Shaders/frag/wp_fade.frag +++ b/Shaders/frag/wp_fade.frag @@ -7,16 +7,13 @@ layout(binding = 2) uniform sampler2D source2; layout(std140, binding = 0) uniform buf { mat4 qt_Matrix; float qt_Opacity; - float fade; + float progress; }; void main() { vec4 color1 = texture(source1, qt_TexCoord0); vec4 color2 = texture(source2, qt_TexCoord0); - // Smooth cross-fade using smoothstep for better visual quality - float smoothFade = smoothstep(0.0, 1.0, fade); - - // Mix the two textures based on fade value - fragColor = mix(color1, color2, smoothFade) * qt_Opacity; + // Mix the two textures based on progress value + fragColor = mix(color1, color2, progress) * qt_Opacity; } \ No newline at end of file diff --git a/Shaders/frag/wp_stripes.frag b/Shaders/frag/wp_stripes.frag new file mode 100644 index 0000000..7d12b30 --- /dev/null +++ b/Shaders/frag/wp_stripes.frag @@ -0,0 +1,126 @@ +// ===== wp_stripes.frag ===== +#version 450 + +layout(location = 0) in vec2 qt_TexCoord0; +layout(location = 0) out vec4 fragColor; + +layout(binding = 1) uniform sampler2D source1; // Current wallpaper +layout(binding = 2) uniform sampler2D source2; // Next wallpaper + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; + float progress; // Transition progress (0.0 to 1.0) + float stripeCount; // Number of stripes (default 12.0) + float angle; // Angle of stripes in degrees (default 30.0) + float smoothness; // Edge smoothness (0.0 to 1.0, 0=sharp, 1=very smooth) + float aspectRatio; // Width / Height of the screen +} ubuf; + +void main() { + vec2 uv = qt_TexCoord0; + vec4 color1 = texture(source1, uv); // Current (old) wallpaper + vec4 color2 = texture(source2, uv); // Next (new) wallpaper + + // Map smoothness from 0.0-1.0 to 0.001-0.1 range + // Using a non-linear mapping for better control at low values + float mappedSmoothness = mix(0.001, 0.1, ubuf.smoothness * ubuf.smoothness); + + // Use values directly without forcing defaults + float stripes = (ubuf.stripeCount > 0.0) ? ubuf.stripeCount : 12.0; + float angleRad = radians(ubuf.angle); + float edgeSmooth = mappedSmoothness; + + // Create a coordinate system for stripes based on angle + // At 0°: vertical stripes (divide by x) + // At 45°: diagonal stripes + // At 90°: horizontal stripes (divide by y) + + // Transform coordinates based on angle + float cosA = cos(angleRad); + float sinA = sin(angleRad); + + // Project the UV position onto the stripe direction + // This gives us the position along the stripe direction + float stripeCoord = uv.x * cosA + uv.y * sinA; + + // Perpendicular coordinate (for edge movement) + float perpCoord = -uv.x * sinA + uv.y * cosA; + + // Calculate the range of perpCoord based on angle + // This determines how far edges need to travel to fully cover the screen + float minPerp = min(min(0.0 * -sinA + 0.0 * cosA, 1.0 * -sinA + 0.0 * cosA), + min(0.0 * -sinA + 1.0 * cosA, 1.0 * -sinA + 1.0 * cosA)); + float maxPerp = max(max(0.0 * -sinA + 0.0 * cosA, 1.0 * -sinA + 0.0 * cosA), + max(0.0 * -sinA + 1.0 * cosA, 1.0 * -sinA + 1.0 * cosA)); + + // Determine which stripe we're in + float stripePos = stripeCoord * stripes; + int stripeIndex = int(floor(stripePos)); + + // Determine if this is an odd or even stripe + bool isOddStripe = (stripeIndex % 2) == 1; + + // Calculate the progress for this specific stripe with wave delay + // Use absolute stripe position for consistent delay across all stripes + float normalizedStripePos = clamp(stripePos / stripes, 0.0, 1.0); + + // Reduced delay factor and better scaling to match other shaders' timing + float maxDelay = 0.15; // Maximum delay for the last stripe + float stripeDelay = normalizedStripePos * maxDelay; + + // Ensure all stripes complete when progress reaches 1.0 + // without making the overall animation appear faster + float stripeProgress = clamp((ubuf.progress - stripeDelay) / (1.0 - maxDelay), 0.0, 1.0); + + // Apply smooth easing + stripeProgress = smoothstep(0.0, 1.0, stripeProgress); + + // Use the perpendicular coordinate for edge comparison + float yPos = perpCoord; + + // Calculate edge position for this stripe + // Use the actual perpendicular coordinate range for this angle + float perpRange = maxPerp - minPerp; + float margin = edgeSmooth * 2.0; // Simplified margin calculation + float edgePosition; + if (isOddStripe) { + // Odd stripes: edge moves from max to min + edgePosition = maxPerp + margin - stripeProgress * (perpRange + margin * 2.0); + } else { + // Even stripes: edge moves from min to max + edgePosition = minPerp - margin + stripeProgress * (perpRange + margin * 2.0); + } + + // Determine which wallpaper to show based on rotated position + float mask; + if (isOddStripe) { + // Odd stripes reveal new wallpaper from bottom + mask = smoothstep(edgePosition - edgeSmooth, edgePosition + edgeSmooth, yPos); + } else { + // Even stripes reveal new wallpaper from top + mask = 1.0 - smoothstep(edgePosition - edgeSmooth, edgePosition + edgeSmooth, yPos); + } + + // Mix the wallpapers + fragColor = mix(color1, color2, mask); + + // Force exact values at start and end to prevent any bleed-through + if (ubuf.progress <= 0.0) { + fragColor = color1; // Only show old wallpaper at start + } else if (ubuf.progress >= 1.0) { + fragColor = color2; // Only show new wallpaper at end + } else { + // Add manga-style edge shadow only during transition + float edgeDist = abs(yPos - edgePosition); + float shadowStrength = 1.0 - smoothstep(0.0, edgeSmooth * 2.5, edgeDist); + shadowStrength *= 0.2 * (1.0 - abs(stripeProgress - 0.5) * 2.0); + fragColor.rgb *= (1.0 - shadowStrength); + + // Add slight vignette during transition for dramatic effect + float vignette = 1.0 - ubuf.progress * 0.1 * (1.0 - abs(stripeProgress - 0.5) * 2.0); + fragColor.rgb *= vignette; + } + + fragColor *= ubuf.qt_Opacity; +} \ No newline at end of file diff --git a/Shaders/frag/wp_wipe.frag b/Shaders/frag/wp_wipe.frag index f6db80e..10b948a 100644 --- a/Shaders/frag/wp_wipe.frag +++ b/Shaders/frag/wp_wipe.frag @@ -1,3 +1,5 @@ + +// ===== wp_wipe.frag ===== #version 450 layout(location = 0) in vec2 qt_TexCoord0; @@ -11,7 +13,7 @@ layout(std140, binding = 0) uniform buf { float qt_Opacity; float progress; // Transition progress (0.0 to 1.0) float direction; // 0=left, 1=right, 2=up, 3=down - float smoothness; // Edge smoothness (0.01 to 0.5, default 0.05) + float smoothness; // Edge smoothness (0.0 to 1.0, 0=sharp, 1=very smooth) } ubuf; void main() { @@ -19,37 +21,41 @@ void main() { vec4 color1 = texture(source1, uv); // Current (old) wallpaper vec4 color2 = texture(source2, uv); // Next (new) wallpaper + // Map smoothness from 0.0-1.0 to 0.001-0.5 range + // Using a non-linear mapping for better control + float mappedSmoothness = mix(0.001, 0.5, ubuf.smoothness * ubuf.smoothness); + float edge = 0.0; float factor = 0.0; // Extend the progress range to account for smoothness // This ensures the transition completes fully at the edges - float extendedProgress = ubuf.progress * (1.0 + 2.0 * ubuf.smoothness) - ubuf.smoothness; + float extendedProgress = ubuf.progress * (1.0 + 2.0 * mappedSmoothness) - mappedSmoothness; // Calculate edge position based on direction // As progress goes from 0 to 1, we reveal source2 (new wallpaper) if (ubuf.direction < 0.5) { // Wipe from right to left (new image enters from right) edge = 1.0 - extendedProgress; - factor = smoothstep(edge - ubuf.smoothness, edge + ubuf.smoothness, uv.x); + factor = smoothstep(edge - mappedSmoothness, edge + mappedSmoothness, uv.x); fragColor = mix(color1, color2, factor); } else if (ubuf.direction < 1.5) { // Wipe from left to right (new image enters from left) edge = extendedProgress; - factor = smoothstep(edge - ubuf.smoothness, edge + ubuf.smoothness, uv.x); + factor = smoothstep(edge - mappedSmoothness, edge + mappedSmoothness, uv.x); fragColor = mix(color2, color1, factor); } else if (ubuf.direction < 2.5) { // Wipe from bottom to top (new image enters from bottom) edge = 1.0 - extendedProgress; - factor = smoothstep(edge - ubuf.smoothness, edge + ubuf.smoothness, uv.y); + factor = smoothstep(edge - mappedSmoothness, edge + mappedSmoothness, uv.y); fragColor = mix(color1, color2, factor); } else { // Wipe from top to bottom (new image enters from top) edge = extendedProgress; - factor = smoothstep(edge - ubuf.smoothness, edge + ubuf.smoothness, uv.y); + factor = smoothstep(edge - mappedSmoothness, edge + mappedSmoothness, uv.y); fragColor = mix(color2, color1, factor); } diff --git a/Shaders/qsb/wp_disc.frag.qsb b/Shaders/qsb/wp_disc.frag.qsb index 485186e..9c66bfb 100644 Binary files a/Shaders/qsb/wp_disc.frag.qsb and b/Shaders/qsb/wp_disc.frag.qsb differ diff --git a/Shaders/qsb/wp_fade.frag.qsb b/Shaders/qsb/wp_fade.frag.qsb index e06417c..89641d8 100644 Binary files a/Shaders/qsb/wp_fade.frag.qsb and b/Shaders/qsb/wp_fade.frag.qsb differ diff --git a/Shaders/qsb/wp_stripes.frag.qsb b/Shaders/qsb/wp_stripes.frag.qsb new file mode 100644 index 0000000..14cfb3e Binary files /dev/null and b/Shaders/qsb/wp_stripes.frag.qsb differ diff --git a/Shaders/qsb/wp_wipe.frag.qsb b/Shaders/qsb/wp_wipe.frag.qsb index e1977ce..478c27d 100644 Binary files a/Shaders/qsb/wp_wipe.frag.qsb and b/Shaders/qsb/wp_wipe.frag.qsb differ