forked from Shinonome/dots-hyprland
sidebar: anime: pull to load more
This commit is contained in:
@@ -0,0 +1,90 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.functions
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool loading: true
|
||||||
|
property double pullProgress: 0
|
||||||
|
|
||||||
|
// Size, color
|
||||||
|
property double implicitSize: 48
|
||||||
|
implicitWidth: implicitSize
|
||||||
|
implicitHeight: implicitSize
|
||||||
|
radius: Math.min(width, height) / 2
|
||||||
|
color: Appearance.colors.colPrimaryContainer
|
||||||
|
property double baseShapeSize: root.implicitSize * 1.3
|
||||||
|
property double leapZoomSize: root.baseShapeSize * 1.3
|
||||||
|
property double leapZoomProgress: 0
|
||||||
|
|
||||||
|
// Shape
|
||||||
|
property list<var> shapes: [
|
||||||
|
MaterialShape.Shape.SoftBurst,
|
||||||
|
MaterialShape.Shape.Cookie9Sided,
|
||||||
|
MaterialShape.Shape.Pentagon,
|
||||||
|
MaterialShape.Shape.Pill,
|
||||||
|
MaterialShape.Shape.Sunny,
|
||||||
|
MaterialShape.Shape.Cookie4Sided,
|
||||||
|
MaterialShape.Shape.Oval,
|
||||||
|
]
|
||||||
|
property int shapeIndex: 0
|
||||||
|
property double pullRotation: root.loading ? 0 : -(root.pullProgress * 360)
|
||||||
|
property double continuousRotation: 0
|
||||||
|
property double leapRotation: 0
|
||||||
|
rotation: pullRotation + continuousRotation + leapRotation
|
||||||
|
|
||||||
|
RotationAnimation on continuousRotation {
|
||||||
|
running: root.loading
|
||||||
|
duration: 12000
|
||||||
|
easing.type: Easing.Linear
|
||||||
|
loops: Animation.Infinite
|
||||||
|
from: 0
|
||||||
|
to: 360
|
||||||
|
}
|
||||||
|
Timer {
|
||||||
|
interval: 800
|
||||||
|
running: root.loading
|
||||||
|
repeat: true
|
||||||
|
onTriggered: leapAnimation.start()
|
||||||
|
}
|
||||||
|
ParallelAnimation {
|
||||||
|
id: leapAnimation
|
||||||
|
PropertyAction { target: root; property: "shapeIndex"; value: (root.shapeIndex + 1) % root.shapes.length }
|
||||||
|
RotationAnimation {
|
||||||
|
target: root
|
||||||
|
direction: RotationAnimation.Shortest
|
||||||
|
property: "leapRotation"
|
||||||
|
to: (root.leapRotation + 90) % 360
|
||||||
|
duration: 350
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
target: root
|
||||||
|
property: "leapZoomProgress"
|
||||||
|
from: 0
|
||||||
|
to: 1
|
||||||
|
duration: 750
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Appearance.animationCurves.standard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialShape {
|
||||||
|
id: shape
|
||||||
|
anchors.centerIn: parent
|
||||||
|
shape: root.shapes[root.shapeIndex]
|
||||||
|
implicitSize: {
|
||||||
|
const leapZoomDiff = root.leapZoomSize - root.baseShapeSize
|
||||||
|
const progressFirstHalf = Math.min(root.leapZoomProgress, 0.5) * 2;
|
||||||
|
const progressSecondHalf = Math.max(root.leapZoomProgress - 0.5, 0) * 2;
|
||||||
|
print("progress", root.leapZoomProgress, "zoom", root.baseShapeSize + leapZoomDiff * progressFirstHalf - leapZoomDiff * progressSecondHalf)
|
||||||
|
return root.baseShapeSize + leapZoomDiff * progressFirstHalf - leapZoomDiff * progressSecondHalf;
|
||||||
|
}
|
||||||
|
color: Appearance.colors.colOnPrimaryContainer
|
||||||
|
|
||||||
|
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,12 +23,21 @@ Item {
|
|||||||
property var suggestionQuery: ""
|
property var suggestionQuery: ""
|
||||||
property var suggestionList: []
|
property var suggestionList: []
|
||||||
|
|
||||||
|
property bool pullLoading: false
|
||||||
|
property int pullLoadingGap: 80
|
||||||
|
property real normalizedPullDistance: Math.max(0, (1 - Math.exp(-booruResponseListView.verticalOvershoot / 50)))
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: Booru
|
target: Booru
|
||||||
function onTagSuggestion(query, suggestions) {
|
function onTagSuggestion(query, suggestions) {
|
||||||
root.suggestionQuery = query;
|
root.suggestionQuery = query;
|
||||||
root.suggestionList = suggestions;
|
root.suggestionList = suggestions;
|
||||||
}
|
}
|
||||||
|
function onRunningRequestsChanged() {
|
||||||
|
if (Booru.runningRequests === 0) {
|
||||||
|
root.pullLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property var allCommands: [
|
property var allCommands: [
|
||||||
@@ -53,6 +62,8 @@ Item {
|
|||||||
if (root.responses.length > 0) {
|
if (root.responses.length > 0) {
|
||||||
const lastResponse = root.responses[root.responses.length - 1];
|
const lastResponse = root.responses[root.responses.length - 1];
|
||||||
root.handleInput(`${lastResponse.tags.join(" ")} ${parseInt(lastResponse.page) + 1}`);
|
root.handleInput(`${lastResponse.tags.join(" ")} ${parseInt(lastResponse.page) + 1}`);
|
||||||
|
} else {
|
||||||
|
root.handleInput("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -176,6 +187,14 @@ Item {
|
|||||||
downloadPath: root.downloadPath
|
downloadPath: root.downloadPath
|
||||||
nsfwPath: root.nsfwPath
|
nsfwPath: root.nsfwPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onDragEnded: { // Pull to load more
|
||||||
|
const gap = booruResponseListView.verticalOvershoot
|
||||||
|
if (gap > root.pullLoadingGap) {
|
||||||
|
root.pullLoading = true
|
||||||
|
root.handleInput(`${root.commandPrefix}next`)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PagePlaceholder {
|
PagePlaceholder {
|
||||||
@@ -192,42 +211,24 @@ Item {
|
|||||||
target: booruResponseListView
|
target: booruResponseListView
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { // Queries awaiting response
|
MaterialLoadingIndicator {
|
||||||
|
id: loadingIndicator
|
||||||
z: 4
|
z: 4
|
||||||
anchors.left: parent.left
|
anchors {
|
||||||
anchors.right: parent.right
|
horizontalCenter: parent.horizontalCenter
|
||||||
anchors.bottom: parent.bottom
|
bottom: parent.bottom
|
||||||
anchors.margins: 10
|
bottomMargin: 20 + (root.pullLoading ? 0 : Math.max(0, (root.normalizedPullDistance - 0.5) * 36))
|
||||||
implicitHeight: pendingBackground.implicitHeight
|
Behavior on bottomMargin {
|
||||||
opacity: Booru.runningRequests > 0 ? 1 : 0
|
NumberAnimation {
|
||||||
visible: opacity > 0
|
duration: 200
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
Behavior on opacity {
|
easing.bezierCurve: Appearance.animationCurves.expressiveFastSpatial
|
||||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: pendingBackground
|
|
||||||
color: Appearance.m3colors.m3inverseSurface
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
implicitHeight: pendingText.implicitHeight + 12 * 2
|
|
||||||
radius: Appearance.rounding.verysmall
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
id: pendingText
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.leftMargin: 12
|
|
||||||
anchors.rightMargin: 12
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
|
||||||
color: Appearance.m3colors.m3inverseOnSurface
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
text: Translation.tr("%1 queries pending").arg(Booru.runningRequests)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
loading: root.pullLoading || Booru.runningRequests > 0
|
||||||
|
pullProgress: Math.min(1, booruResponseListView.verticalOvershoot / root.pullLoadingGap)
|
||||||
|
scale: root.pullLoading ? 1 : Math.min(1, root.normalizedPullDistance * 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user