From bd0135ec03c4db801cd92876a0461c104259f40b Mon Sep 17 00:00:00 2001 From: ferreo Date: Sun, 13 Jul 2025 14:09:59 +0100 Subject: [PATCH] feat: Add support for swww and wallust and clean up a few things --- Bar/Modules/ActiveWindow.qml | 4 +- {Helpers => Components}/Spinner.qml | 0 Helpers/BluetoothManager.qml | 70 --- Helpers/Fuzzysort.js | 2 +- Helpers/Globals.qml | 7 - Helpers/Processes.qml | 68 --- Helpers/WallpaperManager.qml | 58 -- Services/WallpaperManager.qml | 119 ++++ Settings/Settings.qml | 63 +- Settings/Theme.json | 28 + Settings/Theme.qml | 106 +++- Templates/templates/quickshell.json | 28 + Templates/wallust.toml | 47 ++ Widgets/Background.qml | 6 +- Widgets/LockScreen.qml | 4 +- Widgets/Overview.qml | 8 +- Widgets/Sidebar/Config/ProfileSettings.qml | 2 - Widgets/Sidebar/Config/SettingsModal.qml | 61 ++ Widgets/Sidebar/Config/WallpaperSettings.qml | 574 ++++++++++++++++++- Widgets/Sidebar/Panel/System.qml | 32 +- Widgets/Sidebar/Panel/WallpaperPanel.qml | 27 +- shell.qml | 20 +- 22 files changed, 1053 insertions(+), 281 deletions(-) rename {Helpers => Components}/Spinner.qml (100%) delete mode 100644 Helpers/BluetoothManager.qml delete mode 100644 Helpers/Globals.qml delete mode 100644 Helpers/Processes.qml delete mode 100644 Helpers/WallpaperManager.qml create mode 100644 Services/WallpaperManager.qml create mode 100644 Settings/Theme.json create mode 100644 Templates/templates/quickshell.json create mode 100644 Templates/wallust.toml diff --git a/Bar/Modules/ActiveWindow.qml b/Bar/Modules/ActiveWindow.qml index b2b0c0b..ea9b1dc 100644 --- a/Bar/Modules/ActiveWindow.qml +++ b/Bar/Modules/ActiveWindow.qml @@ -67,14 +67,14 @@ Item { anchors.left: parent.left anchors.leftMargin: 6 anchors.verticalCenter: parent.verticalCenter - source: ToplevelManager.activeToplevel ? getIcon() : "" + source: ToplevelManager?.activeToplevel ? getIcon() : "" visible: Settings.showActiveWindowIcon anchors.verticalCenterOffset: -3 } Text { id: activeWindowTitle - text: ToplevelManager.activeToplevel?.title && ToplevelManager.activeToplevel.title.length > 60 ? ToplevelManager.activeToplevel.title.substring(0, 60) + "..." : ToplevelManager.activeToplevel.title + text: ToplevelManager?.activeToplevel?.title && ToplevelManager?.activeToplevel?.title.length > 60 ? ToplevelManager?.activeToplevel?.title.substring(0, 60) + "..." : ToplevelManager?.activeToplevel?.title || "" font.pixelSize: 12 color: Theme.textSecondary elide: Text.ElideRight diff --git a/Helpers/Spinner.qml b/Components/Spinner.qml similarity index 100% rename from Helpers/Spinner.qml rename to Components/Spinner.qml diff --git a/Helpers/BluetoothManager.qml b/Helpers/BluetoothManager.qml deleted file mode 100644 index 91b9b1d..0000000 --- a/Helpers/BluetoothManager.qml +++ /dev/null @@ -1,70 +0,0 @@ -import QtQuick -import Quickshell.Io - -QtObject { - // List all known devices - function listDevices(callback) { - var proc = Qt.createQmlObject(' - import Quickshell.Io;\n\ - Process {\n\ - command: ["bluetoothctl", "devices"],\n\ - running: true;\n\ - stdout: StdioCollector {\n\ - onStreamFinished: {\n\ - var lines = this.text.split("\n");\n\ - var devs = [];\n\ - for (var i = 0; i < lines.length; ++i) {\n\ - var line = lines[i].trim();\n\ - if (line.startsWith("Device ")) {\n\ - var parts = line.split(" ");\n\ - var mac = parts[1];\n\ - var name = parts.slice(2).join(" ");\n\ - devs.push({ mac: mac, name: name });\n\ - }\n\ - }\n\ - callback(devs);\n\ - parent.destroy();\n\ - }\n\ - }\n\ - }', this); - } - - // Check if a device is connected - function checkConnected(mac, callback) { - var proc = Qt.createQmlObject(' - import Quickshell.Io;\n\ - Process {\n\ - command: ["bluetoothctl", "info", "' + mac + '"],\n\ - running: true;\n\ - stdout: StdioCollector {\n\ - onStreamFinished: {\n\ - var connected = this.text.indexOf("Connected: yes") !== -1;\n\ - callback(connected);\n\ - parent.destroy();\n\ - }\n\ - }\n\ - }', this); - } - - // Connect to a device - function connect(mac, callback) { - var proc = Qt.createQmlObject(' - import Quickshell.Io;\n\ - Process {\n\ - command: ["bluetoothctl", "connect", "' + mac + '"],\n\ - running: true;\n\ - stdout: StdioCollector { onStreamFinished: { callback(true); parent.destroy(); } }\n\ - }', this); - } - - // Disconnect from a device - function disconnect(mac, callback) { - var proc = Qt.createQmlObject(' - import Quickshell.Io;\n\ - Process {\n\ - command: ["bluetoothctl", "disconnect", "' + mac + '"],\n\ - running: true;\n\ - stdout: StdioCollector { onStreamFinished: { callback(true); parent.destroy(); } }\n\ - }', this); - } -} \ No newline at end of file diff --git a/Helpers/Fuzzysort.js b/Helpers/Fuzzysort.js index c4f8eb0..0e1f68b 100644 --- a/Helpers/Fuzzysort.js +++ b/Helpers/Fuzzysort.js @@ -675,4 +675,4 @@ var noTarget = prepare('') // Hacked version of https://github.com/lemire/FastPriorityQueue.js var fastpriorityqueue=r=>{var e=[],o=0,a={},v=r=>{for(var a=0,v=e[a],c=1;c>1]=e[a],c=1+(a<<1)}for(var f=a-1>>1;a>0&&v._score>1)e[a]=e[f];e[a]=v};return a.add=(r=>{var a=o;e[o++]=r;for(var v=a-1>>1;a>0&&r._score>1)e[a]=e[v];e[a]=r}),a.poll=(r=>{if(0!==o){var a=e[0];return e[0]=e[--o],v(),a}}),a.peek=(r=>{if(0!==o)return e[0]}),a.replaceTop=(r=>{e[0]=r,v()}),a} -var q = fastpriorityqueue() // reuse this +var q = fastpriorityqueue() // reuse this \ No newline at end of file diff --git a/Helpers/Globals.qml b/Helpers/Globals.qml deleted file mode 100644 index be209dd..0000000 --- a/Helpers/Globals.qml +++ /dev/null @@ -1,7 +0,0 @@ -pragma Singleton -import QtQuick - -QtObject { - // Global username, set at app startup - property string userName: "User" -} \ No newline at end of file diff --git a/Helpers/Processes.qml b/Helpers/Processes.qml deleted file mode 100644 index 5883617..0000000 --- a/Helpers/Processes.qml +++ /dev/null @@ -1,68 +0,0 @@ -pragma Singleton -import QtQuick -import Quickshell.Io - -QtObject { - id: processesRoot - property string userName: "User" - property string uptimeText: "--:--" - property int uptimeUpdateTrigger: 0 - - property Process whoamiProcess: Process { - command: ["whoami"] - running: false - stdout: StdioCollector { - onStreamFinished: { - processesRoot.userName = this.text.trim() - whoamiProcess.running = false - } - } - } - - property Process shutdownProcess: Process { - command: ["shutdown", "-h", "now"] - running: false - } - property Process rebootProcess: Process { - command: ["reboot"] - running: false - } - property Process logoutProcess: Process { - command: ["niri", "msg", "action", "quit", "--skip-confirmation"] - running: false - } - - property Process uptimeProcess: Process { - command: ["sh", "-c", "uptime | awk -F 'up ' '{print $2}' | awk -F ',' '{print $1}' | xargs"] - running: false - stdout: StdioCollector { - onStreamFinished: { - processesRoot.uptimeText = this.text.trim() - uptimeProcess.running = false - } - } - } - - Component.onCompleted: { - whoamiProcess.running = true - updateUptime() - } - - function shutdown() { - shutdownProcess.running = true - } - function reboot() { - rebootProcess.running = true - } - function logout() { - logoutProcess.running = true - } - - function updateUptime() { - uptimeProcess.running = true - } - - onUptimeUpdateTriggerChanged: { - uptimeProcess.running = true - } -} \ No newline at end of file diff --git a/Helpers/WallpaperManager.qml b/Helpers/WallpaperManager.qml deleted file mode 100644 index 0643c21..0000000 --- a/Helpers/WallpaperManager.qml +++ /dev/null @@ -1,58 +0,0 @@ -pragma Singleton -import QtQuick -import Quickshell.Io - -Item { - id: manager - - // Hardcoded directory for v1 - property string wallpaperDirectory: "/home/lysec/nixos/assets/wallpapers" - property var wallpaperList: [] - property string currentWallpaper: "" - property bool scanning: false - - // Log initial state - Component.onCompleted: { - loadWallpapers() - } - - // Scan directory for wallpapers - function loadWallpapers() { - scanning = true; - wallpaperList = []; - findProcess.tempList = []; - findProcess.running = true; - } - - function setCurrentWallpaper(path) { - currentWallpaper = path; - } - - Process { - id: findProcess - property var tempList: [] - running: false - command: ["find", manager.wallpaperDirectory, "-type", "f", "-name", "*.png", "-o", "-name", "*.jpg", "-o", "-name", "*.jpeg"] - onRunningChanged: { - } - stdout: StdioCollector { - onStreamFinished: { - var lines = text.split("\n"); - for (var i = 0; i < lines.length; ++i) { - var trimmed = lines[i].trim(); - if (trimmed) { - findProcess.tempList.push(trimmed); - } - } - } - } - stderr: StdioCollector { - onStreamFinished: { - } - } - onExited: { - manager.wallpaperList = findProcess.tempList.slice(); - scanning = false; - } - } -} \ No newline at end of file diff --git a/Services/WallpaperManager.qml b/Services/WallpaperManager.qml new file mode 100644 index 0000000..4937021 --- /dev/null +++ b/Services/WallpaperManager.qml @@ -0,0 +1,119 @@ +pragma Singleton +import QtQuick +import Qt.labs.folderlistmodel +import Quickshell +import Quickshell.Io +import qs.Settings + +Singleton { + id: manager + + Item { + Component.onCompleted: { + loadWallpapers(); + setCurrentWallpaper(currentWallpaper, true); + toggleRandomWallpaper(); + } + } + property string wallpaperDirectory: Settings.wallpaperFolder + property var wallpaperList: [] + property string currentWallpaper: Settings.currentWallpaper + property bool scanning: false + + function loadWallpapers() { + scanning = true; + wallpaperList = []; + folderModel.folder = ""; + folderModel.folder = "file://" + (Settings.wallpaperFolder !== undefined ? Settings.wallpaperFolder : ""); + } + + function changeWallpaper(path) { + if (!Settings.randomWallpaper) { + setCurrentWallpaper(path); + } + } + + function setCurrentWallpaper(path, isInitial) { + currentWallpaper = path; + if (!isInitial) { + Settings.currentWallpaper = path; + Settings.saveSettings(); + } + if (Settings.useSWWW) { + changeWallpaperProcess.running = true; + } + generateTheme(); + } + + function setRandomWallpaper() { + var randomIndex = Math.floor(Math.random() * wallpaperList.length); + var randomPath = wallpaperList[randomIndex]; + if (!randomPath) { + return; + } + setCurrentWallpaper(randomPath); + } + + function toggleRandomWallpaper() { + if (Settings.randomWallpaper && !randomWallpaperTimer.running) { + randomWallpaperTimer.start(); + setRandomWallpaper(); + } else if (!Settings.randomWallpaper && randomWallpaperTimer.running) { + randomWallpaperTimer.stop(); + } + } + + function restartRandomWallpaperTimer() { + if (Settings.randomWallpaper) { + randomWallpaperTimer.stop(); + randomWallpaperTimer.start(); + setRandomWallpaper(); + } + } + + function generateTheme() { + if (Settings.useWallpaperTheme) { + generateThemeProcess.running = true; + } + } + + Timer { + id: randomWallpaperTimer + interval: Settings.wallpaperInterval * 1000 + running: false + repeat: true + onTriggered: setRandomWallpaper() + triggeredOnStart: false + } + + FolderListModel { + id: folderModel + nameFilters: ["*.avif", "*.jpg", "*.jpeg", "*.png", "*.gif", "*.pnm", "*.tga", "*.tiff", "*.webp", "*.bmp", "*.farbfeld"] + showDirs: false + sortField: FolderListModel.Name + onStatusChanged: { + if (status === FolderListModel.Ready) { + var files = []; + for (var i = 0; i < count; i++) { + var fileph = (Settings.wallpaperFolder !== undefined ? Settings.wallpaperFolder : "") + "/" + get(i, "fileName"); + files.push(fileph); + } + wallpaperList = files; + scanning = false; + } + } + } + + Process { + id: changeWallpaperProcess + command: ["swww", "img", "--resize", Settings.wallpaperResize, "--transition-fps", Settings.transitionFps.toString(), "--transition-type", Settings.transitionType, "--transition-duration", Settings.transitionDuration.toString(), currentWallpaper] + running: false + } + + Process { + id: generateThemeProcess + command: ["wallust", "run", currentWallpaper, "-u", "-k", "-d", "Templates"] + workingDirectory: Quickshell.configDir + running: false + } +} diff --git a/Settings/Settings.qml b/Settings/Settings.qml index e9aa18c..8651155 100644 --- a/Settings/Settings.qml +++ b/Settings/Settings.qml @@ -1,21 +1,35 @@ pragma Singleton import QtQuick import QtCore +import qs.Services QtObject { - property string weatherCity: "Dinslaken" - property string profileImage: "https://cdn.discordapp.com/avatars/158005126638993408/de403f05fd7f74bb17e01a9b066a30fa?size=64" - property bool useFahrenheit - property string wallpaperFolder: "/home/lysec/nixos/assets/wallpapers" // Default path, make persistent - property string currentWallpaper: "" - property string videoPath: "~/Videos/" // Default path, make persistent - property bool showActiveWindowIcon - // Settings persistence - property var settings: Qt.createQmlObject('import QtCore; Settings { category: "Quickshell" }', this, "settings") - Component.onCompleted: { + Qt.application.name = "quickshell" + Qt.application.organization = "quisckshell" + Qt.application.domain = "quickshell.app" loadSettings() } + property string weatherCity: "Dinslaken" + property string profileImage: "/home/user/.face" + property bool useFahrenheit + property string wallpaperFolder: "/usr/share/wallpapers" + property string currentWallpaper: "" + property string videoPath: "~/Videos/" + property bool showActiveWindowIcon: false + property bool useSWWW: false + property bool randomWallpaper: false + property bool useWallpaperTheme: false + property int wallpaperInterval: 300 + property string wallpaperResize: "crop" + property int transitionFps: 60 + property string transitionType: "random" + property real transitionDuration: 1.1 + + // Settings persistence + property var settings: Settings { + category: "quickshell" + } function loadSettings() { weatherCity = settings.value("weatherCity", weatherCity) @@ -25,8 +39,19 @@ QtObject { wallpaperFolder = settings.value("wallpaperFolder", wallpaperFolder) currentWallpaper = settings.value("currentWallpaper", currentWallpaper) videoPath = settings.value("videoPath", videoPath) - showActiveWindowIcon = settings.value("showActiveWindowIcon", showActiveWindowIcon) - console.log("Loaded profileImage:", profileImage) + let showActiveWindowIconFlag = settings.value("showActiveWindowIconFlag", "false") + showActiveWindowIcon = showActiveWindowIconFlag === "true" + let useSWWWFlag = settings.value("useSWWWFlag", "false") + useSWWW = useSWWWFlag === "true" + let randomWallpaperFlag = settings.value("randomWallpaperFlag", "false") + randomWallpaper = randomWallpaperFlag === "true" + let useWallpaperThemeFlag = settings.value("useWallpaperThemeFlag", "false") + useWallpaperTheme = useWallpaperThemeFlag === "true" + wallpaperInterval = settings.value("wallpaperInterval", wallpaperInterval) + wallpaperResize = settings.value("wallpaperResize", wallpaperResize) + transitionFps = settings.value("transitionFps", transitionFps) + transitionType = settings.value("transitionType", transitionType) + transitionDuration = settings.value("transitionDuration", transitionDuration) } function saveSettings() { @@ -36,13 +61,23 @@ QtObject { settings.setValue("wallpaperFolder", wallpaperFolder) settings.setValue("currentWallpaper", currentWallpaper) settings.setValue("videoPath", videoPath) - settings.setValue("showActiveWindowIcon", showActiveWindowIcon) + settings.setValue("showActiveWindowIconFlag", showActiveWindowIcon ? "true" : "false") + settings.setValue("useSWWWFlag", useSWWW ? "true" : "false") + settings.setValue("randomWallpaperFlag", randomWallpaper ? "true" : "false") + settings.setValue("useWallpaperThemeFlag", useWallpaperTheme ? "true" : "false") + settings.setValue("wallpaperInterval", wallpaperInterval) + settings.setValue("wallpaperResize", wallpaperResize) + settings.setValue("transitionFps", transitionFps) + settings.setValue("transitionType", transitionType) + settings.setValue("transitionDuration", transitionDuration) settings.sync() - console.log("Saving profileImage:", profileImage) } // Property change handlers to auto-save (all commented out for explicit save only) // onWeatherCityChanged: saveSettings() // onProfileImageChanged: saveSettings() // onUseFahrenheitChanged: saveSettings() + onRandomWallpaperChanged: WallpaperManager.toggleRandomWallpaper() + onWallpaperIntervalChanged: WallpaperManager.restartRandomWallpaperTimer() + onWallpaperFolderChanged: WallpaperManager.loadWallpapers() } diff --git a/Settings/Theme.json b/Settings/Theme.json new file mode 100644 index 0000000..4c48774 --- /dev/null +++ b/Settings/Theme.json @@ -0,0 +1,28 @@ +{ + "backgroundPrimary": "#0C0D11", + "backgroundSecondary": "#151720", + "backgroundTertiary": "#1D202B", + + "surface": "#1A1C26", + "surfaceVariant": "#2A2D3A", + + "textPrimary": "#CACEE2", + "textSecondary": "#B7BBD0", + "textDisabled": "#6B718A", + + "accentPrimary": "#A8AEFF", + "accentSecondary": "#9EA0FF", + "accentTertiary": "#8EABFF", + + "error": "#FF6B81", + "warning": "#FFBB66", + + "highlight": "#E3C2FF", + "rippleEffect": "#F3DEFF", + + "onAccent": "#1A1A1A", + "outline": "#44485A", + + "shadow": "#000000B3", + "overlay": "#11121ACC" +} diff --git a/Settings/Theme.qml b/Settings/Theme.qml index 2ebb72b..99daf3f 100644 --- a/Settings/Theme.qml +++ b/Settings/Theme.qml @@ -1,48 +1,100 @@ // Theme.qml pragma Singleton import QtQuick +import Quickshell +import Quickshell.Io -QtObject { +Singleton { + id: root + + // FileView to load theme data from JSON file + FileView { + id: themeFile + path: Quickshell.configDir + "/Settings/Theme.json" + watchChanges: true + onFileChanged: reload() + onAdapterUpdated: writeAdapter() + + JsonAdapter { + id: themeData + + // Backgrounds + property string backgroundPrimary: "#0C0D11" + property string backgroundSecondary: "#151720" + property string backgroundTertiary: "#1D202B" + + // Surfaces & Elevation + property string surface: "#1A1C26" + property string surfaceVariant: "#2A2D3A" + + // Text Colors + property string textPrimary: "#CACEE2" + property string textSecondary: "#B7BBD0" + property string textDisabled: "#6B718A" + + // Accent Colors + property string accentPrimary: "#A8AEFF" + property string accentSecondary: "#9EA0FF" + property string accentTertiary: "#8EABFF" + + // Error/Warning + property string error: "#FF6B81" + property string warning: "#FFBB66" + + // Highlights & Focus + property string highlight: "#E3C2FF" + property string rippleEffect: "#F3DEFF" + + // Additional Theme Properties + property string onAccent: "#1A1A1A" + property string outline: "#44485A" + + // Shadows & Overlays + property string shadow: "#000000B3" + property string overlay: "#11121ACC" + } + } + // Backgrounds - readonly property color backgroundPrimary: "#0C0D11" // Deep indigo-black - readonly property color backgroundSecondary: "#151720" // Slightly lifted dark - readonly property color backgroundTertiary: "#1D202B" // Soft contrast surface + property color backgroundPrimary: themeData.backgroundPrimary + property color backgroundSecondary: themeData.backgroundSecondary + property color backgroundTertiary: themeData.backgroundTertiary // Surfaces & Elevation - readonly property color surface: "#1A1C26" // Material-like base layer - readonly property color surfaceVariant: "#2A2D3A" // Lightly elevated + property color surface: themeData.surface + property color surfaceVariant: themeData.surfaceVariant // Text Colors - readonly property color textPrimary: "#CACEE2" // Gentle off-white - readonly property color textSecondary: "#B7BBD0" // Muted lavender-blue - readonly property color textDisabled: "#6B718A" // Dimmed blue-gray + property color textPrimary: themeData.textPrimary + property color textSecondary: themeData.textSecondary + property color textDisabled: themeData.textDisabled - // Accent Colors (lavender-gold theme) - readonly property color accentPrimary: "#A8AEFF" // Light enchanted lavender - readonly property color accentSecondary: "#9EA0FF" // Softer lavender hue - readonly property color accentTertiary: "#8EABFF" // Warm golden glow (from lantern) + // Accent Colors + property color accentPrimary: themeData.accentPrimary + property color accentSecondary: themeData.accentSecondary + property color accentTertiary: themeData.accentTertiary // Error/Warning - readonly property color error: "#FF6B81" // Soft rose red - readonly property color warning: "#FFBB66" // Candlelight amber-orange + property color error: themeData.error + property color warning: themeData.warning // Highlights & Focus - readonly property color highlight: "#E3C2FF" // Bright magical lavender - readonly property color rippleEffect: "#F3DEFF" // Gentle soft splash + property color highlight: themeData.highlight + property color rippleEffect: themeData.rippleEffect // Additional Theme Properties - readonly property color onAccent: "#1A1A1A" // Text on accent background - readonly property color outline: "#44485A" // Subtle bluish-gray line + property color onAccent: themeData.onAccent + property color outline: themeData.outline // Shadows & Overlays - readonly property color shadow: "#000000B3" // Standard soft black shadow - readonly property color overlay: "#11121ACC" // Deep bluish overlay + property color shadow: themeData.shadow + property color overlay: themeData.overlay // Font Properties - readonly property string fontFamily: "Roboto" // Family for all text - - readonly property int fontSizeHeader: 32 // Headers and titles - readonly property int fontSizeBody: 16 // Body text and general content - readonly property int fontSizeSmall: 14 // Small text like clock, labels - readonly property int fontSizeCaption: 12 // Captions and fine print + property string fontFamily: "Roboto" // Family for all text + + property int fontSizeHeader: 32 // Headers and titles + property int fontSizeBody: 16 // Body text and general content + property int fontSizeSmall: 14 // Small text like clock, labels + property int fontSizeCaption: 12 // Captions and fine print } diff --git a/Templates/templates/quickshell.json b/Templates/templates/quickshell.json new file mode 100644 index 0000000..26500c5 --- /dev/null +++ b/Templates/templates/quickshell.json @@ -0,0 +1,28 @@ +{ + "backgroundPrimary": "{{ background }}", + "backgroundSecondary": "{{ background | lighten(0.05) }}", + "backgroundTertiary": "{{ background | lighten(0.1) }}", + + "surface": "{{ background | lighten(0.08) }}", + "surfaceVariant": "{{ background | lighten(0.15) }}", + + "textPrimary": "{{ foreground }}", + "textSecondary": "{{ foreground | darken(0.1) }}", + "textDisabled": "{{ foreground | darken(0.4) }}", + + "accentPrimary": "{{ color4 }}", + "accentSecondary": "{{ color4 | lighten(0.2) }}", + "accentTertiary": "{{ color4 | darken(0.2) }}", + + "error": "{{ color5 | darken(0.1) }}", + "warning": "{{ color5 | lighten(0.1) }}", + + "highlight": "{{ color6 | lighten(0.2) }}", + "rippleEffect": "{{ color6 | lighten(0.2) }}", + + "onAccent": "{{ background }}", + "outline": "{{ background | lighten(0.3) }}", + + "shadow": "{{ background }}B3", + "overlay": "{{ background }}CC" +} diff --git a/Templates/wallust.toml b/Templates/wallust.toml new file mode 100644 index 0000000..f68156a --- /dev/null +++ b/Templates/wallust.toml @@ -0,0 +1,47 @@ +# wallust v3.3 +# +# You can copy this file to ~/.config/wallust/wallust.toml (keep in mind is a sample config) + +# SIMPLE TUTORIAL, or `man wallust.5`: +# https://explosion-mental.codeberg.page/wallust/ +# +# If comming from v2: https://explosion-mental.codeberg.page/wallust/v3.html#wallusttoml + +# Global section - values below can be overwritten by command line flags + +# How the image is parse, in order to get the colors: +# full - resized - wal - thumb - fastresize - kmeans +backend = "resized" + +# What color space to use to produce and select the most prominent colors: +# lab - labmixed - lch - lchmixed +color_space = "labmixed" + +# Use the most prominent colors in a way that makes sense, a scheme color palette: +# dark - dark16 - darkcomp - darkcomp16 +# light - light16 - lightcomp - lightcomp16 +# harddark - harddark16 - harddarkcomp - harddarkcomp16 +# softdark - softdark16 - softdarkcomp - softdarkcomp16 +# softlight - softlight16 - softlightcomp - softlightcomp16 +palette = "dark" + +# Ensures a "readable contrast" (OPTIONAL, disabled by default) +# Should only be enabled when you notice an unreadable contrast frequently happening +# with your images. The reference color for the contrast is the background color. +check_contrast = true + +# Color saturation, between [1% and 100%] (OPTIONAL, disabled by default) +# usually something higher than 50 increases the saturation and below +# decreases it (on a scheme with strong and vivid colors) +#saturation = 50 + +# Alpha value for templating, by default 100 (no other use whatsoever) +#alpha = 100 + +[templates] +# NOTE: prefer '' over "" for paths, avoids escaping. +# template: A RELATIVE path that points to `~/.config/wallust/template` (depends on platform) +# target: ABSOLUTE path in which to place a file with generated templated values. +# ยก If either one is a directory, then both SHOULD be one. ! +# zathura = { template = 'zathura', target = '~/.config/zathura/zathurarc' } +Quickshell = { template = 'quickshell.json', target = 'Settings/Theme.json' } \ No newline at end of file diff --git a/Widgets/Background.qml b/Widgets/Background.qml index 574b04c..5c40efd 100644 --- a/Widgets/Background.qml +++ b/Widgets/Background.qml @@ -1,12 +1,13 @@ import QtQuick import Quickshell import Quickshell.Wayland -import qs.Helpers +import qs.Services import qs.Settings ShellRoot { - property string wallpaperSource: Settings.currentWallpaper !== "" ? Settings.currentWallpaper : "/home/lysec/nixos/assets/wallpapers/lantern.png" + property string wallpaperSource: WallpaperManager.currentWallpaper !== "" && !Settings.useSWWW ? WallpaperManager.currentWallpaper : "" PanelWindow { + visible: wallpaperSource !== "" anchors { bottom: true top: true @@ -24,6 +25,7 @@ ShellRoot { anchors.fill: parent fillMode: Image.PreserveAspectCrop source: wallpaperSource + visible: wallpaperSource !== "" cache: true smooth: true } diff --git a/Widgets/LockScreen.qml b/Widgets/LockScreen.qml index 8070f81..143a71c 100644 --- a/Widgets/LockScreen.qml +++ b/Widgets/LockScreen.qml @@ -8,7 +8,7 @@ import Quickshell import Quickshell.Services.Pam import Quickshell.Io import qs.Settings -import qs.Helpers +import qs.Services import "../Helpers/Weather.js" as WeatherHelper WlSessionLock { @@ -127,7 +127,7 @@ WlSessionLock { id: lockBgImage anchors.fill: parent fillMode: Image.PreserveAspectCrop - source: Settings.currentWallpaper !== "" ? Settings.currentWallpaper : "/home/lysec/nixos/assets/wallpapers/lantern.png" + source: WallpaperManager.currentWallpaper !== "" ? WallpaperManager.currentWallpaper : "" cache: true smooth: true sourceSize.width: 2560 diff --git a/Widgets/Overview.qml b/Widgets/Overview.qml index 6a952ad..ff136ba 100644 --- a/Widgets/Overview.qml +++ b/Widgets/Overview.qml @@ -2,12 +2,13 @@ import QtQuick import Quickshell import Quickshell.Wayland import Qt5Compat.GraphicalEffects -import qs.Helpers +import qs.Services import qs.Settings ShellRoot { - property string wallpaperSource: Settings.currentWallpaper !== "" ? Settings.currentWallpaper : "/home/lysec/nixos/assets/wallpapers/lantern.png" + property string wallpaperSource: WallpaperManager.currentWallpaper !== "" && !Settings.useSWWW ? WallpaperManager.currentWallpaper : "" PanelWindow { + visible: wallpaperSource !== "" anchors { top: true bottom: true @@ -25,10 +26,11 @@ ShellRoot { source: wallpaperSource cache: true smooth: true - visible: true // Show the original for FastBlur input + visible: wallpaperSource !== "" // Show the original for FastBlur input } FastBlur { anchors.fill: parent + visible: wallpaperSource !== "" source: bgImage radius: 24 // Adjust blur strength as needed transparentBorder: true diff --git a/Widgets/Sidebar/Config/ProfileSettings.qml b/Widgets/Sidebar/Config/ProfileSettings.qml index c47b226..016ec8b 100644 --- a/Widgets/Sidebar/Config/ProfileSettings.qml +++ b/Widgets/Sidebar/Config/ProfileSettings.qml @@ -114,7 +114,6 @@ Rectangle { inputMethodHints: Qt.ImhNone onTextChanged: { Settings.profileImage = text - Settings.saveSettings() } MouseArea { anchors.fill: parent @@ -212,7 +211,6 @@ Rectangle { inputMethodHints: Qt.ImhUrlCharactersOnly onTextChanged: { Settings.videoPath = text - Settings.saveSettings() } MouseArea { anchors.fill: parent diff --git a/Widgets/Sidebar/Config/SettingsModal.qml b/Widgets/Sidebar/Config/SettingsModal.qml index 085e175..05311c1 100644 --- a/Widgets/Sidebar/Config/SettingsModal.qml +++ b/Widgets/Sidebar/Config/SettingsModal.qml @@ -4,6 +4,7 @@ import QtQuick.Controls 2.15 import Quickshell import Quickshell.Wayland import qs.Settings +import qs.Services PanelWindow { id: settingsModal @@ -26,6 +27,14 @@ PanelWindow { property string tempProfileImage: (Settings.profileImage !== undefined && Settings.profileImage !== null) ? Settings.profileImage : "" property string tempWallpaperFolder: (Settings.wallpaperFolder !== undefined && Settings.wallpaperFolder !== null) ? Settings.wallpaperFolder : "" property bool tempShowActiveWindowIcon: Settings.showActiveWindowIcon + property bool tempUseSWWW: Settings.useSWWW + property bool tempRandomWallpaper: Settings.randomWallpaper + property bool tempUseWallpaperTheme: Settings.useWallpaperTheme + property int tempWallpaperInterval: Settings.wallpaperInterval + property string tempWallpaperResize: Settings.wallpaperResize + property int tempTransitionFps: Settings.transitionFps + property string tempTransitionType: Settings.transitionType + property real tempTransitionDuration: Settings.transitionDuration Rectangle { anchors.fill: parent @@ -138,10 +147,43 @@ PanelWindow { title: "Wallpaper" expanded: false WallpaperSettings { + id: wallpaperSettings wallpaperFolder: (typeof tempWallpaperFolder !== 'undefined' && tempWallpaperFolder !== null) ? tempWallpaperFolder : "" + useSWWW: tempUseSWWW + randomWallpaper: tempRandomWallpaper + useWallpaperTheme: tempUseWallpaperTheme + wallpaperInterval: tempWallpaperInterval + wallpaperResize: tempWallpaperResize + transitionFps: tempTransitionFps + transitionType: tempTransitionType + transitionDuration: tempTransitionDuration onWallpaperFolderEdited: function (folder) { tempWallpaperFolder = folder; } + onUseSWWWChangedUpdated: function(useSWWW) { + tempUseSWWW = useSWWW; + } + onRandomWallpaperChangedUpdated: function(randomWallpaper) { + tempRandomWallpaper = randomWallpaper; + } + onUseWallpaperThemeChangedUpdated: function(useWallpaperTheme) { + tempUseWallpaperTheme = useWallpaperTheme; + } + onWallpaperIntervalChangedUpdated: function(wallpaperInterval) { + tempWallpaperInterval = wallpaperInterval; + } + onWallpaperResizeChangedUpdated: function(resize) { + tempWallpaperResize = resize; + } + onTransitionFpsChangedUpdated: function(fps) { + tempTransitionFps = fps; + } + onTransitionTypeChangedUpdated: function(type) { + tempTransitionType = type; + } + onTransitionDurationChangedUpdated: function(duration) { + tempTransitionDuration = duration; + } } } } @@ -174,6 +216,14 @@ PanelWindow { Settings.profileImage = (typeof tempProfileImage !== 'undefined' && tempProfileImage !== null) ? tempProfileImage : ""; Settings.wallpaperFolder = (typeof tempWallpaperFolder !== 'undefined' && tempWallpaperFolder !== null) ? tempWallpaperFolder : ""; Settings.showActiveWindowIcon = tempShowActiveWindowIcon; + Settings.useSWWW = tempUseSWWW; + Settings.randomWallpaper = tempRandomWallpaper; + Settings.useWallpaperTheme = tempUseWallpaperTheme; + Settings.wallpaperInterval = tempWallpaperInterval; + Settings.wallpaperResize = tempWallpaperResize; + Settings.transitionFps = tempTransitionFps; + Settings.transitionType = tempTransitionType; + Settings.transitionDuration = tempTransitionDuration; Settings.saveSettings(); if (typeof weather !== 'undefined' && weather) { weather.fetchCityWeather(); @@ -194,6 +244,17 @@ PanelWindow { tempWallpaperFolder = (Settings.wallpaperFolder !== undefined && Settings.wallpaperFolder !== null) ? Settings.wallpaperFolder : ""; if (tempWallpaperFolder === undefined || tempWallpaperFolder === null) tempWallpaperFolder = ""; + + // Initialize wallpaper settings + tempUseSWWW = Settings.useSWWW; + tempRandomWallpaper = Settings.randomWallpaper; + tempUseWallpaperTheme = Settings.useWallpaperTheme; + tempWallpaperInterval = Settings.wallpaperInterval; + tempWallpaperResize = Settings.wallpaperResize; + tempTransitionFps = Settings.transitionFps; + tempTransitionType = Settings.transitionType; + tempTransitionDuration = Settings.transitionDuration; + visible = true; // Force focus on the text input after a short delay focusTimer.start(); diff --git a/Widgets/Sidebar/Config/WallpaperSettings.qml b/Widgets/Sidebar/Config/WallpaperSettings.qml index 8172c43..8c9a210 100644 --- a/Widgets/Sidebar/Config/WallpaperSettings.qml +++ b/Widgets/Sidebar/Config/WallpaperSettings.qml @@ -6,13 +6,29 @@ import qs.Settings Rectangle { id: wallpaperSettingsCard Layout.fillWidth: true - Layout.preferredHeight: 100 + Layout.preferredHeight: 680 color: Theme.surface radius: 18 // Property for binding property string wallpaperFolder: "" signal wallpaperFolderEdited(string folder) + property bool useSWWW: false + signal useSWWWChangedUpdated(bool useSWWW) + property bool randomWallpaper: false + signal randomWallpaperChangedUpdated(bool randomWallpaper) + property bool useWallpaperTheme: false + signal useWallpaperThemeChangedUpdated(bool useWallpaperTheme) + property int wallpaperInterval: 300 + signal wallpaperIntervalChangedUpdated(int wallpaperInterval) + property string wallpaperResize: "crop" + signal wallpaperResizeChangedUpdated(string resize) + property int transitionFps: 60 + signal transitionFpsChangedUpdated(int fps) + property string transitionType: "random" + signal transitionTypeChangedUpdated(string type) + property real transitionDuration: 1.1 + signal transitionDurationChangedUpdated(real duration) ColumnLayout { anchors.fill: parent @@ -75,5 +91,561 @@ Rectangle { } } } + + // Use SWWW Setting + RowLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Use SWWW" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + // Custom Material 3 Switch + Rectangle { + id: swwwSwitch + width: 52 + height: 32 + radius: 16 + color: useSWWW ? Theme.accentPrimary : Theme.surfaceVariant + border.color: useSWWW ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: swwwThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: useSWWW ? swwwSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { duration: 200; easing.type: Easing.OutCubic } + } + } + + MouseArea { + anchors.fill: parent + onClicked: { + useSWWWChangedUpdated(!useSWWW) + } + } + } + } + + // Random Wallpaper Setting + RowLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Random Wallpaper" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + // Custom Material 3 Switch + Rectangle { + id: randomWallpaperSwitch + width: 52 + height: 32 + radius: 16 + color: randomWallpaper ? Theme.accentPrimary : Theme.surfaceVariant + border.color: randomWallpaper ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: randomWallpaperThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: randomWallpaper ? randomWallpaperSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { duration: 200; easing.type: Easing.OutCubic } + } + } + + MouseArea { + anchors.fill: parent + onClicked: { + randomWallpaperChangedUpdated(!randomWallpaper) + } + } + } + } + + // Use Wallpaper Theme Setting + RowLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Use Wallpaper Theme" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + // Custom Material 3 Switch + Rectangle { + id: wallpaperThemeSwitch + width: 52 + height: 32 + radius: 16 + color: useWallpaperTheme ? Theme.accentPrimary : Theme.surfaceVariant + border.color: useWallpaperTheme ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: wallpaperThemeThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: useWallpaperTheme ? wallpaperThemeSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { duration: 200; easing.type: Easing.OutCubic } + } + } + + MouseArea { + anchors.fill: parent + onClicked: { + useWallpaperThemeChangedUpdated(!useWallpaperTheme) + } + } + } + } + + // Wallpaper Interval Setting + ColumnLayout { + spacing: 12 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + Layout.fillWidth: true + Text { + text: "Wallpaper Interval (seconds)" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + Text { + text: wallpaperInterval + font.pixelSize: 13 + color: Theme.textPrimary + } + } + + Slider { + id: intervalSlider + Layout.fillWidth: true + from: 10 + to: 900 + stepSize: 10 + value: wallpaperInterval + snapMode: Slider.SnapAlways + + background: Rectangle { + x: intervalSlider.leftPadding + y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 + implicitWidth: 200 + implicitHeight: 4 + width: intervalSlider.availableWidth + height: implicitHeight + radius: 2 + color: Theme.surfaceVariant + + Rectangle { + width: intervalSlider.visualPosition * parent.width + height: parent.height + color: Theme.accentPrimary + radius: 2 + } + } + + handle: Rectangle { + x: intervalSlider.leftPadding + intervalSlider.visualPosition * (intervalSlider.availableWidth - width) + y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 + implicitWidth: 20 + implicitHeight: 20 + radius: 10 + color: intervalSlider.pressed ? Theme.surfaceVariant : Theme.surface + border.color: Theme.accentPrimary + border.width: 2 + } + + onMoved: { + wallpaperIntervalChangedUpdated(Math.round(value)) + } + } + } + + // Resize Mode Setting + ColumnLayout { + spacing: 12 + Layout.fillWidth: true + Layout.topMargin: 16 + + Text { + text: "Resize Mode" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + ComboBox { + id: resizeComboBox + Layout.fillWidth: true + Layout.preferredHeight: 40 + model: ["no", "crop", "fit", "stretch"] + currentIndex: model.indexOf(wallpaperResize) + + background: Rectangle { + implicitWidth: 120 + implicitHeight: 40 + color: Theme.surfaceVariant + border.color: resizeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + radius: 8 + } + + contentItem: Text { + leftPadding: 12 + rightPadding: resizeComboBox.indicator.width + resizeComboBox.spacing + text: resizeComboBox.displayText + font.family: Theme.fontFamily + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + indicator: Text { + x: resizeComboBox.width - width - 12 + y: resizeComboBox.topPadding + (resizeComboBox.availableHeight - height) / 2 + text: "arrow_drop_down" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: Theme.textPrimary + } + + popup: Popup { + y: resizeComboBox.height + width: resizeComboBox.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: resizeComboBox.popup.visible ? resizeComboBox.delegateModel : null + currentIndex: resizeComboBox.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator { } + } + + background: Rectangle { + color: Theme.surfaceVariant + border.color: Theme.outline + border.width: 1 + radius: 8 + } + } + + delegate: ItemDelegate { + width: resizeComboBox.width + contentItem: Text { + text: modelData + font.family: Theme.fontFamily + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + highlighted: resizeComboBox.highlightedIndex === index + + background: Rectangle { + color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" + } + } + + onActivated: { + wallpaperResizeChangedUpdated(model[index]) + } + } + } + + // Transition Type Setting + ColumnLayout { + spacing: 12 + Layout.fillWidth: true + Layout.topMargin: 16 + + Text { + text: "Transition Type" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + ComboBox { + id: transitionTypeComboBox + Layout.fillWidth: true + Layout.preferredHeight: 40 + model: ["none", "simple", "fade", "left", "right", "top", "bottom", "wipe", "wave", "grow", "center", "any", "outer", "random"] + currentIndex: model.indexOf(transitionType) + + background: Rectangle { + implicitWidth: 120 + implicitHeight: 40 + color: Theme.surfaceVariant + border.color: transitionTypeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + radius: 8 + } + + contentItem: Text { + leftPadding: 12 + rightPadding: transitionTypeComboBox.indicator.width + transitionTypeComboBox.spacing + text: transitionTypeComboBox.displayText + font.family: Theme.fontFamily + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + indicator: Text { + x: transitionTypeComboBox.width - width - 12 + y: transitionTypeComboBox.topPadding + (transitionTypeComboBox.availableHeight - height) / 2 + text: "arrow_drop_down" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: Theme.textPrimary + } + + popup: Popup { + y: transitionTypeComboBox.height + width: transitionTypeComboBox.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: transitionTypeComboBox.popup.visible ? transitionTypeComboBox.delegateModel : null + currentIndex: transitionTypeComboBox.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator { } + } + + background: Rectangle { + color: Theme.surfaceVariant + border.color: Theme.outline + border.width: 1 + radius: 8 + } + } + + delegate: ItemDelegate { + width: transitionTypeComboBox.width + contentItem: Text { + text: modelData + font.family: Theme.fontFamily + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + highlighted: transitionTypeComboBox.highlightedIndex === index + + background: Rectangle { + color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" + } + } + + onActivated: { + transitionTypeChangedUpdated(model[index]) + } + } + } + + // Transition FPS Setting + ColumnLayout { + spacing: 12 + Layout.fillWidth: true + Layout.topMargin: 16 + + RowLayout { + Layout.fillWidth: true + Text { + text: "Transition FPS" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + Text { + text: transitionFps + font.pixelSize: 13 + color: Theme.textPrimary + } + } + + Slider { + id: fpsSlider + Layout.fillWidth: true + from: 30 + to: 500 + stepSize: 5 + value: transitionFps + snapMode: Slider.SnapAlways + + background: Rectangle { + x: fpsSlider.leftPadding + y: fpsSlider.topPadding + fpsSlider.availableHeight / 2 - height / 2 + implicitWidth: 200 + implicitHeight: 4 + width: fpsSlider.availableWidth + height: implicitHeight + radius: 2 + color: Theme.surfaceVariant + + Rectangle { + width: fpsSlider.visualPosition * parent.width + height: parent.height + color: Theme.accentPrimary + radius: 2 + } + } + + handle: Rectangle { + x: fpsSlider.leftPadding + fpsSlider.visualPosition * (fpsSlider.availableWidth - width) + y: fpsSlider.topPadding + fpsSlider.availableHeight / 2 - height / 2 + implicitWidth: 20 + implicitHeight: 20 + radius: 10 + color: fpsSlider.pressed ? Theme.surfaceVariant : Theme.surface + border.color: Theme.accentPrimary + border.width: 2 + } + + onMoved: { + transitionFpsChangedUpdated(Math.round(value)) + } + } + } + + // Transition Duration Setting + ColumnLayout { + spacing: 12 + Layout.fillWidth: true + Layout.topMargin: 16 + + RowLayout { + Layout.fillWidth: true + Text { + text: "Transition Duration (seconds)" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + Text { + text: transitionDuration.toFixed(3) + font.pixelSize: 13 + color: Theme.textPrimary + } + } + + Slider { + id: durationSlider + Layout.fillWidth: true + from: 0.250 + to: 10.0 + stepSize: 0.050 + value: transitionDuration + snapMode: Slider.SnapAlways + + background: Rectangle { + x: durationSlider.leftPadding + y: durationSlider.topPadding + durationSlider.availableHeight / 2 - height / 2 + implicitWidth: 200 + implicitHeight: 4 + width: durationSlider.availableWidth + height: implicitHeight + radius: 2 + color: Theme.surfaceVariant + + Rectangle { + width: durationSlider.visualPosition * parent.width + height: parent.height + color: Theme.accentPrimary + radius: 2 + } + } + + handle: Rectangle { + x: durationSlider.leftPadding + durationSlider.visualPosition * (durationSlider.availableWidth - width) + y: durationSlider.topPadding + durationSlider.availableHeight / 2 - height / 2 + implicitWidth: 20 + implicitHeight: 20 + radius: 10 + color: durationSlider.pressed ? Theme.surfaceVariant : Theme.surface + border.color: Theme.accentPrimary + border.width: 2 + } + + onMoved: { + transitionDurationChangedUpdated(value) + } + } + } } } \ No newline at end of file diff --git a/Widgets/Sidebar/Panel/System.qml b/Widgets/Sidebar/Panel/System.qml index d407fa6..50d42be 100644 --- a/Widgets/Sidebar/Panel/System.qml +++ b/Widgets/Sidebar/Panel/System.qml @@ -232,7 +232,7 @@ Rectangle { anchors.fill: parent hoverEnabled: true onClicked: { - Processes.reboot() + reboot() systemMenu.visible = false } } @@ -270,7 +270,7 @@ Rectangle { anchors.fill: parent hoverEnabled: true onClicked: { - Processes.logout() + logout() systemMenu.visible = false } } @@ -308,7 +308,7 @@ Rectangle { anchors.fill: parent hoverEnabled: true onClicked: { - Processes.shutdown() + shutdown() systemMenu.visible = false } } @@ -342,6 +342,32 @@ Rectangle { } } + Process { + id: shutdownProcess + command: ["shutdown", "-h", "now"] + running: false + } + Process { + id: rebootProcess + command: ["reboot"] + running: false + } + Process { + id: logoutProcess + command: ["niri", "msg", "action", "quit", "--skip-confirmation"] + running: false + } + + function shutdown() { + shutdownProcess.running = true + } + function reboot() { + rebootProcess.running = true + } + function logout() { + logoutProcess.running = true + } + property bool panelVisible: false // Trigger initial update when panel becomes visible diff --git a/Widgets/Sidebar/Panel/WallpaperPanel.qml b/Widgets/Sidebar/Panel/WallpaperPanel.qml index e4d8101..45cdd91 100644 --- a/Widgets/Sidebar/Panel/WallpaperPanel.qml +++ b/Widgets/Sidebar/Panel/WallpaperPanel.qml @@ -4,6 +4,7 @@ import QtQuick.Controls 2.15 import Quickshell import Quickshell.Io import qs.Settings +import qs.Services PanelWindow { id: wallpaperPanelModal @@ -17,15 +18,11 @@ PanelWindow { margins.top: -24 property var wallpapers: [] - - Process { - id: listWallpapersProcess - running: visible - command: ["ls", Settings.wallpaperFolder !== undefined ? Settings.wallpaperFolder : ""] - stdout: StdioCollector { - onStreamFinished: { - wallpaperPanelModal.wallpapers = this.text.split("\n").filter(function(x){return x.length > 0}) - } + + Connections { + target: WallpaperManager + function onWallpaperListChanged() { + wallpapers = WallpaperManager.wallpaperList } } @@ -118,16 +115,16 @@ PanelWindow { anchors.margins: 4 color: Qt.darker(Theme.backgroundPrimary, 1.1) radius: 12 - border.color: Settings.currentWallpaper === (Settings.wallpaperFolder !== undefined ? Settings.wallpaperFolder : "") + "/" + modelData ? Theme.accentPrimary : Theme.outline - border.width: Settings.currentWallpaper === (Settings.wallpaperFolder !== undefined ? Settings.wallpaperFolder : "") + "/" + modelData ? 3 : 1 + border.color: Settings.currentWallpaper === modelData ? Theme.accentPrimary : Theme.outline + border.width: Settings.currentWallpaper === modelData ? 3 : 1 Image { id: wallpaperImage anchors.fill: parent anchors.margins: 4 - source: (Settings.wallpaperFolder !== undefined ? Settings.wallpaperFolder : "") + "/" + modelData + source: modelData fillMode: Image.PreserveAspectCrop asynchronous: true - cache: false + cache: true sourceSize.width: Math.min(width, 150) sourceSize.height: Math.min(height, 90) } @@ -135,9 +132,7 @@ PanelWindow { anchors.fill: parent hoverEnabled: true onClicked: { - var selectedPath = (Settings.wallpaperFolder !== undefined ? Settings.wallpaperFolder : "") + "/" + modelData; - Settings.currentWallpaper = selectedPath; - Settings.saveSettings(); + WallpaperManager.changeWallpaper(modelData); } } } diff --git a/shell.qml b/shell.qml index f669f72..0b94c67 100644 --- a/shell.qml +++ b/shell.qml @@ -16,7 +16,7 @@ Scope { property alias appLauncherPanel: appLauncherPanel Component.onCompleted: { - Quickshell.shell = root + Quickshell.shell = root; } Bar { @@ -35,7 +35,7 @@ Scope { NotificationServer { id: notificationServer - onNotification: function(notification) { + onNotification: function (notification) { console.log("Notification received:", notification.appName); notification.tracked = true; notificationPopup.addNotification(notification); @@ -48,9 +48,7 @@ Scope { } property var defaultAudioSink: Pipewire.defaultAudioSink - property int volume: defaultAudioSink && defaultAudioSink.audio - ? Math.round(defaultAudioSink.audio.volume * 100) - : 0 + property int volume: defaultAudioSink && defaultAudioSink.audio ? Math.round(defaultAudioSink.audio.volume * 100) : 0 PwObjectTracker { objects: [Pipewire.defaultAudioSink] @@ -60,4 +58,16 @@ Scope { appLauncherPanel: appLauncherPanel lockScreen: lockScreen } + + Connections { + function onReloadCompleted() { + Quickshell.inhibitReloadPopup(); + } + + function onReloadFailed() { + Quickshell.inhibitReloadPopup(); + } + + target: Quickshell + } }