From ede90eb282a283fa1eb07c249081b14666f95787 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 29 Oct 2025 10:24:11 +0100 Subject: [PATCH] sidebar: anime: pull to load more --- .../widgets/MaterialLoadingIndicator.qml | 90 +++++++++++++++++++ .../ii/modules/sidebarLeft/Anime.qml | 67 +++++++------- 2 files changed, 124 insertions(+), 33 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/MaterialLoadingIndicator.qml diff --git a/dots/.config/quickshell/ii/modules/common/widgets/MaterialLoadingIndicator.qml b/dots/.config/quickshell/ii/modules/common/widgets/MaterialLoadingIndicator.qml new file mode 100644 index 000000000..e810ca2e1 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/MaterialLoadingIndicator.qml @@ -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 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) + } +} diff --git a/dots/.config/quickshell/ii/modules/sidebarLeft/Anime.qml b/dots/.config/quickshell/ii/modules/sidebarLeft/Anime.qml index c673f6304..1b101d42e 100644 --- a/dots/.config/quickshell/ii/modules/sidebarLeft/Anime.qml +++ b/dots/.config/quickshell/ii/modules/sidebarLeft/Anime.qml @@ -23,12 +23,21 @@ Item { property var suggestionQuery: "" property var suggestionList: [] + property bool pullLoading: false + property int pullLoadingGap: 80 + property real normalizedPullDistance: Math.max(0, (1 - Math.exp(-booruResponseListView.verticalOvershoot / 50))) + Connections { target: Booru function onTagSuggestion(query, suggestions) { root.suggestionQuery = query; root.suggestionList = suggestions; } + function onRunningRequestsChanged() { + if (Booru.runningRequests === 0) { + root.pullLoading = false; + } + } } property var allCommands: [ @@ -53,6 +62,8 @@ Item { if (root.responses.length > 0) { const lastResponse = root.responses[root.responses.length - 1]; root.handleInput(`${lastResponse.tags.join(" ")} ${parseInt(lastResponse.page) + 1}`); + } else { + root.handleInput(""); } } }, @@ -176,6 +187,14 @@ Item { downloadPath: root.downloadPath 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 { @@ -192,42 +211,24 @@ Item { target: booruResponseListView } - Item { // Queries awaiting response + MaterialLoadingIndicator { + id: loadingIndicator z: 4 - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.margins: 10 - implicitHeight: pendingBackground.implicitHeight - opacity: Booru.runningRequests > 0 ? 1 : 0 - visible: opacity > 0 - - Behavior on opacity { - 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) + anchors { + horizontalCenter: parent.horizontalCenter + bottom: parent.bottom + bottomMargin: 20 + (root.pullLoading ? 0 : Math.max(0, (root.normalizedPullDistance - 0.5) * 36)) + Behavior on bottomMargin { + NumberAnimation { + duration: 200 + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.animationCurves.expressiveFastSpatial + } } } + loading: root.pullLoading || Booru.runningRequests > 0 + pullProgress: Math.min(1, booruResponseListView.verticalOvershoot / root.pullLoadingGap) + scale: root.pullLoading ? 1 : Math.min(1, root.normalizedPullDistance * 2) } }