C++ / fmt (pull_request) Failing after 6s
C++ / build (pull_request) Failing after 10s
C++ / clang-tidy (pull_request) Failing after 11s
JS/TS / fmt (pull_request) Successful in 10s
JS/TS / lint (pull_request) Successful in 8s
Python / static (pull_request) Failing after 32s
Rust / build (pull_request) Successful in 52s
Python / verify (pull_request) Failing after 1m23s
Rust / fmt (pull_request) Successful in 51s
Rust / clippy (pull_request) Successful in 1m2s
244 lines
5.7 KiB
QML
244 lines
5.7 KiB
QML
pragma Singleton
|
|
pragma ComponentBehavior: Bound
|
|
|
|
import Quickshell
|
|
import Quickshell.Io
|
|
import QtQuick
|
|
import ZShell.Models
|
|
import ZShell.Wallpaper
|
|
import ZShell.Config
|
|
import qs.Components
|
|
import qs.Services
|
|
import qs.Helpers
|
|
import qs.Paths
|
|
|
|
Searcher {
|
|
id: root
|
|
|
|
property string actualCurrent: WallpaperPath.currentWallpaperPath
|
|
readonly property string current: showPreview ? previewPath : actualCurrent
|
|
readonly property string currentThumbnail: thumbnailFor(current)
|
|
readonly property bool currentIsVideo: isVideoPath(current)
|
|
property bool pendingPreviewClear
|
|
property bool previewColorLock
|
|
property string previewPath
|
|
property bool recentlyChanged
|
|
property bool showPreview: false
|
|
property int cropVersion: 0
|
|
|
|
function getCategoryFor(w: WallpaperEntry): string {
|
|
let category = w.parentDir.slice(Paths.wallsdir.length + 1);
|
|
if (category.includes("/"))
|
|
category = category.slice(0, category.indexOf("/"));
|
|
return category;
|
|
}
|
|
|
|
readonly property var videoExts: ["mp4", "mkv", "webm", "avi", "mov", "wmv", "flv", "m4v", "mpg", "mpeg", "3gp", "ogv", "ts", "vob"]
|
|
|
|
function isVideoPath(path: string): bool {
|
|
if (!path)
|
|
return false;
|
|
const dot = path.lastIndexOf(".");
|
|
if (dot < 0)
|
|
return false;
|
|
return videoExts.includes(path.slice(dot + 1).toLowerCase());
|
|
}
|
|
|
|
function thumbnailFor(path: string): string {
|
|
if (!path)
|
|
return "";
|
|
|
|
for (const entry of root.list) {
|
|
if (entry.path === path)
|
|
return entry.thumbnailPath || path;
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
function getCrop(screen: string): var {
|
|
return wallpapers.crop(root.current, screen);
|
|
}
|
|
|
|
function preview(path: string): void {
|
|
previewPath = path;
|
|
showPreview = true;
|
|
|
|
if (Config.colors.schemeGen)
|
|
previewColorsProc.running = true;
|
|
}
|
|
|
|
function setCrop(screen: string, rect: rect, zoom: real): void {
|
|
if (zoom <= 0)
|
|
zoom = 1.0;
|
|
else if (zoom > 5.0)
|
|
zoom = 5.0;
|
|
|
|
wallpapers.setCrop(root.current, screen, {
|
|
x: rect.x,
|
|
y: rect.y,
|
|
width: rect.width,
|
|
height: rect.height,
|
|
zoom: zoom
|
|
});
|
|
}
|
|
|
|
function setWallpaper(path: string): void {
|
|
actualCurrent = path;
|
|
WallpaperPath.currentWallpaperPath = path;
|
|
Quickshell.execDetached(["zshell-cli", "wallpaper", "lockscreen", "--input-image", `${root.actualCurrent}`, "--output-path", `${Paths.state}/lockscreen_bg.png`, "--blur-amount", `${Config.lock.blurAmount}`]);
|
|
if (Config.colors.schemeGen)
|
|
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${root.actualCurrent}`, "--scheme", `${Config.colors.schemeType}`, "--mode", `${Config.colors.mode}`]);
|
|
}
|
|
|
|
function stopPreview(): void {
|
|
showPreview = false;
|
|
if (previewColorLock)
|
|
pendingPreviewClear = true;
|
|
else
|
|
Colors.showPreview = false;
|
|
}
|
|
|
|
extraOpts: useFuzzy ? ({}) : ({
|
|
forward: false
|
|
})
|
|
key: "relativePath"
|
|
list: wallpapers.entries
|
|
useFuzzy: true
|
|
|
|
onPreviewColorLockChanged: {
|
|
if (!previewColorLock && pendingPreviewClear)
|
|
Colors.showPreview = false;
|
|
}
|
|
|
|
IpcHandler {
|
|
function set(path: string): void {
|
|
root.setWallpaper(path);
|
|
}
|
|
|
|
target: "wallpaper"
|
|
}
|
|
|
|
WallpaperDb {
|
|
id: wallpapers
|
|
|
|
dbPath: `${Paths.state}/wallpapers.sqlite`
|
|
cacheSize: wallpaper.largestBufferSize
|
|
requireHardwareCodec: true
|
|
path: Config.general.wallpaperPath
|
|
}
|
|
|
|
Process {
|
|
id: previewColorsProc
|
|
|
|
command: ["zshell-cli", "scheme", "generate", "-p", "-i", root.previewPath]
|
|
|
|
stdout: StdioCollector {
|
|
onStreamFinished: {
|
|
Colors.load(text, true);
|
|
Colors.showPreview = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
WallpaperService {
|
|
id: wallpaper
|
|
|
|
dbPath: wallpapers.dbPath
|
|
path: root.current
|
|
}
|
|
|
|
Connections {
|
|
target: wallpapers
|
|
|
|
function onCacheStored(path: string): void {
|
|
if (path === root.current)
|
|
wallpaper.invalidateCache();
|
|
}
|
|
|
|
function onCropsChanged(path: string): void {
|
|
if (path === root.current)
|
|
root.cropVersion++;
|
|
}
|
|
}
|
|
|
|
Variants {
|
|
id: screenVariants
|
|
model: Quickshell.screens
|
|
|
|
WallpaperObject {
|
|
id: obj
|
|
required property var modelData
|
|
|
|
// Crop animation only for edits, never for wallpaper swaps.
|
|
property bool animateCrop: false
|
|
|
|
screen: modelData.name
|
|
service: wallpaper
|
|
|
|
// Apply the wallpaper's own crop. Driven off the service's pathChanged
|
|
// (which fires after applyPath has snapshotted the outgoing frame with
|
|
// the outgoing crop) and off crop edits, never as a reactive binding on
|
|
// the wallpaper path — so a swap applies the incoming crop atomically
|
|
// after the base is captured.
|
|
function seedCrop(): void {
|
|
// Load the current wallpaper's crop without pushing: the service
|
|
// already read it from the database. This only seeds the values so
|
|
// a later edit animates from the right starting point.
|
|
obj.syncCrop(root.getCrop(modelData.name));
|
|
}
|
|
|
|
// Apply an edited crop: assign via the setters so the Behaviors animate
|
|
// the numbers and push each intermediate value to the service.
|
|
function editCrop(): void {
|
|
const c = root.getCrop(modelData.name);
|
|
obj.cropX = c?.x ?? 0.0;
|
|
obj.cropY = c?.y ?? 0.0;
|
|
obj.cropWidth = c?.width ?? 1.0;
|
|
obj.cropHeight = c?.height ?? 1.0;
|
|
obj.zoom = c?.zoom ?? 1.0;
|
|
}
|
|
|
|
Component.onCompleted: seedCrop()
|
|
onScreenChanged: seedCrop()
|
|
|
|
Behavior on cropHeight {
|
|
enabled: obj.animateCrop
|
|
Anim {}
|
|
}
|
|
Behavior on cropWidth {
|
|
enabled: obj.animateCrop
|
|
Anim {}
|
|
}
|
|
Behavior on cropX {
|
|
enabled: obj.animateCrop
|
|
Anim {}
|
|
}
|
|
Behavior on cropY {
|
|
enabled: obj.animateCrop
|
|
Anim {}
|
|
}
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: wallpaper
|
|
function onPathChanged(): void {
|
|
for (const obj of screenVariants.instances) {
|
|
obj.animateCrop = false;
|
|
obj.seedCrop();
|
|
}
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: root
|
|
function onCropVersionChanged(): void {
|
|
for (const obj of screenVariants.instances) {
|
|
obj.animateCrop = true;
|
|
obj.editCrop();
|
|
}
|
|
}
|
|
}
|
|
}
|