dotfiles/.config/quickshell/PhotoGallery.qml

132 lines
4.2 KiB
QML
Raw Permalink Normal View History

2026-03-09 00:07:31 -04:00
import Quickshell
import QtQuick
import Quickshell.Io
FloatingWindow {
id: gallery
required property var walColors
property color bgColor: (walColors.special && walColors.special.background) ? walColors.special.background : "#1d160d"
property color accentColor: (walColors.colors && walColors.colors.color1) ? walColors.colors.color1 : "#946F50"
title: "Photo Gallery"
color: "transparent"
implicitWidth: 300
implicitHeight: 600
required property string photosPath
property var photoFiles: []
property int currentIndex: -1
property int nextIndex: -1
// Slide direction: 0 = from left, 1 = from right
property int slideDirection: 0
// Track which image layer is on top
property bool showingFront: true
Process {
id: listProc
command: ["bash", "-c", "find " + gallery.photosPath + " -maxdepth 1 -type f \\( -iname '*.png' -o -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.webp' \\) | sort"]
running: true
stdout: StdioCollector {
onDataChanged: {
let lines = text.trim().split("\n").filter(l => l.length > 0)
gallery.photoFiles = lines
if (lines.length > 0) {
gallery.currentIndex = Math.floor(Math.random() * lines.length)
}
}
}
}
Timer {
id: cycleTimer
interval: 5000
running: gallery.photoFiles.length > 1
repeat: true
onTriggered: gallery.pickNext()
}
function pickNext() {
if (photoFiles.length < 2) return
let idx
do {
idx = Math.floor(Math.random() * photoFiles.length)
} while (idx === currentIndex)
nextIndex = idx
slideDirection = Math.floor(Math.random() * 4)
showingFront = !showingFront
let incoming = showingFront ? frontImage : backImage
let outgoing = showingFront ? backImage : frontImage
// Stop any running animations and reset positions
incoming.x = 0; incoming.y = 0
outgoing.x = 0; outgoing.y = 0
incoming.source = "file://" + photoFiles[nextIndex]
incoming.z = 1
outgoing.z = 0
let horizontal = slideDirection < 2
let size = horizontal ? implicitWidth : implicitHeight
let sign = (slideDirection === 0 || slideDirection === 3) ? -1 : 1
if (horizontal) {
incoming.x = sign * -size
incomingX.from = sign * -size; incomingX.to = 0
outgoingX.from = 0; outgoingX.to = sign * size
incomingX.target = incoming; outgoingX.target = outgoing
incomingY.target = null; outgoingY.target = null
incomingX.start(); outgoingX.start()
} else {
incoming.y = sign * -size
incomingY.from = sign * -size; incomingY.to = 0
outgoingY.from = 0; outgoingY.to = sign * size
incomingY.target = incoming; outgoingY.target = outgoing
incomingX.target = null; outgoingX.target = null
incomingY.start(); outgoingY.start()
}
currentIndex = nextIndex
}
NumberAnimation { id: incomingX; property: "x"; duration: 400; easing.type: Easing.InOutCubic }
NumberAnimation { id: outgoingX; property: "x"; duration: 400; easing.type: Easing.InOutCubic }
NumberAnimation { id: incomingY; property: "y"; duration: 400; easing.type: Easing.InOutCubic }
NumberAnimation { id: outgoingY; property: "y"; duration: 400; easing.type: Easing.InOutCubic }
Item {
anchors.fill: parent
clip: true
Image {
id: backImage
width: parent.width
height: parent.height
fillMode: Image.PreserveAspectCrop
asynchronous: true
mipmap: true
z: 0
}
Image {
id: frontImage
width: parent.width
height: parent.height
fillMode: Image.PreserveAspectCrop
asynchronous: true
mipmap: true
z: 1
source: gallery.currentIndex >= 0 && gallery.photoFiles.length > 0
? "file://" + gallery.photoFiles[gallery.currentIndex]
: ""
}
}
}