From e2816bcb1e747b71752d0185282881da85fbf5fa Mon Sep 17 00:00:00 2001 From: Benjamin Palko Date: Sat, 20 Sep 2025 21:21:30 -0400 Subject: [PATCH 1/5] directory watcher --- utils/DirectoryWatcher.qml | 43 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 utils/DirectoryWatcher.qml diff --git a/utils/DirectoryWatcher.qml b/utils/DirectoryWatcher.qml new file mode 100644 index 0000000..776e4cf --- /dev/null +++ b/utils/DirectoryWatcher.qml @@ -0,0 +1,43 @@ +import QtQuick +import Quickshell.Io + +Item { + id: root + + required property string path + property bool recursive: true + property list fileFilter: [] + signal created(path: string) + signal modified(path: string) + signal deleted(path: string) + signal movedFrom(path: string) + signal movedTo(path: string) + + Process { + running: true + command: ["bash", "-c", `inotifywait -m ${root.recursive ? "-r" : ""} ${root.path} -e modify,move,create,delete --format "%e-%w%f"`] + stderr: StdioCollector { + onStreamFinished: console.error(`DirectoryWatcher: ${this.text}`) + } + stdout: SplitParser { + splitMarker: "\n" + onRead: data => { + const [action, path] = data.split("-"); + if (path.endsWith("~") || root.fileFilter.length > 0 && !root.fileFilter.some(filter => path.endsWith(filter))) { + return; + } + if (action.includes("CREATE")) { + root.created(path); + } else if (action.includes("MODIFY")) { + root.modified(path); + } else if (action.includes("DELETE")) { + root.deleted(path); + } else if (action.includes("MOVED_FROM")) { + root.movedFrom(path); + } else if (action.includes("MOVED_TO")) { + root.movedTo(path); + } + } + } + } +} From 2d251c044dd7c3a2bf456695b326b1211586ab72 Mon Sep 17 00:00:00 2001 From: Benjamin Palko Date: Sat, 20 Sep 2025 23:07:01 -0400 Subject: [PATCH 2/5] remove wallpaper view --- modules/configuration/Configuration.qml | 6 ------ modules/configuration/Wallpaper.qml | 13 ------------- 2 files changed, 19 deletions(-) delete mode 100644 modules/configuration/Wallpaper.qml diff --git a/modules/configuration/Configuration.qml b/modules/configuration/Configuration.qml index a453993..4c70926 100644 --- a/modules/configuration/Configuration.qml +++ b/modules/configuration/Configuration.qml @@ -118,12 +118,6 @@ StyledPanelWindow { title: "Styling" view: StylingView {} } - - ConfigurationView { - icon: Styling.lucide.icons.swatchBook - title: "Wallpapers" - view: Wallpaper {} - } } component ConfigurationView: QtObject { diff --git a/modules/configuration/Wallpaper.qml b/modules/configuration/Wallpaper.qml deleted file mode 100644 index d75a04a..0000000 --- a/modules/configuration/Wallpaper.qml +++ /dev/null @@ -1,13 +0,0 @@ -import qs.services -import QtQuick -import QtQuick.Layouts - -ColumnLayout { - Repeater { - model: WallpaperService.files - delegate: Text { - required property string dataModel - text: dataModel - } - } -} From 9c074dc9e587bcf29d555a062e7d2da2e4d93e6f Mon Sep 17 00:00:00 2001 From: Benjamin Palko Date: Sat, 20 Sep 2025 23:07:37 -0400 Subject: [PATCH 3/5] double import...fixed --- modules/storybook/Components.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/storybook/Components.qml b/modules/storybook/Components.qml index 1d90cbd..0ff3836 100644 --- a/modules/storybook/Components.qml +++ b/modules/storybook/Components.qml @@ -1,7 +1,7 @@ import qs.components import qs.components.composite import qs.config -import qs.config +import qs.services import QtQuick import QtQuick.Controls import QtQuick.Layouts From 3d1816c0f1eaeff9a8ca32ee9745d00eab3d5629 Mon Sep 17 00:00:00 2001 From: Benjamin Palko Date: Sat, 20 Sep 2025 23:08:57 -0400 Subject: [PATCH 4/5] wallpaper service --- services/WallpaperService.qml | 42 +++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/services/WallpaperService.qml b/services/WallpaperService.qml index 2c0026f..ea6bd0e 100644 --- a/services/WallpaperService.qml +++ b/services/WallpaperService.qml @@ -1,5 +1,7 @@ pragma Singleton +import qs.config +import qs.utils import Quickshell import Quickshell.Io @@ -7,18 +9,44 @@ Singleton { id: root property string directory: "~/Wallpapers/" - property list files: [] + property alias files: properties.files + property alias currentWallpaper: properties.currentWallpaper property list fileTypes: ["jpg", "jpeg", "png"] + property string command: `find ${root.directory} -maxdepth 1 -type f` Process { running: true - command: ["bash", "-c", `find "$dir" -maxdepth 1 -type f & inotifywait -m -r ${root.directory} -e modify,move,create,delete | while read dir action; do find "$dir" -maxdepth 1 -type f; done`] - stderr: StdioCollector { - onStreamFinished: console.log(`line read: ${this.text}`) + command: ["bash", "-c", `find ${root.directory} -maxdepth 1 -type f`] + stdout: StdioCollector { + onStreamFinished: { + root.files = this.text.split("\n"); + } } - stdout: SplitParser { - splitMarker: "\n" - onRead: data => root.files = data?.split("\n") ?? [] + } + + DirectoryWatcher { + path: root.directory + fileFilter: ["jpg", "jpeg", "png"] + onCreated: path => root.files.push(path) + onDeleted: path => root.files.filter(file => file != path) + onMovedFrom: path => root.files.filter(file => file != path) + onMovedTo: path => path => root.files.push(path) + } + + FileView { + path: `${Paths.cache}/wallpaper.json` + + watchChanges: true + onFileChanged: reload() + + // when changes are made to properties in the adapter, save them + onAdapterUpdated: writeAdapter() + + JsonAdapter { + id: properties + + property url currentWallpaper: "" + property list files: [] } } } From 2bfa0c189a148d916d116deab97b6845a9359fbf Mon Sep 17 00:00:00 2001 From: Benjamin Palko Date: Mon, 22 Sep 2025 21:29:36 -0400 Subject: [PATCH 5/5] wallpaper selection --- components/composite/WallpaperItem.qml | 14 ++++++ components/composite/WallpaperList.qml | 65 ++++++++++++++++++++++++++ modules/Shell.qml | 18 +++++++ modules/configuration/StylingView.qml | 35 +++++++++++--- modules/wallpaper/Wallpaper.qml | 32 +++++++++++++ 5 files changed, 157 insertions(+), 7 deletions(-) create mode 100644 components/composite/WallpaperItem.qml create mode 100644 components/composite/WallpaperList.qml create mode 100644 modules/wallpaper/Wallpaper.qml diff --git a/components/composite/WallpaperItem.qml b/components/composite/WallpaperItem.qml new file mode 100644 index 0000000..779fb62 --- /dev/null +++ b/components/composite/WallpaperItem.qml @@ -0,0 +1,14 @@ +import QtQuick + +Image { + asynchronous: true + fillMode: Image.PreserveAspectCrop + sourceSize.width: parent.width + sourceSize.height: parent.height + + Behavior on scale { + NumberAnimation { + duration: 200 + } + } +} diff --git a/components/composite/WallpaperList.qml b/components/composite/WallpaperList.qml new file mode 100644 index 0000000..2a67f31 --- /dev/null +++ b/components/composite/WallpaperList.qml @@ -0,0 +1,65 @@ +pragma ComponentBehavior: Bound + +import qs.config +import qs.services +import QtQuick +import Qt.labs.folderlistmodel 2.9 +import Quickshell.Widgets + +ListView { + id: list + + orientation: ListView.Horizontal + + clip: true + spacing: 8 + snapMode: ListView.SnapToItem + + implicitWidth: 160 * 3 + implicitHeight: 90 + model: FolderListModel { + nameFilters: ["*.jpg"] + folder: `${Paths.home}/Wallpapers` + showDirs: false + } + delegate: Item { + id: delegate + + required property url fileUrl + required property int index + property bool hovered: ListView.isCurrentItem + + implicitWidth: 160 + implicitHeight: 90 + + Rectangle { + color: Styling.theme.base200 + anchors.fill: parent + radius: 12 + scale: delegate.hovered ? 1.0 : 0.9 + Behavior on scale { + NumberAnimation { + duration: Styling.animations.veryFast + } + } + + Image { + id: image + + asynchronous: true + anchors.fill: parent + fillMode: Image.PreserveAspectCrop + source: delegate.fileUrl + } + + MouseArea { + anchors.fill: image + + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + onEntered: list.currentIndex = delegate.index + onClicked: WallpaperService.currentWallpaper = delegate.fileUrl + } + } + } +} diff --git a/modules/Shell.qml b/modules/Shell.qml index b25fbe3..be31896 100644 --- a/modules/Shell.qml +++ b/modules/Shell.qml @@ -5,6 +5,7 @@ import "launcher" import "pomodoro" import "powermenu" import "storybook" +import "wallpaper" import QtQuick import Quickshell import Quickshell.Wayland @@ -17,6 +18,23 @@ Variants { required property ShellScreen modelData + PanelWindow { + id: background + + // visible: false + anchors.top: true + anchors.left: true + anchors.right: true + anchors.bottom: true + + color: "transparent" + + WlrLayershell.exclusionMode: ExclusionMode.Ignore + WlrLayershell.layer: WlrLayer.Background + + Wallpaper {} + } + PanelWindow { id: exclusionZone diff --git a/modules/configuration/StylingView.qml b/modules/configuration/StylingView.qml index 5454953..02461a3 100644 --- a/modules/configuration/StylingView.qml +++ b/modules/configuration/StylingView.qml @@ -1,19 +1,40 @@ +pragma ComponentBehavior: Bound + import qs.components import qs.components.composite import qs.config +import qs.services import QtQuick import QtQuick.Layouts +import Qt.labs.folderlistmodel 2.9 +import Quickshell.Widgets -StyledPane { - GridLayout { +ColumnLayout { + StyledPane { + GridLayout { - columnSpacing: Styling.layout.spacing.xl + columnSpacing: Styling.layout.spacing.xl - StyledText { - text: "Theme" - font.pixelSize: Styling.typography.textSize.lg + StyledText { + text: "Theme" + font.pixelSize: Styling.typography.textSize.lg + } + + ThemeComboBox {} } + } + // StyledPane { + // WallpaperList {} + // } - ThemeComboBox {} + StyledPane { + padding: 2 + ClippingWrapperRectangle { + + radius: Styling.theme.radiusBox + color: "transparent" + + WallpaperList {} + } } } diff --git a/modules/wallpaper/Wallpaper.qml b/modules/wallpaper/Wallpaper.qml new file mode 100644 index 0000000..d396074 --- /dev/null +++ b/modules/wallpaper/Wallpaper.qml @@ -0,0 +1,32 @@ +import qs.services +import QtQuick +import QtQuick.Controls + +StackView { + id: stack + property url wallpaper: WallpaperService.currentWallpaper + + anchors.fill: parent + + replaceEnter: Transition { + OpacityAnimator { + from: 0.0 + to: 1.0 + duration: 1000 + } + } + replaceExit: Transition { + PauseAnimation { + duration: 1100 + } + } + + Component { + id: img + Image {} + } + + onWallpaperChanged: stack.replace(img, { + "source": stack.wallpaper + }) +}