diff --git a/Helpers/UPower.qml b/Helpers/UPower.qml index eec419f..cef9b1a 100644 --- a/Helpers/UPower.qml +++ b/Helpers/UPower.qml @@ -6,7 +6,21 @@ import Quickshell.Services.UPower Singleton { id: root + readonly property real batteryPercent: UPower.displayDevice.percentage readonly property list devices: UPower.devices.values readonly property UPowerDevice displayDevice: UPower.displayDevice readonly property bool onBattery: UPower.onBattery + // property bool toastShown + // + // Connections { + // target: UPower + // + // function onPercentageChanged(): { + // if (root.batteryPercent >= 0.2 && toastShown) + // return; + // + // root.toastShown = true; + // Toaster.toast(qsTr("Battery ")) + // } + // } } diff --git a/Helpers/Wallpapers.qml b/Helpers/Wallpapers.qml index c22920d..06fdfeb 100644 --- a/Helpers/Wallpapers.qml +++ b/Helpers/Wallpapers.qml @@ -1,7 +1,9 @@ pragma Singleton +pragma ComponentBehavior: Bound import Quickshell import Quickshell.Io +import QtQuick import ZShell.Models import qs.Config import qs.Modules @@ -12,11 +14,17 @@ Searcher { id: root property string actualCurrent: WallpaperPath.currentWallpaperPath + property alias crops: adapter.monitorCrops readonly property string current: showPreview ? previewPath : actualCurrent + property alias monitorCrops: monitorCrops property string previewPath property bool recentlyChanged property bool showPreview: false + function getCrop(screen: string): var { + return root.crops[screen]; + } + function preview(path: string): void { previewPath = path; if (Config.general.color.schemeGeneration) @@ -24,9 +32,26 @@ Searcher { showPreview = true; } + function setCrop(screen: string, rect: rect, zoom: real): void { + let updated = Object.assign({}, root.crops); + + updated[screen] = { + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height, + zoom: zoom + }; + + root.crops = updated; + monitorCrops.writeAdapter(); + monitorCrops.reload(); + } + function setWallpaper(path: string): void { actualCurrent = path; WallpaperPath.currentWallpaperPath = path; + Quickshell.screens.forEach(n => setCrop(n.name, Qt.rect(0, 0, 0, 0), 1.0)); Quickshell.execDetached(["zshell-cli", "wallpaper", "lockscreen", "--input-image", `${root.actualCurrent}`, "--output-path", `${Paths.state}/lockscreen_bg.png`, "--blur-amount", `${Config.lock.blurAmount}`]); if (Config.general.color.schemeGeneration) Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${root.actualCurrent}`, "--scheme", `${Config.colors.schemeType}`, "--mode", `${Config.general.color.mode}`]); @@ -53,6 +78,22 @@ Searcher { target: "wallpaper" } + FileView { + id: monitorCrops + + path: `${Paths.state}/wallpaper-crops.json` + watchChanges: true + + onAdapterUpdated: writeAdapter() + onFileChanged: reload() + + JsonAdapter { + id: adapter + + property var monitorCrops: ({}) + } + } + FileSystemModel { id: wallpapers diff --git a/Modules/Settings/Categories/Background.qml b/Modules/Settings/Categories/Background.qml index 68a4931..d519565 100644 --- a/Modules/Settings/Categories/Background.qml +++ b/Modules/Settings/Categories/Background.qml @@ -1,3 +1,4 @@ +import Quickshell import QtQuick.Layouts import qs.Modules.Settings.Controls import qs.Config @@ -5,6 +6,8 @@ import qs.Config SettingsPage { id: root + required property ShellScreen screen + SettingsSection { sectionId: "Wallpaper" @@ -35,6 +38,7 @@ SettingsPage { WallpaperCropper { Layout.fillWidth: true Layout.preferredHeight: 600 + screen: root.screen } } diff --git a/Modules/Settings/Content.qml b/Modules/Settings/Content.qml index e06bc36..81d33cf 100644 --- a/Modules/Settings/Content.qml +++ b/Modules/Settings/Content.qml @@ -1,3 +1,5 @@ +pragma ComponentBehavior: Bound + import Quickshell import Quickshell.Widgets import QtQuick @@ -20,7 +22,6 @@ Item { required property PersistentProperties visibilities function scrollToSetting(section: string, settingName: string) { - // Wait for the StackView transition to complete, then scroll root.pendingSection = section; root.pendingSetting = settingName; scrollTimer.restart(); @@ -157,6 +158,7 @@ Item { id: background Cat.Background { + screen: root.screen } } diff --git a/Modules/Settings/Controls/WallpaperCropper.qml b/Modules/Settings/Controls/WallpaperCropper.qml index a17acda..5627c9f 100644 --- a/Modules/Settings/Controls/WallpaperCropper.qml +++ b/Modules/Settings/Controls/WallpaperCropper.qml @@ -1,3 +1,5 @@ +pragma ComponentBehavior: Bound + import QtQuick import QtQuick.Layouts import QtQuick.Controls @@ -6,108 +8,130 @@ import qs.Config import qs.Components import qs.Helpers -Item { +RowLayout { id: root - Image { - id: imageView + required property ShellScreen screen - property real displayH: paintedHeight - property real displayW: paintedWidth - property real displayX: (width - paintedWidth) * 0.5 - property real displayY: (height - paintedHeight) * 0.5 - property real scaleX: sourceW / displayW - property real scaleY: sourceH / displayH - property real sourceH: Quickshell.screens[0].height - property real sourceW: Quickshell.screens[0].width + spacing: Appearance.spacing.normal - anchors.fill: parent - fillMode: Image.PreserveAspectFit - smooth: true - source: Wallpapers.current + Repeater { + model: Quickshell.screens + + delegate: ImgCrop { + } } - Item { - id: overlay + component ImgCrop: Item { + id: cropper - clip: true - height: imageView.displayH - width: imageView.displayW - x: imageView.displayX - y: imageView.displayY + readonly property var displayData: Wallpapers.getCrop(modelData.name) + required property ShellScreen modelData - CustomRect { - id: cropRect + Layout.fillHeight: true + Layout.fillWidth: true - property real aspectRatio: Quickshell.screens[0].width / Quickshell.screens[0].height - readonly property rect sourceRect: Qt.rect(x * imageView.scaleX, y * imageView.scaleY, width * imageView.scaleX, height * imageView.scaleY) - property real zoom: Config.background.zoom + Image { + id: imageView - function clampToBounds() { - x = Math.max(0, Math.min(x, overlay.width - width)); - - y = Math.max(0, Math.min(y, overlay.height - height)); - } - - border.color: DynamicColors.palette.m3primary - border.width: 2 - color: DynamicColors.tPalette.m3primary - height: width / aspectRatio - radius: Appearance.rounding.small - visible: imageView.status === Image.Ready - width: Math.min(overlay.width / zoom, overlay.height * aspectRatio / zoom) - x: Config.background.sourceClipX / imageView.scaleX - y: Config.background.sourceClipY / imageView.scaleY - } - - MouseArea { - function updateCrop(mouseX, mouseY) { - let nx = mouseX - cropRect.width * 0.5; - let ny = mouseY - cropRect.height * 0.5; - - nx = Math.max(0, Math.min(nx, overlay.width - cropRect.width)); - - ny = Math.max(0, Math.min(ny, overlay.height - cropRect.height)); - - cropRect.x = nx; - cropRect.y = ny; - } + property real displayH: paintedHeight + property real displayW: paintedWidth + property real displayX: (width - paintedWidth) * 0.5 + property real displayY: (height - paintedHeight) * 0.5 + property real scaleX: sourceW / displayW + property real scaleY: sourceH / displayH + property real sourceH: cropper.modelData.height + property real sourceW: cropper.modelData.width anchors.fill: parent - hoverEnabled: true - preventStealing: true + fillMode: Image.PreserveAspectFit + smooth: true + source: Wallpapers.current + } - onPositionChanged: mouse => { - if (pressed) + Item { + id: overlay + + clip: true + height: imageView.displayH + width: imageView.displayW + x: imageView.displayX + y: imageView.displayY + + CustomRect { + id: cropRect + + property real aspectRatio: cropper.modelData.width / cropper.modelData.height + readonly property rect sourceRect: Qt.rect(x * imageView.scaleX, y * imageView.scaleY, width * imageView.scaleX, height * imageView.scaleY) + property real zoom: cropper.displayData.zoom + + function clampToBounds() { + x = Math.max(0, Math.min(x, overlay.width - width)); + + y = Math.max(0, Math.min(y, overlay.height - height)); + } + + border.color: DynamicColors.palette.m3primary + border.width: 2 + color: DynamicColors.tPalette.m3primary + height: width / aspectRatio + radius: Appearance.rounding.small + visible: imageView.status === Image.Ready + width: Math.min(overlay.width / zoom, overlay.height * aspectRatio / zoom) + x: cropper.displayData.x / imageView.scaleX + y: cropper.displayData.y / imageView.scaleY + } + + MouseArea { + function updateCrop(mouseX, mouseY) { + let nx = mouseX - cropRect.width * 0.5; + let ny = mouseY - cropRect.height * 0.5; + + nx = Math.max(0, Math.min(nx, overlay.width - cropRect.width)); + + ny = Math.max(0, Math.min(ny, overlay.height - cropRect.height)); + + cropRect.x = nx; + cropRect.y = ny; + } + + anchors.fill: parent + hoverEnabled: true + preventStealing: true + + onPositionChanged: mouse => { + if (pressed) + updateCrop(mouse.x, mouse.y); + } + onPressed: mouse => { updateCrop(mouse.x, mouse.y); - } - onPressed: mouse => { - updateCrop(mouse.x, mouse.y); - } - onReleased: { - Wallpapers.recentlyChanged = false; - Config.background.sourceClipX = cropRect.sourceRect.x; - Config.background.sourceClipY = cropRect.sourceRect.y; - Config.background.sourceClipW = cropRect.sourceRect.width; - Config.background.sourceClipH = cropRect.sourceRect.height; - Config.save(); - } - onWheel: wheel => { - let oldCenterX = cropRect.x + cropRect.width * 0.5; - let oldCenterY = cropRect.y + cropRect.height * 0.5; + } + onReleased: { + Wallpapers.recentlyChanged = false; + Wallpapers.setCrop(cropper.modelData.name, cropRect.sourceRect, cropRect.zoom); + // Config.background.sourceClipX = cropRect.sourceRect.x; + // Config.background.sourceClipY = cropRect.sourceRect.y; + // Config.background.sourceClipW = cropRect.sourceRect.width; + // Config.background.sourceClipH = cropRect.sourceRect.height; + // Config.save(); + } + onWheel: wheel => { + let oldCenterX = cropRect.x + cropRect.width * 0.5; + let oldCenterY = cropRect.y + cropRect.height * 0.5; - if (wheel.angleDelta.y > 0) - cropRect.zoom *= 1.1; - else - cropRect.zoom /= 1.1; + if (wheel.angleDelta.y > 0) + cropRect.zoom *= 1.1; + else + cropRect.zoom /= 1.1; - cropRect.zoom = Math.max(1.0, Math.min(cropRect.zoom, 10.0)); - Config.background.zoom = cropRect.zoom; + cropRect.zoom = Math.max(1.0, Math.min(cropRect.zoom, 10.0)); + Config.background.zoom = cropRect.zoom; - cropRect.x = oldCenterX - cropRect.width * 0.5; - cropRect.y = oldCenterY - cropRect.height * 0.5; + cropRect.x = oldCenterX - cropRect.width * 0.5; + cropRect.y = oldCenterY - cropRect.height * 0.5; - cropRect.clampToBounds(); + cropRect.clampToBounds(); + } } } } diff --git a/Modules/Wallpaper/WallBackground.qml b/Modules/Wallpaper/WallBackground.qml index 9c95859..ec2e667 100644 --- a/Modules/Wallpaper/WallBackground.qml +++ b/Modules/Wallpaper/WallBackground.qml @@ -17,26 +17,63 @@ Item { Image { id: img - anchors.fill: parent asynchronous: true - fillMode: Image.PreserveAspectCrop + fillMode: Image.Stretch opacity: 1 retainWhileLoading: true source: root.source - sourceClipRect: Wallpapers.recentlyChanged ? null : Qt.rect(Config.background.sourceClipX, Config.background.sourceClipY, Config.background.sourceClipW, Config.background.sourceClipH) sourceSize.height: root.screen.height sourceSize.width: root.screen.width - onSourceChanged: { - if (Wallpapers.recentlyChanged) { - Config.background.sourceClipH = 0; - Config.background.sourceClipW = 0; - Config.background.sourceClipY = 0; - Config.background.sourceClipX = 0; - Config.background.zoom = 1.0; - Config.save(); + Behavior on height { + Anim { } - Wallpapers.recentlyChanged = true; + } + Behavior on width { + Anim { + } + } + Behavior on x { + Anim { + } + } + Behavior on y { + Anim { + } + } + + Connections { + function onAdapterUpdated(): void { + const displayData = Wallpapers.getCrop(root.screen.name); + const displayRect = Qt.rect(displayData.x, displayData.y, displayData.width, displayData.height); + const scale = root.screen.width / displayData.width; + if (displayRect.width > 0 && displayRect.height > 0) { + img.anchors.fill = null; + img.x = -displayRect.x; + img.y = -displayRect.y; + img.width = img.implicitWidth * scale; + img.height = img.implicitHeight * scale; + } else { + img.anchors.fill = root; + } + } + + function onLoaded(): void { + const displayData = Wallpapers.getCrop(root.screen.name); + const displayRect = Qt.rect(displayData.x, displayData.y, displayData.width, displayData.height); + const scale = root.screen.width / displayData.width; + if (displayRect.width > 0 && displayRect.height > 0) { + img.anchors.fill = null; + img.x = -displayRect.x; + img.y = -displayRect.y; + img.width = img.implicitWidth * scale; + img.height = img.implicitHeight * scale; + } else { + img.anchors.fill = root; + } + } + + target: Wallpapers.monitorCrops } } }