Kirigami2

PullDownIndicator.qml
1/*
2 * SPDX-FileCopyrightText: 2023 Connor Carney <hello@connorcarney.com>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7import QtQuick
8import QtQuick.Controls as QQC2
9import org.kde.kirigami as Kirigami
10import QtQuick.Shapes as QQShapes
11
12/**
13 * @brief A pull-down to refresh indicator that can be added to any Flickable or ScrollablePage.
14 */
15Item {
16 id: root
17
18 //BEGIN properties
19 /**
20 * @brief The flickable that this indicator is attached to.
21 *
22 * If this is not set, the indicator will search for a Flickable in its parent chain.
23 */
24 property Flickable flickable: {
25 let candidate = parent
26 while (candidate) {
27 if (candidate instanceof Flickable) {
28 return candidate
29 } else if (candidate instanceof Kirigami.ScrollablePage) {
30 return candidate.flickable
31 }
32 candidate = candidate.parent
33 }
34 return null;
35 }
36
37 /**
38 * @brief Whether to show the busy indicator at the top of the flickable
39 *
40 * This should be set to true whenever a refresh is in progress. It should typically
41 * be set to true whe triggered() is emitted, and set to false when the refresh is
42 * complete. This is not done automatically because the refresh may be triggered
43 * from outside the indicator.
44 */
45 property bool active: false
47 /**
48 * @brief How far the flickable has been pulled down, between 0 (not at all) and 1 (where a refresh is triggered).
49 */
50 readonly property real progress: !refreshing ? Math.min(-Math.min(flickable?.verticalOvershoot ?? 0, 0) / indicatorContainer.height, 1) : 0
51
52 /**
53 * @brief Time to wait after the flickable has been pulled down before triggering a refresh
54 *
55 * This gives the user a chance to back out of the refresh if they release the flickable
56 * before the refreshDelay has elapsed.
57 */
58 property int refreshDelay: 500
59
60 /**
61 * @brief emitted when the flickable is pulled down far enough to trigger a refresh
62 */
63 signal triggered()
64 //END properties
65
66 Item {
67 id: indicatorContainer
68 parent: root.flickable
69 anchors {
70 bottom: parent?.contentItem?.top
71 bottomMargin: root.flickable.topMargin
72 }
73
74 width: flickable?.width
75 height: Kirigami.Units.gridUnit * 4
76 QQC2.BusyIndicator {
77 id: busyIndicator
78 z: 1
79 anchors.centerIn: parent
80 running: root.active
81 visible: root.active
82 // Android busywidget QQC seems to be broken at custom sizes
83 }
84 QQShapes.Shape {
85 id: spinnerProgress
86 anchors {
87 fill: busyIndicator
88 margins: Kirigami.Units.smallSpacing
89 }
90 visible: !root.active && root.progress > 0
91 QQShapes.ShapePath {
92 strokeWidth: Kirigami.Units.smallSpacing
93 strokeColor: Kirigami.Theme.highlightColor
94 fillColor: "transparent"
95 PathAngleArc {
96 centerX: spinnerProgress.width / 2
97 centerY: spinnerProgress.height / 2
98 radiusX: spinnerProgress.width / 2 - Kirigami.Units.smallSpacing / 2
99 radiusY: spinnerProgress.height / 2 - Kirigami.Units.smallSpacing / 2
100 startAngle: 0
101 sweepAngle: 360 * root.progress
102 }
103 }
104 }
105 }
106
107 onProgressChanged: {
108 if (!root.active && root.progress >= 1) {
109 refreshTriggerTimer.running = true;
110 } else {
111 refreshTriggerTimer.running = false;
112 }
113 }
114
115
116 states: [
117 State {
118 name: "active"
119 when: root.active
120 PropertyChanges {
121 target: indicatorContainer
122 anchors.bottomMargin: root.flickable.topMargin - indicatorContainer.height
123 }
124 PropertyChanges {
125 target: root.flickable
126 explicit: true
127
128 // this is not a loop because of explicit:true above
129 // It adds the height of the indicator to the topMargin of the flickable
130 // when we enter the active state; the change is automatically reversed
131 // when returning to the base state.
132 topMargin: indicatorContainer.height + root.flickable.topMargin
133 }
134 }
135 ]
136
137 transitions: [
138 Transition {
139 from: ""
140 to: "active"
141 enabled: root.flickable.verticalOvershoot >= 0
142 reversible: true
143 NumberAnimation {
144 target: root.flickable
145 properties: "topMargin"
146 easing.type: Easing.InOutQuad
147 duration: Kirigami.Units.longDuration
148 }
149 }
150 ]
151
152 Timer {
153 id: refreshTriggerTimer
154 interval: root.refreshDelay
155 onTriggered: {
156 if (!root.active && root.progress >= 1) {
157 root.triggered()
158 }
159 }
160 }
161
162}
QString name(GameStandardAction id)
KGuiItem properties()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 4 2024 16:33:57 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.