From de94d94265d8ab66dc3c695ed043efc75556243c Mon Sep 17 00:00:00 2001 From: Quadbyte Date: Sun, 3 Aug 2025 15:37:13 -0400 Subject: [PATCH] Wallpaper refining (#81) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Wallpaper refining - Swww: allow all images formats supported by Swww but only show the one we can render in the panel - Fix: timer should be restarted when selecting a wallpaper - Fix: avoid recomputing selected thumbnail by altering the border width - Removed LazyLoader as it was not helping * Wallpapers changes - revert: maintaining a single list all wallpapers, support only basic images format. - settings: reordered wallpapers settings and made the Swww settings conditionals to activating the useSWWW toggle * Fix broken avatar display in Panel, Settings and LockScreen --------- Co-authored-by: Sébastien Atoch --- Components/Avatar.qml | 13 +- Services/WallpaperManager.qml | 15 +- Widgets/LockScreen/LockScreen.qml | 11 +- Widgets/Sidebar/Config/ProfileSettings.qml | 20 +- Widgets/Sidebar/Config/WallpaperSettings.qml | 259 ++++++++++++------- Widgets/Sidebar/Panel/PanelPopup.qml | 26 +- Widgets/Sidebar/Panel/WallpaperPanel.qml | 27 +- 7 files changed, 228 insertions(+), 143 deletions(-) diff --git a/Components/Avatar.qml b/Components/Avatar.qml index 03c690f..0c879bb 100644 --- a/Components/Avatar.qml +++ b/Components/Avatar.qml @@ -7,6 +7,11 @@ import QtQuick.Effects Item { anchors.fill: parent + anchors.leftMargin: 2 + anchors.rightMargin: 2 + anchors.topMargin: 2 + anchors.bottomMargin: 2 + IconImage { id: avatarImage anchors.fill: parent @@ -18,7 +23,7 @@ Item { } MultiEffect { - anchors.fill: avatarImage + anchors.fill: parent source: avatarImage maskEnabled: true maskSource: mask @@ -27,13 +32,11 @@ Item { Item { id: mask - - anchors.fill: avatarImage + anchors.fill: parent layer.enabled: true visible: false - Rectangle { - anchors.fill: avatarImage + anchors.fill: parent radius: avatarImage.width / 2 } } diff --git a/Services/WallpaperManager.qml b/Services/WallpaperManager.qml index 3b50d9a..f22f0fb 100644 --- a/Services/WallpaperManager.qml +++ b/Services/WallpaperManager.qml @@ -15,7 +15,7 @@ Singleton { toggleRandomWallpaper(); } } - property string wallpaperDirectory: Settings.settings.wallpaperFolder + property var wallpaperList: [] property string currentWallpaper: Settings.settings.currentWallpaper property bool scanning: false @@ -46,6 +46,11 @@ Singleton { } changeWallpaperProcess.running = true; } + + if (randomWallpaperTimer.running) { + randomWallpaperTimer.restart(); + } + generateTheme(); } @@ -91,15 +96,17 @@ Singleton { FolderListModel { id: folderModel - nameFilters: Settings.settings.useSWWW ? ["*.avif", "*.jpg", "*.jpeg", "*.png", "*.gif", "*.pnm", "*.tga", "*.tiff", "*.webp", "*.bmp", "*.farbfeld"] : ["*.jpg", "*.jpeg", "*.png", "*.gif", "*.pnm", "*.bmp"] + // Swww supports many images format but Quickshell only support a subset of those. + nameFilters: ["*.jpg", "*.jpeg", "*.png", "*.gif", "*.pnm", "*.bmp"] showDirs: false sortField: FolderListModel.Name onStatusChanged: { if (status === FolderListModel.Ready) { var files = []; + var filesSwww = []; for (var i = 0; i < count; i++) { - var fileph = (Settings.settings.wallpaperFolder !== undefined ? Settings.settings.wallpaperFolder : "") + "/" + get(i, "fileName"); - files.push(fileph); + var filepath = (Settings.settings.wallpaperFolder !== undefined ? Settings.settings.wallpaperFolder : "") + "/" + get(i, "fileName"); + files.push(filepath); } wallpaperList = files; scanning = false; diff --git a/Widgets/LockScreen/LockScreen.qml b/Widgets/LockScreen/LockScreen.qml index 6f72085..8e19ff2 100644 --- a/Widgets/LockScreen/LockScreen.qml +++ b/Widgets/LockScreen/LockScreen.qml @@ -161,14 +161,21 @@ WlSessionLock { radius: 40 color: Theme.accentPrimary + Rectangle { + anchors.fill: parent + color: "transparent" + radius: 40 + border.color: Theme.accentPrimary + border.width: 3 + z: 2 + } + Avatar {} layer.enabled: true layer.effect: MultiEffect { shadowEnabled: true shadowColor: Theme.accentPrimary - // radius: 8 - // samples: 16 } } diff --git a/Widgets/Sidebar/Config/ProfileSettings.qml b/Widgets/Sidebar/Config/ProfileSettings.qml index 20fc980..3451e25 100644 --- a/Widgets/Sidebar/Config/ProfileSettings.qml +++ b/Widgets/Sidebar/Config/ProfileSettings.qml @@ -55,13 +55,21 @@ Rectangle { spacing: 8 Layout.fillWidth: true + // Profile image Rectangle { - width: 40 - height: 40 - radius: 20 - color: Theme.surfaceVariant - border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 + width: 48 + height: 48 + radius: 24 + + // Border + Rectangle { + anchors.fill: parent + color: "transparent" + radius: 24 + border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 2 + z: 2 + } Avatar {} } diff --git a/Widgets/Sidebar/Config/WallpaperSettings.qml b/Widgets/Sidebar/Config/WallpaperSettings.qml index c824721..2f5b66d 100644 --- a/Widgets/Sidebar/Config/WallpaperSettings.qml +++ b/Widgets/Sidebar/Config/WallpaperSettings.qml @@ -1,12 +1,13 @@ import QtQuick -import QtQuick.Layouts import QtQuick.Controls +import QtQuick.Layouts import qs.Settings Rectangle { id: wallpaperSettingsCard + Layout.fillWidth: true - Layout.preferredHeight: 720 + Layout.preferredHeight: Settings.settings.useSWWW ? 720 : 360 color: Theme.surface radius: 18 @@ -15,26 +16,29 @@ Rectangle { anchors.margins: 18 spacing: 12 - // Header - RowLayout { - Layout.fillWidth: true - spacing: 12 - Text { - text: "image" - font.family: "Material Symbols Outlined" - font.pixelSize: 20 - color: Theme.accentPrimary - } - Text { - text: "Wallpaper Settings" - font.family: Theme.fontFamily - font.pixelSize: 16 - font.bold: true - color: Theme.textPrimary + // Header + RowLayout { Layout.fillWidth: true - } - } + spacing: 12 + Text { + text: "image" + font.family: "Material Symbols Outlined" + font.pixelSize: 20 + color: Theme.accentPrimary + } + + Text { + text: "Wallpaper Settings" + font.family: Theme.fontFamily + font.pixelSize: 16 + font.bold: true + color: Theme.textPrimary + Layout.fillWidth: true + } + } + + // Wallpaper Path ColumnLayout { spacing: 8 Layout.fillWidth: true @@ -55,8 +59,10 @@ Rectangle { color: Theme.surfaceVariant border.color: folderInput.activeFocus ? Theme.accentPrimary : Theme.outline border.width: 1 + TextInput { id: folderInput + anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top @@ -77,70 +83,20 @@ Rectangle { onTextChanged: { Settings.settings.wallpaperFolder = text; } + MouseArea { anchors.fill: parent cursorShape: Qt.IBeamCursor onClicked: folderInput.forceActiveFocus() } + } + } + } - // 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: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.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: Settings.settings.useSWWW ? swwwSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.useSWWW = !Settings.settings.useSWWW; - } - } - } - } // Random Wallpaper Setting RowLayout { @@ -162,6 +118,7 @@ Rectangle { // Custom Material 3 Switch Rectangle { id: randomWallpaperSwitch + width: 52 height: 32 radius: 16 @@ -171,6 +128,7 @@ Rectangle { Rectangle { id: randomWallpaperThumb + width: 28 height: 28 radius: 14 @@ -185,7 +143,9 @@ Rectangle { duration: 200 easing.type: Easing.OutCubic } + } + } MouseArea { @@ -195,7 +155,9 @@ Rectangle { Settings.settings.randomWallpaper = !Settings.settings.randomWallpaper; } } + } + } // Use Wallpaper Theme Setting @@ -218,6 +180,7 @@ Rectangle { // Custom Material 3 Switch Rectangle { id: wallpaperThemeSwitch + width: 52 height: 32 radius: 16 @@ -227,6 +190,7 @@ Rectangle { Rectangle { id: wallpaperThemeThumb + width: 28 height: 28 radius: 14 @@ -241,7 +205,9 @@ Rectangle { duration: 200 easing.type: Easing.OutCubic } + } + } MouseArea { @@ -251,7 +217,9 @@ Rectangle { Settings.settings.useWallpaperTheme = !Settings.settings.useWallpaperTheme; } } + } + } // Wallpaper Interval Setting @@ -262,6 +230,7 @@ Rectangle { RowLayout { Layout.fillWidth: true + Text { text: "Wallpaper Interval (seconds)" font.pixelSize: 13 @@ -278,16 +247,21 @@ Rectangle { font.pixelSize: 13 color: Theme.textPrimary } + } Slider { id: intervalSlider + Layout.fillWidth: true from: 10 to: 900 stepSize: 10 value: Settings.settings.wallpaperInterval snapMode: Slider.SnapAlways + onMoved: { + Settings.settings.wallpaperInterval = Math.round(value); + } background: Rectangle { x: intervalSlider.leftPadding @@ -305,6 +279,7 @@ Rectangle { color: Theme.accentPrimary radius: 2 } + } handle: Rectangle { @@ -318,10 +293,70 @@ Rectangle { border.width: 2 } - onMoved: { - Settings.settings.wallpaperInterval = Math.round(value); - } } + + } + + // 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: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.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: Settings.settings.useSWWW ? swwwSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + + } + + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.useSWWW = !Settings.settings.useSWWW; + } + } + + } + } // Resize Mode Setting @@ -329,6 +364,7 @@ Rectangle { spacing: 12 Layout.fillWidth: true Layout.topMargin: 16 + visible: Settings.settings.useSWWW Text { text: "Resize Mode" @@ -339,10 +375,14 @@ Rectangle { ComboBox { id: resizeComboBox + Layout.fillWidth: true Layout.preferredHeight: 40 model: ["no", "crop", "fit", "stretch"] currentIndex: model.indexOf(Settings.settings.wallpaperResize) + onActivated: { + Settings.settings.wallpaperResize = model[index]; + } background: Rectangle { implicitWidth: 120 @@ -385,7 +425,9 @@ Rectangle { model: resizeComboBox.popup.visible ? resizeComboBox.delegateModel : null currentIndex: resizeComboBox.highlightedIndex - ScrollIndicator.vertical: ScrollIndicator {} + ScrollIndicator.vertical: ScrollIndicator { + } + } background: Rectangle { @@ -394,10 +436,13 @@ Rectangle { border.width: 1 radius: 16 } + } delegate: ItemDelegate { width: resizeComboBox.width + highlighted: resizeComboBox.highlightedIndex === index + contentItem: Text { text: modelData font.family: Theme.fontFamily @@ -406,17 +451,15 @@ Rectangle { verticalAlignment: Text.AlignVCenter elide: Text.ElideRight } - highlighted: resizeComboBox.highlightedIndex === index background: Rectangle { color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" } + } - onActivated: { - Settings.settings.wallpaperResize = model[index]; - } } + } // Transition Type Setting @@ -424,6 +467,7 @@ Rectangle { spacing: 12 Layout.fillWidth: true Layout.topMargin: 16 + visible: Settings.settings.useSWWW Text { text: "Transition Type" @@ -434,10 +478,14 @@ Rectangle { 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(Settings.settings.transitionType) + onActivated: { + Settings.settings.transitionType = model[index]; + } background: Rectangle { implicitWidth: 120 @@ -480,7 +528,9 @@ Rectangle { model: transitionTypeComboBox.popup.visible ? transitionTypeComboBox.delegateModel : null currentIndex: transitionTypeComboBox.highlightedIndex - ScrollIndicator.vertical: ScrollIndicator {} + ScrollIndicator.vertical: ScrollIndicator { + } + } background: Rectangle { @@ -489,10 +539,13 @@ Rectangle { border.width: 1 radius: 16 } + } delegate: ItemDelegate { width: transitionTypeComboBox.width + highlighted: transitionTypeComboBox.highlightedIndex === index + contentItem: Text { text: modelData font.family: Theme.fontFamily @@ -501,17 +554,15 @@ Rectangle { verticalAlignment: Text.AlignVCenter elide: Text.ElideRight } - highlighted: transitionTypeComboBox.highlightedIndex === index background: Rectangle { color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" } + } - onActivated: { - Settings.settings.transitionType = model[index]; - } } + } // Transition FPS Setting @@ -519,9 +570,11 @@ Rectangle { spacing: 12 Layout.fillWidth: true Layout.topMargin: 16 + visible: Settings.settings.useSWWW RowLayout { Layout.fillWidth: true + Text { text: "Transition FPS" font.pixelSize: 13 @@ -538,16 +591,21 @@ Rectangle { font.pixelSize: 13 color: Theme.textPrimary } + } Slider { id: fpsSlider + Layout.fillWidth: true from: 30 to: 500 stepSize: 5 value: Settings.settings.transitionFps snapMode: Slider.SnapAlways + onMoved: { + Settings.settings.transitionFps = Math.round(value); + } background: Rectangle { x: fpsSlider.leftPadding @@ -565,6 +623,7 @@ Rectangle { color: Theme.accentPrimary radius: 2 } + } handle: Rectangle { @@ -578,10 +637,8 @@ Rectangle { border.width: 2 } - onMoved: { - Settings.settings.transitionFps = Math.round(value); - } } + } // Transition Duration Setting @@ -589,9 +646,11 @@ Rectangle { spacing: 12 Layout.fillWidth: true Layout.topMargin: 16 - + visible: Settings.settings.useSWWW + RowLayout { Layout.fillWidth: true + Text { text: "Transition Duration (seconds)" font.pixelSize: 13 @@ -608,16 +667,21 @@ Rectangle { font.pixelSize: 13 color: Theme.textPrimary } + } Slider { id: durationSlider + Layout.fillWidth: true - from: 0.250 - to: 10.0 - stepSize: 0.050 + from: 0.25 + to: 10 + stepSize: 0.05 value: Settings.settings.transitionDuration snapMode: Slider.SnapAlways + onMoved: { + Settings.settings.transitionDuration = value; + } background: Rectangle { x: durationSlider.leftPadding @@ -635,6 +699,7 @@ Rectangle { color: Theme.accentPrimary radius: 2 } + } handle: Rectangle { @@ -648,10 +713,10 @@ Rectangle { border.width: 2 } - onMoved: { - Settings.settings.transitionDuration = value; - } } + } + } + } diff --git a/Widgets/Sidebar/Panel/PanelPopup.qml b/Widgets/Sidebar/Panel/PanelPopup.qml index 1a68a23..9ccff1c 100644 --- a/Widgets/Sidebar/Panel/PanelPopup.qml +++ b/Widgets/Sidebar/Panel/PanelPopup.qml @@ -59,8 +59,8 @@ PanelWithOverlay { if (sidebarPopupRect.settingsModal && sidebarPopupRect.settingsModal.visible) { sidebarPopupRect.settingsModal.visible = false; } - if (wallpaperPanelLoader && wallpaperPanelLoader.active) { - wallpaperPanelLoader.active = false; + if (wallpaperPanel && wallpaperPanel.visible) { + wallpaperPanel.visible = false; } if (sidebarPopupRect.wifiPanelModal && sidebarPopupRect.wifiPanelModal.visible) { sidebarPopupRect.wifiPanelModal.visible = false; @@ -312,7 +312,9 @@ PanelWithOverlay { onSettingsRequested: { settingsModal.visible = true; } - onWallpaperRequested: wallpaperPanelLoader.active = true; + onWallpaperRequested: { + wallpaperPanel.visible = true; + } } } Keys.onEscapePressed: sidebarPopupRect.hidePopup() @@ -399,18 +401,12 @@ PanelWithOverlay { } } - LazyLoader { - id: wallpaperPanelLoader - loading: false - - WallpaperPanel { - // Need to keep this visible so it shows once loaded - visible: true - Component.onCompleted: { - if (parent) { - anchors.top = parent.top; - anchors.right = parent.right; - } + WallpaperPanel { + id: wallpaperPanel + Component.onCompleted: { + if (parent) { + anchors.top = parent.top; + anchors.right = parent.right; } } } diff --git a/Widgets/Sidebar/Panel/WallpaperPanel.qml b/Widgets/Sidebar/Panel/WallpaperPanel.qml index 53361ca..4a69a0f 100644 --- a/Widgets/Sidebar/Panel/WallpaperPanel.qml +++ b/Widgets/Sidebar/Panel/WallpaperPanel.qml @@ -30,7 +30,7 @@ PanelWindow { } onVisibleChanged: { - if (wallpaperPanelLoader.active) { + if (wallpaperPanel.visible) { wallpapers = WallpaperManager.wallpaperList } else { wallpapers = [] @@ -81,7 +81,9 @@ PanelWindow { id: closeButtonArea anchors.fill: parent hoverEnabled: true - onClicked: wallpaperPanelLoader.active = false; + onClicked: { + wallpaperPanel.visible = false; + } cursorShape: Qt.PointingHandCursor } } @@ -114,7 +116,7 @@ PanelWindow { cellWidth: Math.max(120, (scrollView.width / 3) - 12) cellHeight: cellWidth * 0.6 model: wallpapers - cacheBuffer: 32 + cacheBuffer: 64 leftMargin: 8 rightMargin: 8 topMargin: 8 @@ -129,27 +131,24 @@ PanelWindow { color: Qt.darker(Theme.backgroundPrimary, 1.1) radius: 12 border.color: Settings.settings.currentWallpaper === modelData ? Theme.accentPrimary : Theme.outline - border.width: Settings.settings.currentWallpaper === modelData ? 3 : 1 + border.width: 2 Image { id: wallpaperImage anchors.fill: parent source: modelData fillMode: Image.PreserveAspectCrop asynchronous: true - cache: false + cache: true smooth: true mipmap: true - // Limit memory usage - sourceSize.width: 480 - sourceSize.height: 270 - } - Rectangle { - anchors.fill: parent - color: Theme.textPrimary - opacity: (wallpaperImage.status == Image.Ready) ? 0.0 : 1.0 + // Limit memory usage - FullHD/4 on width and height + sourceSize.width: Math.min(width, 480) + sourceSize.height: Math.min(height, 270) + // Opacity animation once image is ready + opacity: (wallpaperImage.status == Image.Ready) ? 1.0 : 0.0 Behavior on opacity { NumberAnimation { - duration: 500 + duration: 300 easing.type: Easing.OutCubic } }