From 1bd093db7fcc8f15404d2219dd7fe0a04a348f33 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Thu, 18 Sep 2025 17:55:30 +0200 Subject: [PATCH 01/14] WallpaperSelector overhaul: initial commit --- Commons/Settings.qml | 3 + Modules/SettingsPanel/SettingsPanel.qml | 19 +- Modules/SidePanel/Cards/UtilitiesCard.qml | 10 +- .../WallpaperSelectorPanel.qml | 282 ++++++++++++++++++ Services/WallpaperService.qml | 14 + shell.qml | 6 + 6 files changed, 310 insertions(+), 24 deletions(-) create mode 100644 Modules/WallpaperSelectorPanel/WallpaperSelectorPanel.qml diff --git a/Commons/Settings.qml b/Commons/Settings.qml index ed6b8499..3f909c53 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -177,6 +177,9 @@ Singleton { MatugenService.init() + // Ensure wallpapers are restored after settings have been loaded + WallpaperService.init() + FontService.init() HooksService.init() diff --git a/Modules/SettingsPanel/SettingsPanel.qml b/Modules/SettingsPanel/SettingsPanel.qml index 887954d3..55bef4a9 100644 --- a/Modules/SettingsPanel/SettingsPanel.qml +++ b/Modules/SettingsPanel/SettingsPanel.qml @@ -36,8 +36,7 @@ NPanel { Network, Notification, ScreenRecorder, - Wallpaper, - WallpaperSelector + Wallpaper } property int requestedTab: SettingsPanel.Tab.General @@ -92,10 +91,7 @@ NPanel { id: wallpaperTab Tabs.WallpaperTab {} } - Component { - id: wallpaperSelectorTab - Tabs.WallpaperSelectorTab {} - } + Component { id: screenRecorderTab Tabs.ScreenRecorderTab {} @@ -176,16 +172,7 @@ NPanel { "source": wallpaperTab }] - // Only add the Wallpaper Selector tab if the feature is enabled - if (Settings.data.wallpaper.enabled) { - newTabs.push({ - "id": SettingsPanel.Tab.WallpaperSelector, - "label": "Wallpaper Selector", - "icon": "settings-wallpaper-selector", - "source": wallpaperSelectorTab - }) - } - + // Wallpaper selector moved to its own panel newTabs.push({ "id": SettingsPanel.Tab.ScreenRecorder, "label": "Screen Recorder", diff --git a/Modules/SidePanel/Cards/UtilitiesCard.qml b/Modules/SidePanel/Cards/UtilitiesCard.qml index fd23adf0..9be59e3f 100644 --- a/Modules/SidePanel/Cards/UtilitiesCard.qml +++ b/Modules/SidePanel/Cards/UtilitiesCard.qml @@ -55,14 +55,8 @@ NBox { visible: Settings.data.wallpaper.enabled icon: "wallpaper-selector" tooltipText: "Left click: Open wallpaper selector.\nRight click: Set random wallpaper." - onClicked: { - var settingsPanel = PanelService.getPanel("settingsPanel") - settingsPanel.requestedTab = SettingsPanel.Tab.WallpaperSelector - settingsPanel.open() - } - onRightClicked: { - WallpaperService.setRandomWallpaper() - } + onClicked: PanelService.getPanel("wallpaperSelectorPanel")?.toggle(this) + onRightClicked: WallpaperService.setRandomWallpaper() } Item { diff --git a/Modules/WallpaperSelectorPanel/WallpaperSelectorPanel.qml b/Modules/WallpaperSelectorPanel/WallpaperSelectorPanel.qml new file mode 100644 index 00000000..33a16b27 --- /dev/null +++ b/Modules/WallpaperSelectorPanel/WallpaperSelectorPanel.qml @@ -0,0 +1,282 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import Quickshell +import Quickshell.Wayland +import qs.Commons +import qs.Services +import qs.Widgets + +NPanel { + id: root + + preferredWidth: 640 + preferredHeight: 480 + preferredWidthRatio: 0.4 + preferredHeightRatio: 0.5 + panelAnchorHorizontalCenter: true + panelAnchorVerticalCenter: true + panelKeyboardFocus: true + + // Local reactive state + property list wallpapersList: [] + property string currentWallpaper: "" + + function refreshForScreen() { + const name = Screen.name + wallpapersList = WallpaperService.getWallpapersList(name) + currentWallpaper = WallpaperService.getWallpaper(name) + } + + onOpened: refreshForScreen() + + Connections { + target: WallpaperService + function onWallpaperChanged(screenName, path) { + if (screenName === Screen.name) { + currentWallpaper = WallpaperService.getWallpaper(Screen.name) + } + } + function onWallpaperDirectoryChanged(screenName, directory) { + if (screenName === Screen.name) { + refreshForScreen() + } + } + function onWallpaperListChanged(screenName, count) { + if (screenName === Screen.name) { + refreshForScreen() + } + } + } + + panelContent: Rectangle { + color: Color.transparent + + ColumnLayout { + anchors.fill: parent + anchors.margins: Style.marginL * scaling + spacing: Style.marginM * scaling + + // Header + RowLayout { + Layout.fillWidth: true + spacing: Style.marginM * scaling + + NIcon { + icon: "settings-wallpaper-selector" + font.pointSize: Style.fontSizeXXL * scaling + color: Color.mPrimary + } + + NText { + text: "Wallpaper Selector" + font.pointSize: Style.fontSizeL * scaling + font.weight: Style.fontWeightBold + color: Color.mOnSurface + Layout.fillWidth: true + } + + NIconButton { + icon: "refresh" + tooltipText: "Refresh wallpaper list" + baseSize: Style.baseWidgetSize * 0.8 + onClicked: WallpaperService.refreshWallpapersList() + } + + NIconButton { + icon: "close" + tooltipText: "Close." + baseSize: Style.baseWidgetSize * 0.8 + onClicked: root.close() + } + } + + NDivider { + Layout.fillWidth: true + Layout.topMargin: Style.marginXL * scaling + Layout.bottomMargin: Style.marginXL * scaling + } + + // Scroll container mirrors SettingsPanel to avoid overflow and keep interactions smooth + Flickable { + Layout.fillWidth: true + Layout.fillHeight: true + pressDelay: 200 + + NScrollView { + id: scrollView + anchors.fill: parent + horizontalPolicy: ScrollBar.AlwaysOff + verticalPolicy: ScrollBar.AsNeeded + padding: Style.marginL * 0 * scaling + clip: true + + ColumnLayout { + width: scrollView.availableWidth + spacing: Style.marginM * scaling + + // Selector header removed (title and refresh are redundant here) + NToggle { + label: "Apply to all monitors" + description: "Apply selected wallpaper to all monitors at once." + checked: Settings.data.wallpaper.setWallpaperOnAllMonitors + onToggled: checked => Settings.data.wallpaper.setWallpaperOnAllMonitors = checked + visible: (wallpapersList.length > 0) + } + + // Grid container + Item { + visible: !WallpaperService.scanning + Layout.fillWidth: true + Layout.preferredHeight: Math.ceil(wallpapersList.length / wallpaperGridView.columns) * wallpaperGridView.cellHeight + + GridView { + id: wallpaperGridView + anchors.fill: parent + model: wallpapersList + interactive: false + clip: true + + property int columns: 5 + property int itemSize: Math.floor((width - leftMargin - rightMargin - (columns * Style.marginS * scaling)) / columns) + + cellWidth: Math.floor((width - leftMargin - rightMargin) / columns) + cellHeight: Math.floor(itemSize * 0.67) + Style.marginS * scaling + + leftMargin: Style.marginS * scaling + rightMargin: Style.marginS * scaling + topMargin: Style.marginS * scaling + bottomMargin: Style.marginS * scaling + + delegate: Rectangle { + id: wallpaperItem + + property string wallpaperPath: modelData + property bool isSelected: (wallpaperPath === currentWallpaper) + + width: wallpaperGridView.itemSize + height: Math.round(wallpaperGridView.itemSize * 0.67) + color: Color.transparent + + NImageCached { + id: img + imagePath: wallpaperPath + anchors.fill: parent + } + + Rectangle { + anchors.fill: parent + color: Color.transparent + border.color: isSelected ? Color.mSecondary : Color.mSurface + border.width: Math.max(1, Style.borderL * 1.5 * scaling) + } + + Rectangle { + anchors.top: parent.top + anchors.right: parent.right + anchors.margins: Style.marginS * scaling + width: 28 * scaling + height: 28 * scaling + radius: width / 2 + color: Color.mSecondary + border.color: Color.mOutline + border.width: Math.max(1, Style.borderS * scaling) + visible: isSelected + + NIcon { + icon: "check" + font.pointSize: Style.fontSizeM * scaling + font.weight: Style.fontWeightBold + color: Color.mOnSecondary + anchors.centerIn: parent + } + } + + Rectangle { + anchors.fill: parent + color: Color.mSurface + opacity: (mouseArea.containsMouse || isSelected) ? 0 : 0.3 + radius: parent.radius + Behavior on opacity { + NumberAnimation { + duration: Style.animationFast + } + } + } + + MouseArea { + id: mouseArea + anchors.fill: parent + acceptedButtons: Qt.LeftButton + hoverEnabled: true + onPressed: { + if (Settings.data.wallpaper.setWallpaperOnAllMonitors) { + WallpaperService.changeWallpaper(wallpaperPath, undefined) + } else { + WallpaperService.changeWallpaper(wallpaperPath, Screen.name) + } + } + } + } + } + } + + // Empty / scanning state + Rectangle { + color: Color.mSurface + radius: Style.radiusM * scaling + border.color: Color.mOutline + border.width: Math.max(1, Style.borderS * scaling) + visible: wallpapersList.length === 0 || WallpaperService.scanning + Layout.fillWidth: true + Layout.preferredHeight: 130 * scaling + + ColumnLayout { + anchors.fill: parent + visible: WallpaperService.scanning + NBusyIndicator { + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + } + } + + ColumnLayout { + anchors.fill: parent + visible: wallpapersList.length === 0 && !WallpaperService.scanning + Item { + Layout.fillHeight: true + } + NIcon { + icon: "folder-open" + font.pointSize: Style.fontSizeXXL * scaling + color: Color.mOnSurface + Layout.alignment: Qt.AlignHCenter + } + NText { + text: "No wallpaper found." + color: Color.mOnSurface + font.weight: Style.fontWeightBold + Layout.alignment: Qt.AlignHCenter + } + NText { + text: "Configure your wallpaper directory with images." + color: Color.mOnSurfaceVariant + wrapMode: Text.WordWrap + Layout.alignment: Qt.AlignHCenter + } + Item { + Layout.fillHeight: true + } + } + } + + NDivider { + Layout.fillWidth: true + Layout.topMargin: Style.marginXL * scaling + Layout.bottomMargin: Style.marginXL * scaling + } + } + } + } + } + } +} diff --git a/Services/WallpaperService.qml b/Services/WallpaperService.qml index 3a0756e5..b09a7839 100644 --- a/Services/WallpaperService.qml +++ b/Services/WallpaperService.qml @@ -9,6 +9,20 @@ import qs.Commons Singleton { id: root + // Public init to rehydrate cache after Settings load + function init() { + // Rebuild cache from persisted settings + var monitors = Settings.data.wallpaper.monitors || [] + currentWallpapers = ({}) + for (var i = 0; i < monitors.length; i++) { + if (monitors[i].name && monitors[i].wallpaper) { + currentWallpapers[monitors[i].name] = monitors[i].wallpaper + // Notify listeners so Background updates immediately after settings load + root.wallpaperChanged(monitors[i].name, monitors[i].wallpaper) + } + } + } + Component.onCompleted: { Logger.log("Wallpaper", "Service started") diff --git a/shell.qml b/shell.qml index 24544f5f..e8e00f78 100644 --- a/shell.qml +++ b/shell.qml @@ -28,6 +28,7 @@ import qs.Modules.PowerPanel import qs.Modules.SidePanel import qs.Modules.Toast import qs.Modules.WiFiPanel +import qs.Modules.WallpaperSelectorPanel import qs.Services import qs.Widgets @@ -94,6 +95,11 @@ ShellRoot { objectName: "bluetoothPanel" } + WallpaperSelectorPanel { + id: wallpaperSelectorPanel + objectName: "wallpaperSelectorPanel" + } + Component.onCompleted: { // Save a ref. to our lockScreen so we can access it easily PanelService.lockScreen = lockScreen From 6b61599633bb1819f3f4f48bbdbc5f17701b4ddb Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Thu, 18 Sep 2025 18:06:18 +0200 Subject: [PATCH 02/14] WallpaperSelector: change sizing --- Modules/WallpaperSelectorPanel/WallpaperSelectorPanel.qml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Modules/WallpaperSelectorPanel/WallpaperSelectorPanel.qml b/Modules/WallpaperSelectorPanel/WallpaperSelectorPanel.qml index 33a16b27..23616be8 100644 --- a/Modules/WallpaperSelectorPanel/WallpaperSelectorPanel.qml +++ b/Modules/WallpaperSelectorPanel/WallpaperSelectorPanel.qml @@ -13,7 +13,7 @@ NPanel { preferredWidth: 640 preferredHeight: 480 preferredWidthRatio: 0.4 - preferredHeightRatio: 0.5 + preferredHeightRatio: 0.41 panelAnchorHorizontalCenter: true panelAnchorVerticalCenter: true panelKeyboardFocus: true @@ -268,12 +268,6 @@ NPanel { } } } - - NDivider { - Layout.fillWidth: true - Layout.topMargin: Style.marginXL * scaling - Layout.bottomMargin: Style.marginXL * scaling - } } } } From 07a6a16011140afb4fb3a19a90ef29ecc2932bc9 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Thu, 18 Sep 2025 18:11:37 +0200 Subject: [PATCH 03/14] WallpaperSelector: cleanup --- Modules/WallpaperSelectorPanel/WallpaperSelectorPanel.qml | 2 -- 1 file changed, 2 deletions(-) diff --git a/Modules/WallpaperSelectorPanel/WallpaperSelectorPanel.qml b/Modules/WallpaperSelectorPanel/WallpaperSelectorPanel.qml index 23616be8..9c2a07c5 100644 --- a/Modules/WallpaperSelectorPanel/WallpaperSelectorPanel.qml +++ b/Modules/WallpaperSelectorPanel/WallpaperSelectorPanel.qml @@ -97,7 +97,6 @@ NPanel { Layout.bottomMargin: Style.marginXL * scaling } - // Scroll container mirrors SettingsPanel to avoid overflow and keep interactions smooth Flickable { Layout.fillWidth: true Layout.fillHeight: true @@ -115,7 +114,6 @@ NPanel { width: scrollView.availableWidth spacing: Style.marginM * scaling - // Selector header removed (title and refresh are redundant here) NToggle { label: "Apply to all monitors" description: "Apply selected wallpaper to all monitors at once." From 6fba3457f75fdadae28b0599876ddf2d2702de99 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Thu, 18 Sep 2025 18:27:35 +0200 Subject: [PATCH 04/14] NPanel: add drag support --- Modules/SettingsPanel/SettingsPanel.qml | 2 + .../WallpaperSelectorPanel.qml | 2 + Widgets/NPanel.qml | 55 ++++++++++++++++++- 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/Modules/SettingsPanel/SettingsPanel.qml b/Modules/SettingsPanel/SettingsPanel.qml index 55bef4a9..678a8556 100644 --- a/Modules/SettingsPanel/SettingsPanel.qml +++ b/Modules/SettingsPanel/SettingsPanel.qml @@ -21,6 +21,8 @@ NPanel { panelKeyboardFocus: true + draggable: true + // Tabs enumeration, order is NOT relevant enum Tab { About, diff --git a/Modules/WallpaperSelectorPanel/WallpaperSelectorPanel.qml b/Modules/WallpaperSelectorPanel/WallpaperSelectorPanel.qml index 9c2a07c5..1236aac1 100644 --- a/Modules/WallpaperSelectorPanel/WallpaperSelectorPanel.qml +++ b/Modules/WallpaperSelectorPanel/WallpaperSelectorPanel.qml @@ -18,6 +18,8 @@ NPanel { panelAnchorVerticalCenter: true panelKeyboardFocus: true + draggable: true + // Local reactive state property list wallpapersList: [] property string currentWallpaper: "" diff --git a/Widgets/NPanel.qml b/Widgets/NPanel.qml index 5b4b898a..138cd764 100644 --- a/Widgets/NPanel.qml +++ b/Widgets/NPanel.qml @@ -16,6 +16,7 @@ Loader { property real preferredWidthRatio property real preferredHeightRatio property color panelBackgroundColor: Color.mSurface + property bool draggable: false property bool panelAnchorHorizontalCenter: false property bool panelAnchorVerticalCenter: false @@ -215,6 +216,11 @@ Loader { radius: Style.radiusL * scaling border.color: Color.mOutline border.width: Math.max(1, Style.borderS * scaling) + // Dragging support + property bool draggable: root.draggable + property bool isDragged: false + property real manualX: 0 + property real manualY: 0 width: { var w if (preferredWidthRatio !== undefined) { @@ -239,8 +245,8 @@ Loader { scale: root.scaleValue opacity: root.opacityValue - x: calculatedX - y: calculatedY + x: isDragged ? manualX : calculatedX + y: isDragged ? manualY : calculatedY // --------------------------------------------- // Does not account for corners are they are negligible and helps keep the code clean. @@ -373,6 +379,14 @@ Loader { root.opacityValue = 1.0 } + // Reset drag position when panel closes + Connections { + target: root + function onClosed() { + panelBackground.isDragged = false + } + } + // Prevent closing when clicking in the panel bg MouseArea { anchors.fill: parent @@ -398,6 +412,43 @@ Loader { anchors.fill: parent sourceComponent: root.panelContent } + + // Handle drag move on the whole panel area + DragHandler { + id: dragHandler + target: null + enabled: panelBackground.draggable + property real dragStartX: 0 + property real dragStartY: 0 + onActiveChanged: { + if (active) { + // Capture current position into manual coordinates BEFORE toggling isDragged + panelBackground.manualX = panelBackground.x + panelBackground.manualY = panelBackground.y + dragStartX = panelBackground.x + dragStartY = panelBackground.y + panelBackground.isDragged = true + if (root.enableBackgroundClick) root.disableBackgroundClick() + } else { + // Keep isDragged true so we continue using the manual x/y after release + if (root.enableBackgroundClick) root.enableBackgroundClick() + } + } + onTranslationChanged: { + // Proposed new coordinates from fixed drag origin + var nx = dragStartX + translation.x + var ny = dragStartY + translation.y + + // Clamp within screen bounds accounting for margins + var maxX = panelWindow.width - panelBackground.width - panelBackground.marginRight + var minX = panelBackground.marginLeft + var maxY = panelWindow.height - panelBackground.height - panelBackground.marginBottom + var minY = panelBackground.marginTop + + panelBackground.manualX = Math.round(Math.max(minX, Math.min(nx, maxX))) + panelBackground.manualY = Math.round(Math.max(minY, Math.min(ny, maxY))) + } + } } } } From b8bce3d421e2a67c232dd3d949b8b2273f7c2f8f Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Thu, 18 Sep 2025 18:34:48 +0200 Subject: [PATCH 05/14] NPanel: add border while dragging --- Widgets/NPanel.qml | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/Widgets/NPanel.qml b/Widgets/NPanel.qml index 138cd764..f9c600bd 100644 --- a/Widgets/NPanel.qml +++ b/Widgets/NPanel.qml @@ -428,10 +428,12 @@ Loader { dragStartX = panelBackground.x dragStartY = panelBackground.y panelBackground.isDragged = true - if (root.enableBackgroundClick) root.disableBackgroundClick() + if (root.enableBackgroundClick) + root.disableBackgroundClick() } else { // Keep isDragged true so we continue using the manual x/y after release - if (root.enableBackgroundClick) root.enableBackgroundClick() + if (root.enableBackgroundClick) + root.enableBackgroundClick() } } onTranslationChanged: { @@ -449,6 +451,30 @@ Loader { panelBackground.manualY = Math.round(Math.max(minY, Math.min(ny, maxY))) } } + + // Drag indicator border + Rectangle { + anchors.fill: parent + anchors.margins: 0 + color: Color.transparent + border.color: Color.mPrimary + border.width: Math.max(2, Style.borderL * scaling) + radius: parent.radius + visible: panelBackground.isDragged && dragHandler.active + opacity: 0.8 + z: 3000 + + // Subtle glow effect + Rectangle { + anchors.fill: parent + anchors.margins: 0 + color: Color.transparent + border.color: Color.mPrimary + border.width: Math.max(1, Style.borderS * scaling) + radius: parent.radius + opacity: 0.3 + } + } } } } From 28026a4c37a2e601d758d34b6afb146fa9a80908 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Thu, 18 Sep 2025 19:24:00 +0200 Subject: [PATCH 06/14] NPanel: add bar detection while dragging WallpaperSelectorPanel: adjust layout --- .../WallpaperSelectorPanel.qml | 74 ++++++++++++++++--- Widgets/NPanel.qml | 20 +++-- 2 files changed, 79 insertions(+), 15 deletions(-) diff --git a/Modules/WallpaperSelectorPanel/WallpaperSelectorPanel.qml b/Modules/WallpaperSelectorPanel/WallpaperSelectorPanel.qml index 1236aac1..f18d3526 100644 --- a/Modules/WallpaperSelectorPanel/WallpaperSelectorPanel.qml +++ b/Modules/WallpaperSelectorPanel/WallpaperSelectorPanel.qml @@ -6,6 +6,7 @@ import Quickshell.Wayland import qs.Commons import qs.Services import qs.Widgets +import "../../Helpers/FuzzySort.js" as FuzzySort NPanel { id: root @@ -13,7 +14,7 @@ NPanel { preferredWidth: 640 preferredHeight: 480 preferredWidthRatio: 0.4 - preferredHeightRatio: 0.41 + preferredHeightRatio: 0.48 panelAnchorHorizontalCenter: true panelAnchorVerticalCenter: true panelKeyboardFocus: true @@ -23,11 +24,14 @@ NPanel { // Local reactive state property list wallpapersList: [] property string currentWallpaper: "" + property string filterText: "" + property list filteredWallpapers: [] function refreshForScreen() { const name = Screen.name wallpapersList = WallpaperService.getWallpapersList(name) currentWallpaper = WallpaperService.getWallpaper(name) + updateFiltered() } onOpened: refreshForScreen() @@ -51,6 +55,28 @@ NPanel { } } + function updateFiltered() { + if (!filterText || filterText.trim().length === 0) { + filteredWallpapers = wallpapersList + return + } + // Build objects with basename for ranking + const items = wallpapersList.map(function (p) { + return { + "path": p, + "name": p.split('/').pop() + } + }) + const results = FuzzySort.go(filterText.trim(), items, { + "key": 'name', + "limit": 200 + }) + // Map back to path list + filteredWallpapers = results.map(function (r) { + return r.obj.path + }) + } + panelContent: Rectangle { color: Color.transparent @@ -95,10 +121,9 @@ NPanel { NDivider { Layout.fillWidth: true - Layout.topMargin: Style.marginXL * scaling - Layout.bottomMargin: Style.marginXL * scaling } + // Scroll container mirrors SettingsPanel to avoid overflow and keep interactions smooth Flickable { Layout.fillWidth: true Layout.fillHeight: true @@ -124,20 +149,43 @@ NPanel { visible: (wallpapersList.length > 0) } + // Filter input + RowLayout { + Layout.fillWidth: true + spacing: Style.marginM * scaling + + NText { + text: "Search:" + color: Color.mOnSurface + font.pointSize: Style.fontSizeM * scaling + Layout.preferredWidth: implicitWidth + } + + NTextInput { + placeholderText: "Type to filter wallpapers..." + text: filterText + onTextChanged: { + filterText = text + updateFiltered() + } + Layout.fillWidth: true + } + } + // Grid container Item { visible: !WallpaperService.scanning Layout.fillWidth: true - Layout.preferredHeight: Math.ceil(wallpapersList.length / wallpaperGridView.columns) * wallpaperGridView.cellHeight + Layout.preferredHeight: Math.ceil(filteredWallpapers.length / wallpaperGridView.columns) * wallpaperGridView.cellHeight GridView { id: wallpaperGridView anchors.fill: parent - model: wallpapersList + model: filteredWallpapers interactive: false clip: true - property int columns: 5 + property int columns: 4 property int itemSize: Math.floor((width - leftMargin - rightMargin - (columns * Style.marginS * scaling)) / columns) cellWidth: Math.floor((width - leftMargin - rightMargin) / columns) @@ -227,7 +275,7 @@ NPanel { radius: Style.radiusM * scaling border.color: Color.mOutline border.width: Math.max(1, Style.borderS * scaling) - visible: wallpapersList.length === 0 || WallpaperService.scanning + visible: (filteredWallpapers.length === 0 && !WallpaperService.scanning) || WallpaperService.scanning Layout.fillWidth: true Layout.preferredHeight: 130 * scaling @@ -241,7 +289,7 @@ NPanel { ColumnLayout { anchors.fill: parent - visible: wallpapersList.length === 0 && !WallpaperService.scanning + visible: filteredWallpapers.length === 0 && !WallpaperService.scanning Item { Layout.fillHeight: true } @@ -252,13 +300,13 @@ NPanel { Layout.alignment: Qt.AlignHCenter } NText { - text: "No wallpaper found." + text: (filterText && filterText.length > 0) ? "No match for filter." : "No wallpaper found." color: Color.mOnSurface font.weight: Style.fontWeightBold Layout.alignment: Qt.AlignHCenter } NText { - text: "Configure your wallpaper directory with images." + text: (filterText && filterText.length > 0) ? "Try a different query." : "Configure your wallpaper directory with images." color: Color.mOnSurfaceVariant wrapMode: Text.WordWrap Layout.alignment: Qt.AlignHCenter @@ -268,6 +316,12 @@ NPanel { } } } + + NDivider { + Layout.fillWidth: true + Layout.topMargin: Style.marginXL * scaling + Layout.bottomMargin: Style.marginXL * scaling + } } } } diff --git a/Widgets/NPanel.qml b/Widgets/NPanel.qml index f9c600bd..158e86b4 100644 --- a/Widgets/NPanel.qml +++ b/Widgets/NPanel.qml @@ -441,11 +441,21 @@ Loader { var nx = dragStartX + translation.x var ny = dragStartY + translation.y - // Clamp within screen bounds accounting for margins - var maxX = panelWindow.width - panelBackground.width - panelBackground.marginRight - var minX = panelBackground.marginLeft - var maxY = panelWindow.height - panelBackground.height - panelBackground.marginBottom - var minY = panelBackground.marginTop + // Calculate gaps so we never overlap the bar on any side + var baseGap = Style.marginS * scaling + var floatExtraH = Settings.data.bar.floating ? Settings.data.bar.marginHorizontal * 2 * Style.marginXL * scaling : 0 + var floatExtraV = Settings.data.bar.floating ? Settings.data.bar.marginVertical * 2 * Style.marginXL * scaling : 0 + + var insetLeft = baseGap + ((barIsVisible && barPosition === "left") ? (Style.barHeight * scaling + floatExtraH) : 0) + var insetRight = baseGap + ((barIsVisible && barPosition === "right") ? (Style.barHeight * scaling + floatExtraH) : 0) + var insetTop = baseGap + ((barIsVisible && barPosition === "top") ? (Style.barHeight * scaling + floatExtraV) : 0) + var insetBottom = baseGap + ((barIsVisible && barPosition === "bottom") ? (Style.barHeight * scaling + floatExtraV) : 0) + + // Clamp within screen bounds accounting for insets + var maxX = panelWindow.width - panelBackground.width - insetRight + var minX = insetLeft + var maxY = panelWindow.height - panelBackground.height - insetBottom + var minY = insetTop panelBackground.manualX = Math.round(Math.max(minX, Math.min(nx, maxX))) panelBackground.manualY = Math.round(Math.max(minY, Math.min(ny, maxY))) From b374f167ef81ab5cf0591e5278e9d031356768b9 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Thu, 18 Sep 2025 19:26:35 +0200 Subject: [PATCH 07/14] WallpaperSelectorPanel: rename to WallpaperSelector --- .../Tabs/WallpaperSelectorTab.qml | 268 ------------------ Modules/SidePanel/Cards/UtilitiesCard.qml | 2 +- .../WallpaperSelector.qml} | 0 shell.qml | 8 +- 4 files changed, 5 insertions(+), 273 deletions(-) delete mode 100644 Modules/SettingsPanel/Tabs/WallpaperSelectorTab.qml rename Modules/{WallpaperSelectorPanel/WallpaperSelectorPanel.qml => WallpaperSelector/WallpaperSelector.qml} (100%) diff --git a/Modules/SettingsPanel/Tabs/WallpaperSelectorTab.qml b/Modules/SettingsPanel/Tabs/WallpaperSelectorTab.qml deleted file mode 100644 index a6d340ac..00000000 --- a/Modules/SettingsPanel/Tabs/WallpaperSelectorTab.qml +++ /dev/null @@ -1,268 +0,0 @@ -import QtQuick -import QtQuick.Layouts -import QtQuick.Controls -import Qt.labs.folderlistmodel -import qs.Commons -import qs.Services -import qs.Widgets - -ColumnLayout { - id: root - width: parent.width - spacing: Style.marginL * scaling - - property list wallpapersList: [] - property string currentWallpaper: "" - - Component.onCompleted: { - wallpapersList = screen ? WallpaperService.getWallpapersList(screen.name) : [] - currentWallpaper = screen ? WallpaperService.getWallpaper(screen.name) : "" - } - - Connections { - target: WallpaperService - function onWallpaperChanged(screenName, path) { - if (screenName === screen.name) { - currentWallpaper = WallpaperService.getWallpaper(screen.name) - } - } - function onWallpaperDirectoryChanged(screenName, directory) { - if (screenName === screen.name) { - wallpapersList = WallpaperService.getWallpapersList(screen.name) - currentWallpaper = WallpaperService.getWallpaper(screen.name) - } - } - function onWallpaperListChanged(screenName, count) { - if (screenName === screen.name) { - wallpapersList = WallpaperService.getWallpapersList(screen.name) - currentWallpaper = WallpaperService.getWallpaper(screen.name) - } - } - } - - // Current wallpaper display - NHeader { - label: "Current Wallpaper" - description: "Preview and manage your desktop background." - } - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 140 * scaling - radius: Style.radiusM * scaling - color: Color.transparent - - NImageRounded { - anchors.fill: parent - anchors.margins: Style.marginXS * scaling - imagePath: currentWallpaper - fallbackIcon: "image" - imageRadius: Style.radiusM * scaling - borderColor: Color.mSecondary - borderWidth: Style.borderL * 2 * scaling - } - } - - NDivider { - Layout.fillWidth: true - Layout.topMargin: Style.marginXL * scaling - Layout.bottomMargin: Style.marginXL * scaling - } - - // Wallpaper selector - RowLayout { - Layout.fillWidth: true - - ColumnLayout { - Layout.fillWidth: true - - // Wallpaper grid - NHeader { - label: "Wallpaper Selector" - description: "Click on a wallpaper to set it as your current wallpaper." - } - } - - NIconButton { - icon: "refresh" - tooltipText: "Refresh wallpaper list" - onClicked: { - WallpaperService.refreshWallpapersList() - } - Layout.alignment: Qt.AlignVCenter | Qt.AlignRight - } - } - - NToggle { - label: "Apply to all monitors" - description: "Apply selected wallpaper to all monitors at once." - checked: Settings.data.wallpaper.setWallpaperOnAllMonitors - onToggled: checked => Settings.data.wallpaper.setWallpaperOnAllMonitors = checked - visible: (wallpapersList.length > 0) - } - - // Wallpaper grid container - Item { - visible: !WallpaperService.scanning - Layout.fillWidth: true - Layout.preferredHeight: { - return Math.ceil(wallpapersList.length / wallpaperGridView.columns) * wallpaperGridView.cellHeight - } - - GridView { - id: wallpaperGridView - anchors.fill: parent - model: wallpapersList - - interactive: false - clip: true - - property int columns: 4 - property int itemSize: Math.floor((width - leftMargin - rightMargin - (4 * Style.marginS * scaling)) / columns) - - cellWidth: Math.floor((width - leftMargin - rightMargin) / columns) - cellHeight: Math.floor(itemSize * 0.67) + Style.marginS * scaling - - leftMargin: Style.marginS * scaling - rightMargin: Style.marginS * scaling - topMargin: Style.marginS * scaling - bottomMargin: Style.marginS * scaling - - delegate: Rectangle { - id: wallpaperItem - - property string wallpaperPath: modelData - property bool isSelected: screen ? (wallpaperPath === currentWallpaper) : false - - width: wallpaperGridView.itemSize - height: Math.round(wallpaperGridView.itemSize * 0.67) - color: Color.transparent - - // NImageCached relies on the image being visible to work properly. - // MultiEffect relies on the image being invisible to apply effects. - // That's why we don't have rounded corners here, as we don't want to bring back qt5compat. - NImageCached { - id: img - imagePath: wallpaperPath - anchors.fill: parent - } - - // Borders on top - Rectangle { - anchors.fill: parent - color: Color.transparent - border.color: isSelected ? Color.mSecondary : Color.mSurface - border.width: Math.max(1, Style.borderL * 1.5 * scaling) - } - - // Selection tick-mark - Rectangle { - anchors.top: parent.top - anchors.right: parent.right - anchors.margins: Style.marginS * scaling - width: 28 * scaling - height: 28 * scaling - radius: width / 2 - color: Color.mSecondary - border.color: Color.mOutline - border.width: Math.max(1, Style.borderS * scaling) - visible: isSelected - - NIcon { - icon: "check" - font.pointSize: Style.fontSizeM * scaling - font.weight: Style.fontWeightBold - color: Color.mOnSecondary - anchors.centerIn: parent - } - } - - // Hover effect - Rectangle { - anchors.fill: parent - color: Color.mSurface - opacity: (mouseArea.containsMouse || isSelected) ? 0 : 0.3 - radius: parent.radius - - Behavior on opacity { - NumberAnimation { - duration: Style.animationFast - } - } - } - - MouseArea { - id: mouseArea - anchors.fill: parent - acceptedButtons: Qt.LeftButton - hoverEnabled: true - onPressed: { - if (Settings.data.wallpaper.setWallpaperOnAllMonitors) { - WallpaperService.changeWallpaper(wallpaperPath, undefined) - } else if (screen) { - WallpaperService.changeWallpaper(wallpaperPath, screen.name) - } - } - } - } - } - } - - // Empty state - Rectangle { - color: Color.mSurface - radius: Style.radiusM * scaling - border.color: Color.mOutline - border.width: Math.max(1, Style.borderS * scaling) - visible: wallpapersList.length === 0 || WallpaperService.scanning - Layout.fillWidth: true - Layout.preferredHeight: 130 * scaling - - ColumnLayout { - anchors.fill: parent - visible: WallpaperService.scanning - NBusyIndicator { - Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter - } - } - - ColumnLayout { - anchors.fill: parent - visible: wallpapersList.length === 0 && !WallpaperService.scanning - Item { - Layout.fillHeight: true - } - - NIcon { - icon: "folder-open" - font.pointSize: Style.fontSizeXXL * scaling - color: Color.mOnSurface - Layout.alignment: Qt.AlignHCenter - } - - NText { - text: "No wallpaper found." - color: Color.mOnSurface - font.weight: Style.fontWeightBold - Layout.alignment: Qt.AlignHCenter - } - - NText { - text: "Make sure your wallpaper directory is configured and contains image files." - color: Color.mOnSurfaceVariant - wrapMode: Text.WordWrap - Layout.alignment: Qt.AlignHCenter - } - - Item { - Layout.fillHeight: true - } - } - } - - NDivider { - Layout.fillWidth: true - Layout.topMargin: Style.marginXL * scaling - Layout.bottomMargin: Style.marginXL * scaling - } -} diff --git a/Modules/SidePanel/Cards/UtilitiesCard.qml b/Modules/SidePanel/Cards/UtilitiesCard.qml index 9be59e3f..bed94308 100644 --- a/Modules/SidePanel/Cards/UtilitiesCard.qml +++ b/Modules/SidePanel/Cards/UtilitiesCard.qml @@ -55,7 +55,7 @@ NBox { visible: Settings.data.wallpaper.enabled icon: "wallpaper-selector" tooltipText: "Left click: Open wallpaper selector.\nRight click: Set random wallpaper." - onClicked: PanelService.getPanel("wallpaperSelectorPanel")?.toggle(this) + onClicked: PanelService.getPanel("wallpaperSelector")?.toggle(this) onRightClicked: WallpaperService.setRandomWallpaper() } diff --git a/Modules/WallpaperSelectorPanel/WallpaperSelectorPanel.qml b/Modules/WallpaperSelector/WallpaperSelector.qml similarity index 100% rename from Modules/WallpaperSelectorPanel/WallpaperSelectorPanel.qml rename to Modules/WallpaperSelector/WallpaperSelector.qml diff --git a/shell.qml b/shell.qml index e8e00f78..22f4f521 100644 --- a/shell.qml +++ b/shell.qml @@ -28,7 +28,7 @@ import qs.Modules.PowerPanel import qs.Modules.SidePanel import qs.Modules.Toast import qs.Modules.WiFiPanel -import qs.Modules.WallpaperSelectorPanel +import qs.Modules.WallpaperSelector import qs.Services import qs.Widgets @@ -95,9 +95,9 @@ ShellRoot { objectName: "bluetoothPanel" } - WallpaperSelectorPanel { - id: wallpaperSelectorPanel - objectName: "wallpaperSelectorPanel" + WallpaperSelector { + id: wallpaperSelector + objectName: "wallpaperSelector" } Component.onCompleted: { From 89eb5ecde6adcabc4a68107c8457736606fbca61 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Thu, 18 Sep 2025 19:31:04 +0200 Subject: [PATCH 08/14] IPCManager: add wallpaper selector toggle --- Modules/IPC/IPCManager.qml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Modules/IPC/IPCManager.qml b/Modules/IPC/IPCManager.qml index be88ff0c..dbf0590a 100644 --- a/Modules/IPC/IPCManager.qml +++ b/Modules/IPC/IPCManager.qml @@ -127,6 +127,12 @@ Item { // Wallpaper IPC: trigger a new random wallpaper IpcHandler { target: "wallpaper" + function toggle() { + if (Settings.data.wallpaper.enabled) { + wallpaperSelector.toggle() + } + } + function random() { if (Settings.data.wallpaper.enabled) { WallpaperService.setRandomWallpaper() From b65d82d89531db5b7bf8d1f2e4546d724e1c863f Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Thu, 18 Sep 2025 19:51:45 +0200 Subject: [PATCH 09/14] WallpaperSelector: more layout changes --- Modules/IPC/IPCManager.qml | 2 +- .../WallpaperSelector/WallpaperSelector.qml | 130 ++++++++++-------- 2 files changed, 72 insertions(+), 60 deletions(-) diff --git a/Modules/IPC/IPCManager.qml b/Modules/IPC/IPCManager.qml index dbf0590a..ba644c75 100644 --- a/Modules/IPC/IPCManager.qml +++ b/Modules/IPC/IPCManager.qml @@ -132,7 +132,7 @@ Item { wallpaperSelector.toggle() } } - + function random() { if (Settings.data.wallpaper.enabled) { WallpaperService.setRandomWallpaper() diff --git a/Modules/WallpaperSelector/WallpaperSelector.qml b/Modules/WallpaperSelector/WallpaperSelector.qml index f18d3526..5aadf1d7 100644 --- a/Modules/WallpaperSelector/WallpaperSelector.qml +++ b/Modules/WallpaperSelector/WallpaperSelector.qml @@ -189,81 +189,99 @@ NPanel { property int itemSize: Math.floor((width - leftMargin - rightMargin - (columns * Style.marginS * scaling)) / columns) cellWidth: Math.floor((width - leftMargin - rightMargin) / columns) - cellHeight: Math.floor(itemSize * 0.67) + Style.marginS * scaling + cellHeight: Math.floor(itemSize * 0.67) + Style.marginXS * scaling + Style.fontSizeXS * scaling + Style.marginS * scaling leftMargin: Style.marginS * scaling rightMargin: Style.marginS * scaling topMargin: Style.marginS * scaling bottomMargin: Style.marginS * scaling - delegate: Rectangle { + delegate: ColumnLayout { id: wallpaperItem property string wallpaperPath: modelData property bool isSelected: (wallpaperPath === currentWallpaper) + property string filename: wallpaperPath.split('/').pop() width: wallpaperGridView.itemSize - height: Math.round(wallpaperGridView.itemSize * 0.67) - color: Color.transparent - - NImageCached { - id: img - imagePath: wallpaperPath - anchors.fill: parent - } + spacing: Style.marginXS * scaling Rectangle { - anchors.fill: parent + id: imageContainer + Layout.fillWidth: true + Layout.preferredHeight: Math.round(wallpaperGridView.itemSize * 0.67) color: Color.transparent - border.color: isSelected ? Color.mSecondary : Color.mSurface - border.width: Math.max(1, Style.borderL * 1.5 * scaling) - } - Rectangle { - anchors.top: parent.top - anchors.right: parent.right - anchors.margins: Style.marginS * scaling - width: 28 * scaling - height: 28 * scaling - radius: width / 2 - color: Color.mSecondary - border.color: Color.mOutline - border.width: Math.max(1, Style.borderS * scaling) - visible: isSelected - - NIcon { - icon: "check" - font.pointSize: Style.fontSizeM * scaling - font.weight: Style.fontWeightBold - color: Color.mOnSecondary - anchors.centerIn: parent + NImageCached { + id: img + imagePath: wallpaperPath + anchors.fill: parent } - } - Rectangle { - anchors.fill: parent - color: Color.mSurface - opacity: (mouseArea.containsMouse || isSelected) ? 0 : 0.3 - radius: parent.radius - Behavior on opacity { - NumberAnimation { - duration: Style.animationFast + Rectangle { + anchors.fill: parent + color: Color.transparent + border.color: isSelected ? Color.mSecondary : Color.mSurface + border.width: Math.max(1, Style.borderL * 1.5 * scaling) + } + + Rectangle { + anchors.top: parent.top + anchors.right: parent.right + anchors.margins: Style.marginS * scaling + width: 28 * scaling + height: 28 * scaling + radius: width / 2 + color: Color.mSecondary + border.color: Color.mOutline + border.width: Math.max(1, Style.borderS * scaling) + visible: isSelected + + NIcon { + icon: "check" + font.pointSize: Style.fontSizeM * scaling + font.weight: Style.fontWeightBold + color: Color.mOnSecondary + anchors.centerIn: parent + } + } + + Rectangle { + anchors.fill: parent + color: Color.mSurface + opacity: (mouseArea.containsMouse || isSelected) ? 0 : 0.3 + radius: parent.radius + Behavior on opacity { + NumberAnimation { + duration: Style.animationFast + } + } + } + + MouseArea { + id: mouseArea + anchors.fill: parent + acceptedButtons: Qt.LeftButton + hoverEnabled: true + onPressed: { + if (Settings.data.wallpaper.setWallpaperOnAllMonitors) { + WallpaperService.changeWallpaper(wallpaperPath, undefined) + } else { + WallpaperService.changeWallpaper(wallpaperPath, Screen.name) + } } } } - MouseArea { - id: mouseArea - anchors.fill: parent - acceptedButtons: Qt.LeftButton - hoverEnabled: true - onPressed: { - if (Settings.data.wallpaper.setWallpaperOnAllMonitors) { - WallpaperService.changeWallpaper(wallpaperPath, undefined) - } else { - WallpaperService.changeWallpaper(wallpaperPath, Screen.name) - } - } + NText { + text: filename + color: Color.mOnSurfaceVariant + opacity: 0.5 + font.pointSize: Style.fontSizeXS * scaling + Layout.fillWidth: true + Layout.alignment: Qt.AlignHCenter + horizontalAlignment: Text.AlignHCenter + elide: Text.ElideRight } } } @@ -316,12 +334,6 @@ NPanel { } } } - - NDivider { - Layout.fillWidth: true - Layout.topMargin: Style.marginXL * scaling - Layout.bottomMargin: Style.marginXL * scaling - } } } } From c7ab350cbd60bbdcbf5f70b92a8353f998956189 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Thu, 18 Sep 2025 19:53:06 +0200 Subject: [PATCH 10/14] MatugenService: add check for Settings.isLoaded --- Services/MatugenService.qml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Services/MatugenService.qml b/Services/MatugenService.qml index 3d090c18..c2ebcd53 100644 --- a/Services/MatugenService.qml +++ b/Services/MatugenService.qml @@ -47,6 +47,11 @@ Singleton { // Generate colors using current wallpaper and settings function generateFromWallpaper() { + if (!Settings.isLoaded) { + Logger.log("Matugen", "Settings not loaded yet, skipping wallpaper color generation") + return + } + Logger.log("Matugen", "Generating from wallpaper on screen:", Screen.name) var wp = WallpaperService.getWallpaper(Screen.name).replace(/'/g, "'\\''") if (wp === "") { From c3019230aef601f770137e5ef7127d783d22fbbb Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Thu, 18 Sep 2025 20:04:03 +0200 Subject: [PATCH 11/14] WallpaperSelector: even more layout changes --- .../WallpaperSelector/WallpaperSelector.qml | 71 ++++++++++--------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/Modules/WallpaperSelector/WallpaperSelector.qml b/Modules/WallpaperSelector/WallpaperSelector.qml index 5aadf1d7..02269ca7 100644 --- a/Modules/WallpaperSelector/WallpaperSelector.qml +++ b/Modules/WallpaperSelector/WallpaperSelector.qml @@ -14,7 +14,7 @@ NPanel { preferredWidth: 640 preferredHeight: 480 preferredWidthRatio: 0.4 - preferredHeightRatio: 0.48 + preferredHeightRatio: 0.52 panelAnchorHorizontalCenter: true panelAnchorVerticalCenter: true panelKeyboardFocus: true @@ -123,7 +123,39 @@ NPanel { Layout.fillWidth: true } - // Scroll container mirrors SettingsPanel to avoid overflow and keep interactions smooth + NToggle { + label: "Apply to all monitors" + description: "Apply selected wallpaper to all monitors at once." + checked: Settings.data.wallpaper.setWallpaperOnAllMonitors + onToggled: checked => Settings.data.wallpaper.setWallpaperOnAllMonitors = checked + visible: (wallpapersList.length > 0) + Layout.fillWidth: true + } + + // Filter input + RowLayout { + Layout.fillWidth: true + spacing: Style.marginM * scaling + + NText { + text: "Search:" + color: Color.mOnSurface + font.pointSize: Style.fontSizeM * scaling + Layout.preferredWidth: implicitWidth + } + + NTextInput { + placeholderText: "Type to filter wallpapers..." + text: filterText + onTextChanged: { + filterText = text + updateFiltered() + } + Layout.fillWidth: true + } + } + + // Scroll container for wallpaper grid only Flickable { Layout.fillWidth: true Layout.fillHeight: true @@ -141,37 +173,6 @@ NPanel { width: scrollView.availableWidth spacing: Style.marginM * scaling - NToggle { - label: "Apply to all monitors" - description: "Apply selected wallpaper to all monitors at once." - checked: Settings.data.wallpaper.setWallpaperOnAllMonitors - onToggled: checked => Settings.data.wallpaper.setWallpaperOnAllMonitors = checked - visible: (wallpapersList.length > 0) - } - - // Filter input - RowLayout { - Layout.fillWidth: true - spacing: Style.marginM * scaling - - NText { - text: "Search:" - color: Color.mOnSurface - font.pointSize: Style.fontSizeM * scaling - Layout.preferredWidth: implicitWidth - } - - NTextInput { - placeholderText: "Type to filter wallpapers..." - text: filterText - onTextChanged: { - filterText = text - updateFiltered() - } - Layout.fillWidth: true - } - } - // Grid container Item { visible: !WallpaperService.scanning @@ -189,7 +190,7 @@ NPanel { property int itemSize: Math.floor((width - leftMargin - rightMargin - (columns * Style.marginS * scaling)) / columns) cellWidth: Math.floor((width - leftMargin - rightMargin) / columns) - cellHeight: Math.floor(itemSize * 0.67) + Style.marginXS * scaling + Style.fontSizeXS * scaling + Style.marginS * scaling + cellHeight: Math.floor(itemSize * 0.7) + Style.marginXS * scaling + Style.fontSizeXS * scaling + Style.marginM * scaling leftMargin: Style.marginS * scaling rightMargin: Style.marginS * scaling @@ -279,6 +280,8 @@ NPanel { opacity: 0.5 font.pointSize: Style.fontSizeXS * scaling Layout.fillWidth: true + Layout.leftMargin: Style.marginS * scaling + Layout.rightMargin: Style.marginS * scaling Layout.alignment: Qt.AlignHCenter horizontalAlignment: Text.AlignHCenter elide: Text.ElideRight From b2d46ab75977def39e0a0c03864e9bb99534fa58 Mon Sep 17 00:00:00 2001 From: LemmyCook Date: Thu, 18 Sep 2025 17:34:55 -0400 Subject: [PATCH 12/14] Settings: cleanup since we moved the wallpaper selector out. --- Modules/SettingsPanel/SettingsPanel.qml | 40 ++++++++++--------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/Modules/SettingsPanel/SettingsPanel.qml b/Modules/SettingsPanel/SettingsPanel.qml index 678a8556..d04aea82 100644 --- a/Modules/SettingsPanel/SettingsPanel.qml +++ b/Modules/SettingsPanel/SettingsPanel.qml @@ -46,13 +46,6 @@ NPanel { property var tabsModel: [] property var activeScrollView: null - Connections { - target: Settings.data.wallpaper - function onEnabledChanged() { - updateTabsModel() - } - } - Component.onCompleted: { updateTabsModel() } @@ -172,26 +165,23 @@ NPanel { "label": "Wallpaper", "icon": "settings-wallpaper", "source": wallpaperTab + }, { + "id": SettingsPanel.Tab.ScreenRecorder, + "label": "Screen Recorder", + "icon": "settings-screen-recorder", + "source": screenRecorderTab + }, { + "id": SettingsPanel.Tab.Hooks, + "label": "Hooks", + "icon": "settings-hooks", + "source": hooksTab + }, { + "id": SettingsPanel.Tab.About, + "label": "About", + "icon": "settings-about", + "source": aboutTab }] - // Wallpaper selector moved to its own panel - newTabs.push({ - "id": SettingsPanel.Tab.ScreenRecorder, - "label": "Screen Recorder", - "icon": "settings-screen-recorder", - "source": screenRecorderTab - }, { - "id": SettingsPanel.Tab.Hooks, - "label": "Hooks", - "icon": "settings-hooks", - "source": hooksTab - }, { - "id": SettingsPanel.Tab.About, - "label": "About", - "icon": "settings-about", - "source": aboutTab - }) - root.tabsModel = newTabs // Assign the generated list to the model } // When the panel opens, choose the appropriate tab From 4e67f265766629edc991e53ef408aa503c3796e2 Mon Sep 17 00:00:00 2001 From: LemmyCook Date: Thu, 18 Sep 2025 17:35:25 -0400 Subject: [PATCH 13/14] Wallpaper Selector: fix for multi screens / multi directories setup --- .../WallpaperSelector/WallpaperSelector.qml | 123 +++++++++--------- 1 file changed, 63 insertions(+), 60 deletions(-) diff --git a/Modules/WallpaperSelector/WallpaperSelector.qml b/Modules/WallpaperSelector/WallpaperSelector.qml index 02269ca7..8d231358 100644 --- a/Modules/WallpaperSelector/WallpaperSelector.qml +++ b/Modules/WallpaperSelector/WallpaperSelector.qml @@ -18,66 +18,69 @@ NPanel { panelAnchorHorizontalCenter: true panelAnchorVerticalCenter: true panelKeyboardFocus: true - draggable: true - // Local reactive state - property list wallpapersList: [] - property string currentWallpaper: "" - property string filterText: "" - property list filteredWallpapers: [] - - function refreshForScreen() { - const name = Screen.name - wallpapersList = WallpaperService.getWallpapersList(name) - currentWallpaper = WallpaperService.getWallpaper(name) - updateFiltered() - } - - onOpened: refreshForScreen() - - Connections { - target: WallpaperService - function onWallpaperChanged(screenName, path) { - if (screenName === Screen.name) { - currentWallpaper = WallpaperService.getWallpaper(Screen.name) - } - } - function onWallpaperDirectoryChanged(screenName, directory) { - if (screenName === Screen.name) { - refreshForScreen() - } - } - function onWallpaperListChanged(screenName, count) { - if (screenName === Screen.name) { - refreshForScreen() - } - } - } - - function updateFiltered() { - if (!filterText || filterText.trim().length === 0) { - filteredWallpapers = wallpapersList - return - } - // Build objects with basename for ranking - const items = wallpapersList.map(function (p) { - return { - "path": p, - "name": p.split('/').pop() - } - }) - const results = FuzzySort.go(filterText.trim(), items, { - "key": 'name', - "limit": 200 - }) - // Map back to path list - filteredWallpapers = results.map(function (r) { - return r.obj.path - }) - } - panelContent: Rectangle { + // Local reactive state + property list wallpapersList: [] + property string currentWallpaper: "" + property string filterText: "" + property list filteredWallpapers: [] + + Component.onCompleted: { + refreshWallpaperScreenData() + } + + Connections { + target: WallpaperService + function onWallpaperChanged(screenName, path) { + if (screen !== null && screenName === screen.name) { + currentWallpaper = WallpaperService.getWallpaper(screen.name) + } + } + function onWallpaperDirectoryChanged(screenName, directory) { + if (screen !== null && screenName === screen.name) { + refreshWallpaperScreenData() + } + } + function onWallpaperListChanged(screenName, count) { + if (screen !== null && screenName === screen.name) { + refreshWallpaperScreenData() + } + } + } + + function refreshWallpaperScreenData() { + if (screen === null) { + return + } + wallpapersList = WallpaperService.getWallpapersList(screen.name) + currentWallpaper = WallpaperService.getWallpaper(screen.name) + updateFiltered() + } + + function updateFiltered() { + if (!filterText || filterText.trim().length === 0) { + filteredWallpapers = wallpapersList + return + } + // Build objects with basename for ranking + const items = wallpapersList.map(function (p) { + return { + "path": p, + "name": p.split('/').pop() + } + }) + const results = FuzzySort.go(filterText.trim(), items, { + "key": 'name', + "limit": 200 + }) + // Map back to path list + filteredWallpapers = results.map(function (r) { + return r.obj.path + }) + } + color: Color.transparent ColumnLayout { @@ -106,7 +109,7 @@ NPanel { NIconButton { icon: "refresh" - tooltipText: "Refresh wallpaper list" + tooltipText: "Refresh wallpaper list." baseSize: Style.baseWidgetSize * 0.8 onClicked: WallpaperService.refreshWallpapersList() } @@ -321,13 +324,13 @@ NPanel { Layout.alignment: Qt.AlignHCenter } NText { - text: (filterText && filterText.length > 0) ? "No match for filter." : "No wallpaper found." + text: (filterText && filterText.length > 0) ? "No match found." : "No wallpaper found." color: Color.mOnSurface font.weight: Style.fontWeightBold Layout.alignment: Qt.AlignHCenter } NText { - text: (filterText && filterText.length > 0) ? "Try a different query." : "Configure your wallpaper directory with images." + text: (filterText && filterText.length > 0) ? "Try a different search query." : "Configure your wallpaper directory with images." color: Color.mOnSurfaceVariant wrapMode: Text.WordWrap Layout.alignment: Qt.AlignHCenter From 6fe498ce19d46fabf25a70ebbea17ceeba69d00c Mon Sep 17 00:00:00 2001 From: LemmyCook Date: Thu, 18 Sep 2025 17:47:26 -0400 Subject: [PATCH 14/14] Wallpaper Selector: auto-focus search field --- Modules/WallpaperSelector/WallpaperSelector.qml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Modules/WallpaperSelector/WallpaperSelector.qml b/Modules/WallpaperSelector/WallpaperSelector.qml index 8d231358..cd855352 100644 --- a/Modules/WallpaperSelector/WallpaperSelector.qml +++ b/Modules/WallpaperSelector/WallpaperSelector.qml @@ -148,6 +148,7 @@ NPanel { } NTextInput { + id: searchInput placeholderText: "Type to filter wallpapers..." text: filterText onTextChanged: { @@ -155,6 +156,11 @@ NPanel { updateFiltered() } Layout.fillWidth: true + Component.onCompleted: { + if (searchInput.inputItem && searchInput.inputItem.visible) { + searchInput.inputItem.forceActiveFocus() + } + } } } @@ -187,7 +193,6 @@ NPanel { anchors.fill: parent model: filteredWallpapers interactive: false - clip: true property int columns: 4 property int itemSize: Math.floor((width - leftMargin - rightMargin - (columns * Style.marginS * scaling)) / columns)