From 0bbb1c04b749fa75dcf5a764e6df1b625fcce3a2 Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Fri, 5 Jun 2026 21:39:05 +1000 Subject: [PATCH] feat: actually impl wall selector w/ categories --- modules/nexus/NexusState.qml | 2 + modules/nexus/PageCompRegistry.qml | 3 + modules/nexus/common/WallItem.qml | 105 +++++++++++++++ .../pages/wallandstyle/WallpaperCategory.qml | 38 ++++++ .../pages/wallandstyle/WallpaperSelect.qml | 123 +++++------------- services/Wallpapers.qml | 7 + 6 files changed, 189 insertions(+), 89 deletions(-) create mode 100644 modules/nexus/common/WallItem.qml create mode 100644 modules/nexus/pages/wallandstyle/WallpaperCategory.qml diff --git a/modules/nexus/NexusState.qml b/modules/nexus/NexusState.qml index 3e2afc72..ddcb3e55 100644 --- a/modules/nexus/NexusState.qml +++ b/modules/nexus/NexusState.qml @@ -8,6 +8,8 @@ QtObject { property list subPageIdxStack property bool searchOpen + property string selectedWallpaperCategory + signal close signal subPageOpened(idx: int) signal subPageClosed diff --git a/modules/nexus/PageCompRegistry.qml b/modules/nexus/PageCompRegistry.qml index 2e04a2bf..13d104fe 100644 --- a/modules/nexus/PageCompRegistry.qml +++ b/modules/nexus/PageCompRegistry.qml @@ -18,6 +18,9 @@ QtObject { Component { WallpaperSelect {} } + Component { + WallpaperCategory {} + } } } ] diff --git a/modules/nexus/common/WallItem.qml b/modules/nexus/common/WallItem.qml new file mode 100644 index 00000000..39dd22e3 --- /dev/null +++ b/modules/nexus/common/WallItem.qml @@ -0,0 +1,105 @@ +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Layouts +import Quickshell +import Caelestia.Config +import qs.components +import qs.components.controls +import qs.services + +Item { + id: root + + property alias source: img.source + property alias text: label.text + property alias radius: imgWrapper.radius + property alias imgHeight: imgWrapper.implicitHeight + property bool fillLabel: true + + signal clicked + + Layout.fillWidth: true + implicitHeight: layout.implicitHeight + + ColumnLayout { + id: layout + + anchors.fill: parent + spacing: Tokens.spacing.small + + StyledClippingRect { + id: imgWrapper + + Layout.fillWidth: true + implicitHeight: width + radius: Tokens.rounding.largeIncreased + color: Colours.tPalette.m3surfaceContainer + + Loader { + anchors.centerIn: parent + + opacity: img.status === Image.Ready ? 0 : 1 + active: opacity > 0 + + sourceComponent: StyledRect { + implicitWidth: loadingIndicator.implicitSize + Tokens.padding.large * 2 + implicitHeight: loadingIndicator.implicitSize + Tokens.padding.large * 2 + + color: Colours.palette.m3primaryContainer + radius: Tokens.rounding.full + + LoadingIndicator { + id: loadingIndicator + + anchors.centerIn: parent + containsIcon: true + implicitSize: Math.min(imgWrapper.width, imgWrapper.height) * 0.3 + } + } + + Behavior on opacity { + Anim { + type: Anim.DefaultEffects + } + } + } + + Image { + id: img + + anchors.fill: parent + asynchronous: true + fillMode: Image.PreserveAspectCrop + sourceSize: { + const dpr = (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1; + return Qt.size(width * dpr, height * dpr); + } + retainWhileLoading: true + opacity: status === Image.Ready ? 1 : 0 + + Behavior on opacity { + Anim { + type: Anim.SlowEffects + } + } + } + } + + StyledText { + id: label + + Layout.bottomMargin: Tokens.padding.small + Layout.fillWidth: true + color: Colours.palette.m3onSurfaceVariant + font: Tokens.font.label.builders.small.weight(Font.Medium).build() + horizontalAlignment: Text.AlignHCenter + elide: Text.ElideRight + } + } + + StateLayer { + anchors.bottomMargin: root.fillLabel ? 0 : layout.implicitHeight - imgWrapper.implicitHeight + onClicked: root.clicked() + } +} diff --git a/modules/nexus/pages/wallandstyle/WallpaperCategory.qml b/modules/nexus/pages/wallandstyle/WallpaperCategory.qml new file mode 100644 index 00000000..891df158 --- /dev/null +++ b/modules/nexus/pages/wallandstyle/WallpaperCategory.qml @@ -0,0 +1,38 @@ +import QtQuick +import QtQuick.Layouts +import Caelestia.Config +import Caelestia.Models +import qs.services +import qs.modules.nexus.common + +PageBase { + id: root + + title: { + const c = nState.selectedWallpaperCategory; + return c.slice(0, 1).toUpperCase() + c.slice(1); + } + isSubPage: true + + GridLayout { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + width: root.cappedWidth + + columns: Config.nexus.wallpapersPerRow + rowSpacing: Tokens.spacing.medium + columnSpacing: Tokens.spacing.large + + Repeater { + model: Wallpapers.list.filter(w => Wallpapers.getCategoryFor(w) === root.nState.selectedWallpaperCategory) + + WallItem { + required property FileSystemEntry modelData + + source: modelData.path + text: modelData.name + onClicked: Wallpapers.setWallpaper(modelData.path) + } + } + } +} diff --git a/modules/nexus/pages/wallandstyle/WallpaperSelect.qml b/modules/nexus/pages/wallandstyle/WallpaperSelect.qml index 9f60ab29..6eaf5831 100644 --- a/modules/nexus/pages/wallandstyle/WallpaperSelect.qml +++ b/modules/nexus/pages/wallandstyle/WallpaperSelect.qml @@ -2,12 +2,11 @@ pragma ComponentBehavior: Bound import QtQuick import QtQuick.Layouts -import Quickshell import Caelestia.Config import Caelestia.Models import qs.components -import qs.components.controls import qs.services +import qs.utils import qs.modules.nexus.common PageBase { @@ -22,17 +21,13 @@ PageBase { width: root.cappedWidth spacing: Tokens.spacing.small - Wallpaper { - Layout.fillWidth: true - implicitHeight: Math.round(width * 0.3) + WallItem { + imgHeight: Math.round(width * 0.3) radius: Tokens.rounding.extraLarge source: Wallpapers.current - } - - StyledText { - Layout.alignment: Qt.AlignHCenter text: qsTr("Featured wallpaper") - font: Tokens.font.label.medium + fillLabel: false + onClicked: ; // TODO } StyledText { @@ -45,95 +40,45 @@ PageBase { Layout.fillWidth: true columns: Config.nexus.wallpapersPerRow - rowSpacing: Tokens.spacing.large + rowSpacing: Tokens.spacing.medium columnSpacing: Tokens.spacing.large Repeater { - model: Wallpapers.list - - ColumnLayout { - id: wallDelegate + model: { + const walls = Wallpapers.list; + const baseDir = Paths.wallsdir; + const categories = {}; + for (const w of walls) { + if (w.parentDir !== baseDir) { + const category = Wallpapers.getCategoryFor(w); + if (category && !(category in categories)) + categories[category] = w; + } + } + return Object.values(categories); + } + WallItem { required property FileSystemEntry modelData - Layout.fillWidth: true - Layout.preferredWidth: 0 // Force uniform size - spacing: Tokens.spacing.small - - Wallpaper { - Layout.fillWidth: true - implicitHeight: width - radius: Tokens.rounding.largeIncreased - source: wallDelegate.modelData.path + source: modelData.path + text: { + if (modelData.parentDir !== Paths.wallsdir) { + const category = Wallpapers.getCategoryFor(modelData); + return category.slice(0, 1).toUpperCase() + category.slice(1); + } + return modelData.name; } - - StyledText { - Layout.fillWidth: true - text: wallDelegate.modelData.name - color: Colours.palette.m3onSurfaceVariant - font: Tokens.font.label.builders.small.weight(Font.Medium).build() - horizontalAlignment: Text.AlignHCenter - elide: Text.ElideRight + onClicked: { + if (modelData.parentDir !== Paths.wallsdir) { + root.nState.selectedWallpaperCategory = Wallpapers.getCategoryFor(modelData); + root.nState.openSubPage(2); + } else { + Wallpapers.setWallpaper(modelData.path); + } } } } } } - - component Wallpaper: StyledClippingRect { - id: wallpaper - - property alias source: img.source - - color: Colours.tPalette.m3surfaceContainer - - Loader { - anchors.centerIn: parent - - opacity: img.status === Image.Ready ? 0 : 1 - active: opacity > 0 - - sourceComponent: StyledRect { - implicitWidth: loadingIndicator.implicitSize + Tokens.padding.large * 2 - implicitHeight: loadingIndicator.implicitSize + Tokens.padding.large * 2 - - color: Colours.palette.m3primaryContainer - radius: Tokens.rounding.full - - LoadingIndicator { - id: loadingIndicator - - anchors.centerIn: parent - containsIcon: true - implicitSize: Math.min(wallpaper.width, wallpaper.height) * 0.3 - } - } - - Behavior on opacity { - Anim { - type: Anim.DefaultEffects - } - } - } - - Image { - id: img - - anchors.fill: parent - asynchronous: true - fillMode: Image.PreserveAspectCrop - sourceSize: { - const dpr = (QsWindow.window as QsWindow)?.devicePixelRatio ?? 1; - return Qt.size(width * dpr, height * dpr); - } - retainWhileLoading: true - opacity: status === Image.Ready ? 1 : 0 - - Behavior on opacity { - Anim { - type: Anim.SlowEffects - } - } - } - } } diff --git a/services/Wallpapers.qml b/services/Wallpapers.qml index 15daf5c1..49d8653e 100644 --- a/services/Wallpapers.qml +++ b/services/Wallpapers.qml @@ -20,6 +20,13 @@ Searcher { property string actualCurrent property bool previewColourLock + function getCategoryFor(w: FileSystemEntry): string { + let category = w.parentDir.slice(Paths.wallsdir.length + 1); + if (category.includes("/")) + category = category.slice(0, category.indexOf("/")); + return category; + } + function setWallpaper(path: string): void { actualCurrent = path; Quickshell.execDetached(["caelestia", "wallpaper", "-f", path, ...smartArg]);