Files
dotfiles/noctalia/plugins/wallcards/src/CardDeck.qml
T
Domipoke 955801c666 noctalia
2026-06-25 19:58:23 +02:00

163 lines
4.7 KiB
QML

import qs.Commons
import qs.Widgets
import QtQuick
Item {
id: cardDeck
required property int animationDuration
property real animationIndex: 0
required property int cardRadius
required property int cardSpacing
required property int cardStripWidth
required property int cardsShown
property real centerWidth: parent.width * centerWidthRatio
required property real centerWidthRatio
property real centerX: width / 2 - centerWidth / 2
property int currentIndex: 0
required property int filteredCount
required property var filteredModel
property int halfVisible: Math.floor(visibleCount / 2)
property real runningIndex: 0
required property real shearFactor
property int sideCount: Math.floor(cardsShown / 2) - 1
property int visibleCount: cardsShown
signal applyRequested(int index)
function navigateTo(idx) {
var maxLead = Math.max(1, halfVisible - 1);
if (Math.abs(runningIndex - animationIndex) >= maxLead)
return;
var newIdx = wrappedIndex(idx);
var diff = 0;
if (filteredCount > 0) {
diff = newIdx - currentIndex;
var half = filteredCount / 2;
if (diff > half)
diff -= filteredCount;
else if (diff < -half)
diff += filteredCount;
}
runningIndex += diff;
animationIndex = runningIndex;
currentIndex = newIdx;
}
function randomJump() {
var rnd = Math.floor(Math.random() * filteredCount);
if (rnd === currentIndex)
rnd = (rnd + 1) % filteredCount;
navigateTo(rnd);
}
function slotToWidth(slot) {
var t = Math.min(Math.abs(slot), 1);
return centerWidth + (cardStripWidth - centerWidth) * t;
}
function slotToX(slot) {
var rightEdge = centerX + centerWidth + cardSpacing;
var leftEdge = centerX - cardSpacing - cardStripWidth;
if (slot >= 0 && slot <= 1)
return centerX + (rightEdge - centerX) * slot;
if (slot > 1)
return rightEdge + (slot - 1) * (cardStripWidth + cardSpacing);
if (slot >= -1 && slot < 0)
return centerX + (leftEdge - centerX) * (-slot);
if (slot < -1)
return leftEdge + (slot + 1) * (cardStripWidth + cardSpacing);
return 0;
}
function wrappedIndex(idx) {
return ((idx % filteredCount) + filteredCount) % filteredCount;
}
function jumpTo(idx) {
animBehavior.enabled = false;
runningIndex = idx;
animationIndex = idx;
currentIndex = idx;
reenableTimer.restart();
}
Timer {
id: reenableTimer
interval: 0
onTriggered: animBehavior.enabled = true
}
width: (cardsShown - 3) * cardStripWidth + (cardsShown - 3) * cardSpacing + centerWidth
Behavior on animationIndex {
id: animBehavior
NumberAnimation {
duration: cardDeck.animationDuration
easing.type: Easing.OutCubic
}
}
transform: Matrix4x4 {
property real s: cardDeck.shearFactor
matrix: Qt.matrix4x4(1, s, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
}
Repeater {
model: cardDeck.filteredCount > 0 ? cardDeck.visibleCount : 0
delegate: Rectangle {
id: card
property real fractionalSlot: offset + (cardDeck.runningIndex - cardDeck.animationIndex)
property bool isCenter: offset === 0
property int modelIndex: cardDeck.wrappedIndex(Math.round(cardDeck.runningIndex) + offset)
property int offset: index - cardDeck.halfVisible
signal applyRequested
border.color: isCenter ? Color.mOutline : Color.mSurface
border.width: isCenter ? Style.borderM : Style.borderS
color: "transparent"
height: cardDeck.height
opacity: Math.max(0, Math.min(1, cardDeck.halfVisible - Math.abs(fractionalSlot)))
radius: cardDeck.cardRadius
visible: (x + width) > 0 && x < cardDeck.width
width: cardDeck.slotToWidth(fractionalSlot)
x: cardDeck.slotToX(fractionalSlot)
y: 0
z: isCenter ? 100 : cardDeck.visibleCount - Math.abs(offset)
Card {
animationDuration: cardDeck.animationDuration
centerWidth: cardDeck.centerWidth
filePath: cardDeck.filteredModel[card.modelIndex]?.filePath ?? ""
isCenter: card.isCenter
isVideo: cardDeck.filteredModel[card.modelIndex]?.isVideo ?? false
radius: cardDeck.cardRadius
thumbnailPath: cardDeck.filteredModel[card.modelIndex]?.thumbnail ?? ""
}
MouseArea {
anchors.fill: parent
cursorShape: card.isCenter ? Qt.PointingHandCursor : Qt.ArrowCursor
onClicked: {
if (card.isCenter)
cardDeck.applyRequested(card.modelIndex);
}
//
onWheel: function (wheel) {
if (wheel.angleDelta.y > 0)
cardDeck.navigateTo(cardDeck.currentIndex - 1);
else if (wheel.angleDelta.y < 0)
cardDeck.navigateTo(cardDeck.currentIndex + 1);
}
}
}
}
}