From f5b49842957b40bb92fc47153de2026cbbe0d81a Mon Sep 17 00:00:00 2001 From: LemmyCook Date: Fri, 29 Aug 2025 19:06:01 -0400 Subject: [PATCH] Wallpaper: swipe left/right/up/down --- Assets/Matugen/Matugen.qml | 3 +- Modules/Background/Background.qml | 88 +++++++++++++----- Services/WallpaperService.qml | 16 ++++ .../frag/{mix_images.frag => wp_fade.frag} | 0 Shaders/frag/wp_swipe.frag | 57 ++++++++++++ .../{mix_images.frag.qsb => wp_fade.frag.qsb} | Bin Shaders/qsb/wp_swipe.frag.qsb | Bin 0 -> 2629 bytes 7 files changed, 140 insertions(+), 24 deletions(-) rename Shaders/frag/{mix_images.frag => wp_fade.frag} (100%) create mode 100644 Shaders/frag/wp_swipe.frag rename Shaders/qsb/{mix_images.frag.qsb => wp_fade.frag.qsb} (100%) create mode 100644 Shaders/qsb/wp_swipe.frag.qsb diff --git a/Assets/Matugen/Matugen.qml b/Assets/Matugen/Matugen.qml index b6e2d2c..0d5c2b6 100644 --- a/Assets/Matugen/Matugen.qml +++ b/Assets/Matugen/Matugen.qml @@ -50,7 +50,8 @@ Singleton { lines.push("\n[templates.ghostty]") lines.push('input_path = "' + Quickshell.shellDir + '/Assets/Matugen/templates/ghostty.conf"') lines.push('output_path = "~/.config/ghostty/themes/noctalia"') - lines.push("post_hook = \"grep -q '^theme *= *' ~/.config/ghostty/config; and sed -i 's/^theme *= *.*/theme = noctalia/' ~/.config/ghostty/config; or echo 'theme = noctalia' >> ~/.config/ghostty/config\"") + lines.push( + "post_hook = \"grep -q '^theme *= *' ~/.config/ghostty/config; and sed -i 's/^theme *= *.*/theme = noctalia/' ~/.config/ghostty/config; or echo 'theme = noctalia' >> ~/.config/ghostty/config\"") } return lines.join("\n") + "\n" diff --git a/Modules/Background/Background.qml b/Modules/Background/Background.qml index 1911f34..3d2c058 100644 --- a/Modules/Background/Background.qml +++ b/Modules/Background/Background.qml @@ -18,9 +18,13 @@ Variants { id: root // Internal state management - property bool transitioning: false - property real fadeValue: 0.0 property bool firstWallpaper: true + property bool transitioning: false + property real transitionProgress: 0.0 + + // Swipe direction: 0=left, 1=right, 2=up, 3=down + property real swipeDirection: 0 + property real swipeSmoothness: 0.05 // External state management property string servicedWallpaper: WallpaperService.getWallpaper(modelData.name) @@ -34,10 +38,29 @@ Variants { return } - if (Settings.data.wallpaper.transitionType === 'fade') { - setWallpaperWithTransition(servicedWallpaper) - } else { - setWallpaperImmediate(servicedWallpaper) + switch (Settings.data.wallpaper.transitionType) { + case "none": + setWallpaperImmediate(servicedWallpaper) + break + case "swipe_left": + swipeDirection = 0 + setWallpaperWithTransition(servicedWallpaper) + break + case "swipe_right": + swipeDirection = 1 + setWallpaperWithTransition(servicedWallpaper) + break + case "swipe_up": + swipeDirection = 2 + setWallpaperWithTransition(servicedWallpaper) + break + case "swipe_down": + swipeDirection = 3 + setWallpaperWithTransition(servicedWallpaper) + break + default: + setWallpaperWithTransition(servicedWallpaper) + break } } } @@ -77,30 +100,52 @@ Variants { visible: false } + // Fade transition shader ShaderEffect { - id: shaderEffect + id: fadeShader anchors.fill: parent + visible: Settings.data.wallpaper.transitionType === 'fade' property variant source1: currentWallpaper property variant source2: nextWallpaper - property real fade: fadeValue - fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/mix_images.frag.qsb") + property real fade: transitionProgress + fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/wp_fade.frag.qsb") } - // Animation for the fade value + // Swipe transition shader + ShaderEffect { + id: swipeShader + anchors.fill: parent + visible: Settings.data.wallpaper.transitionType.startsWith('swipe_') + + property variant source1: currentWallpaper + property variant source2: nextWallpaper + property real progress: transitionProgress + property real direction: swipeDirection + property real smoothness: swipeSmoothness + fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/wp_swipe.frag.qsb") + } + + // Animation for the transition progress NumberAnimation { - id: fadeAnimation + id: transitionAnimation target: root - property: "fadeValue" + property: "transitionProgress" from: 0.0 to: 1.0 - duration: Settings.data.wallpaper.transitionDuration - easing.type: Easing.InOutQuad + duration: Settings.data.wallpaper.transitionDuration ?? 1000 + easing.type: { + const transitionType = Settings.data.wallpaper.transitionType ?? 'fade' + if (transitionType.startsWith('swipe_')) { + return Easing.InOutCubic + } + return Easing.InOutCubic + } onFinished: { // Swap images after transition completes currentWallpaper.source = nextWallpaper.source - fadeValue = 0.0 + transitionProgress = 0.0 transitioning = false } } @@ -108,14 +153,14 @@ Variants { function startTransition() { if (!transitioning && nextWallpaper.source != currentWallpaper.source) { transitioning = true - fadeAnimation.start() + transitionAnimation.start() } } function setWallpaperImmediate(source) { currentWallpaper.source = source nextWallpaper.source = source - fadeValue = 0.0 + transitionProgress = 0.0 transitioning = false } @@ -123,13 +168,10 @@ Variants { if (source != currentWallpaper.source) { if (transitioning) { - // we are interupting a transition - if (fadeValue >= 0.5) { - - } + // We are interrupting a transition currentWallpaper.source = nextWallpaper.source - fadeAnimation.stop() - fadeValue = 0 + transitionAnimation.stop() + transitionProgress = 0 transitioning = false } diff --git a/Services/WallpaperService.qml b/Services/WallpaperService.qml index ea3a594..e12a273 100644 --- a/Services/WallpaperService.qml +++ b/Services/WallpaperService.qml @@ -23,6 +23,22 @@ Singleton { key: "fade" name: "Fade" } + ListElement { + key: "swipe_left" + name: "Swipe Left" + } + ListElement { + key: "swipe_right" + name: "Swipe Right" + } + ListElement { + key: "swipe_up" + name: "Swipe Up" + } + ListElement { + key: "swipe_down" + name: "Swipe Down" + } } property var wallpaperLists: ({}) diff --git a/Shaders/frag/mix_images.frag b/Shaders/frag/wp_fade.frag similarity index 100% rename from Shaders/frag/mix_images.frag rename to Shaders/frag/wp_fade.frag diff --git a/Shaders/frag/wp_swipe.frag b/Shaders/frag/wp_swipe.frag new file mode 100644 index 0000000..caa1ebc --- /dev/null +++ b/Shaders/frag/wp_swipe.frag @@ -0,0 +1,57 @@ +#version 440 + +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 direction; // 0=left, 1=right, 2=up, 3=down + float smoothness; // Edge smoothness (0.01 to 0.5, default 0.05) +} ubuf; + +void main() { + vec2 uv = qt_TexCoord0; + vec4 color1 = texture(source1, uv); // Current (old) wallpaper + vec4 color2 = texture(source2, uv); // Next (new) wallpaper + + 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; + + // Calculate edge position based on direction + // As progress goes from 0 to 1, we reveal source2 (new wallpaper) + if (ubuf.direction < 0.5) { + // Swipe from right to left (new image enters from right) + edge = 1.0 - extendedProgress; + factor = smoothstep(edge - ubuf.smoothness, edge + ubuf.smoothness, uv.x); + fragColor = mix(color1, color2, factor); + } + else if (ubuf.direction < 1.5) { + // Swipe from left to right (new image enters from left) + edge = extendedProgress; + factor = smoothstep(edge - ubuf.smoothness, edge + ubuf.smoothness, uv.x); + fragColor = mix(color2, color1, factor); + } + else if (ubuf.direction < 2.5) { + // Swipe from bottom to top (new image enters from bottom) + edge = 1.0 - extendedProgress; + factor = smoothstep(edge - ubuf.smoothness, edge + ubuf.smoothness, uv.y); + fragColor = mix(color1, color2, factor); + } + else { + // Swipe from top to bottom (new image enters from top) + edge = extendedProgress; + factor = smoothstep(edge - ubuf.smoothness, edge + ubuf.smoothness, uv.y); + fragColor = mix(color2, color1, factor); + } + + fragColor *= ubuf.qt_Opacity; +} \ No newline at end of file diff --git a/Shaders/qsb/mix_images.frag.qsb b/Shaders/qsb/wp_fade.frag.qsb similarity index 100% rename from Shaders/qsb/mix_images.frag.qsb rename to Shaders/qsb/wp_fade.frag.qsb diff --git a/Shaders/qsb/wp_swipe.frag.qsb b/Shaders/qsb/wp_swipe.frag.qsb new file mode 100644 index 0000000000000000000000000000000000000000..e1977cede70717110957613f25d9f80aba5a19a1 GIT binary patch literal 2629 zcmV-L3cB?G05n5*ob6j}cN51IzSy!2;w>Q%NYW&ekWd+nkcCN8jKS3At-&?LE`g|y zx|UY*f}|C@DIkI=2 zx%bZ8``n#7JF3=#DZ5&Z`j#azMWBWt!?_ zQGpx^=sE@+*RwOPC#X&yxl|^PZ1Ty6!elpvZB!&j2^T0Jhg_;b<#acd?c`Hc#W%N$OD(+(`ZE_o=fpXVZRAOphFriAGzfSINn~tu&zGAU{>hkugGJ_3uJ|4{Nvkd)2+{J57leE_3iS^|jgq$TW`a#&3jie2VF_SoF~?UhXJT#c(-e!rQ`Dozd5LTMIiTTU+DB2r=N&U^xPe6VPWF`I#ZBVi4k4Z?~evW$SNyJddE}nFe-Zb`sZZra3c0igwq?u*foqxlkQrh!Y2Y7HpK2dK4oUod zU>W4u0VX#L`x$0G8)N?<@Y~QEf$l;0A%JNcuffJ4X5)Ln|4Mx-M-Ky&x%+qOr6b6d ze^Em9jS|re@@ACT83WgPE=S?l7>n;wCVvdMqvtjro~NgpAIaH=rwX?MHZabbA`lj&%X4Jkvg&X^(O4Gx@;asu9E0B5-Z<8uS*KOw+J)J;u%r;F|NB z(7VAnUjpavFuiwyYtG+eoL_^TcNypJF^+E;&-DAoGyQXe^V`7qM|1uGvBho7>mG_1yn}z1?TnpZpQn*K7_F`!}tl*TY?R0`F1lury&S-hXBHqV4&P zTNA^XOtS9T1xK|Oobp0llq#+jj7DIm7K|p7e&96_XtUH4ge!*9{hGN_1&7nQYXYlIvCF(4sNl zC^0CUpl?;{72C^B3g2yb1$%gO1%d2nGP!7Z*PU8fEZT*vobjyPoN!&Qn2~+1Qxr0U zsX;iaq#BE2%n(K2h$y%f*BcgNBCwl5!?UxKDNJTaG!_R<$!v^dmPv+@u#06|jEPJ- z!_kse2wX3U+s(kP7470V1X_%Vl#EweM_%j|so``+>=UVMIwSU~Xr|lY9vmDL`%N$$ z!70I3i!pITWYUKQRc{z0d11zKl>Lzjg{IVmwF}`meqh&AN;NY4P-t>r6mKl1n~0XY z%9Z>{&niz^ftAb5i7`=inyHYLA;f%05s(T8AB66b`e|2u+i)e;ftlmA~ixy#DQ ztL<`f=o?Z_oLbzAiQ5RmcZiZ_l_zApAI{6lYF|~>UQ`~!e!a!rx9lAy*6+2~ll#w# z=&IV0CGi=s>MVG~ZzlJd4c)6q+u+ItqzB)K;adNf!hEAtvOTqM9~PcncKpEhQu7(T zqWal96G#VV!ooEZSb;6_kjkT+acN&lce9)A zushtvZZ_7glur7A6*z^^OS59~zRt3VUBb+{Gv_blCp_2p^Ji-HMgYQNtck;7_Qb`B z>FM*6nJ`o_Hl`baVT27U4vU!!`B}>=+W}0d>MaHG9VHyYcPZkq+bZJ-eL4(W%A~?P zS7e19!8J5n!CvM>vefNJvb35r$-HqUS;C=Y38#|zxKqh|w^PZyaVlBrek@sfkaNj= zmjy4s%5q<^y?SJ2564V5o6Y8G%YBEx!!3UwO})P#r@K4OmiVaS_k0}L5r}L@Afih5 zk_ZnuHY-)YRVGF>Mv*uz>-J;l?~3_w4;4ReRQ%|=9KI(-)_mayR@u%w@?NjN9V`DB zmq^Df$n6C@1Y(8`Ex4)?3UMUO!^7&7l=JMM;nfTSOMF9W!3 zQndrClJ|pR#hE{vY~#LDEgq0R(hEnE$%Ze#3#(aG+pk*%TPUr#Z~a=^pZ~@CG)9+B zW~AfY%4#mBawwGTH%D(z8Zaiw{O>2UgF*_ zLoU9F=MG)H=cOmk`)|>gqxYR5(Ktn7wXE-tr`M~=4AFI8(rGsCG(t+DsJme;m&LqK nEx$OPFpenj?-KYQ8{?1drL1hrkFgV2wVu4<^wsrW)>D-%9z;2$ literal 0 HcmV?d00001