Files
dots-hyprland/.config/quickshell/ii/modules/common/widgets/StyledListView.qml
T

155 lines
5.2 KiB
QML

import qs
import qs.modules.common
import qs.modules.common.widgets
import qs.services
import QtQuick
import QtQuick.Controls
/**
* A ListView with animations.
*/
ListView {
id: root
spacing: 5
property real removeOvershoot: 20 // Account for gaps and bouncy animations
property int dragIndex: -1
property real dragDistance: 0
property bool popin: true
property bool animateAppearance: true
property bool animateMovement: false
// Accumulated scroll destination so wheel deltas stack while animating
property real scrollTargetY: 0
property real touchpadScrollFactor: Config?.options.interactions.scrolling.touchpadScrollFactor ?? 100
property real mouseScrollFactor: Config?.options.interactions.scrolling.mouseScrollFactor ?? 50
property real mouseScrollDeltaThreshold: Config?.options.interactions.scrolling.mouseScrollDeltaThreshold ?? 120
function resetDrag() {
root.dragIndex = -1
root.dragDistance = 0
}
maximumFlickVelocity: 3500
boundsBehavior: Flickable.DragOverBounds
ScrollBar.vertical: StyledScrollBar {}
MouseArea {
visible: Config?.options.interactions.scrolling.fasterTouchpadScroll
anchors.fill: parent
acceptedButtons: Qt.NoButton
onWheel: function(wheelEvent) {
const delta = wheelEvent.angleDelta.y / root.mouseScrollDeltaThreshold;
// The angleDelta.y of a touchpad is usually small and continuous,
// while that of a mouse wheel is typically in multiples of ±120.
var scrollFactor = Math.abs(wheelEvent.angleDelta.y) >= root.mouseScrollDeltaThreshold ? root.mouseScrollFactor : root.touchpadScrollFactor;
const maxY = Math.max(0, root.contentHeight - root.height);
const base = scrollAnim.running ? root.scrollTargetY : root.contentY;
var targetY = Math.max(0, Math.min(base - delta * scrollFactor, maxY));
root.scrollTargetY = targetY;
root.contentY = targetY;
wheelEvent.accepted = true;
}
}
Behavior on contentY {
NumberAnimation {
id: scrollAnim
duration: Appearance.animation.scroll.duration
easing.type: Appearance.animation.scroll.type
easing.bezierCurve: Appearance.animation.scroll.bezierCurve
}
}
// Keep target synced when not animating (e.g., drag/flick or programmatic changes)
onContentYChanged: {
if (!scrollAnim.running) {
root.scrollTargetY = root.contentY;
}
}
add: Transition {
animations: animateAppearance ? [
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
properties: popin ? "opacity,scale" : "opacity",
from: 0,
to: 1,
}),
] : []
}
addDisplaced: Transition {
animations: animateAppearance ? [
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
property: "y",
}),
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
properties: popin ? "opacity,scale" : "opacity",
to: 1,
}),
] : []
}
displaced: Transition {
animations: root.animateMovement ? [
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
property: "y",
}),
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
properties: "opacity,scale",
to: 1,
}),
] : []
}
move: Transition {
animations: root.animateMovement ? [
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
property: "y",
}),
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
properties: "opacity,scale",
to: 1,
}),
] : []
}
moveDisplaced: Transition {
animations: root.animateMovement ? [
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
property: "y",
}),
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
properties: "opacity,scale",
to: 1,
}),
] : []
}
remove: Transition {
animations: animateAppearance ? [
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
property: "x",
to: root.width + root.removeOvershoot,
}),
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
property: "opacity",
to: 0,
})
] : []
}
// This is movement when something is removed, not removing animation!
removeDisplaced: Transition {
animations: animateAppearance ? [
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
property: "y",
}),
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
properties: "opacity,scale",
to: 1,
}),
] : []
}
}