1 | // Copyright (C) 2017 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qquickdelaybutton_p.h" |
5 | #include "qquickabstractbutton_p_p.h" |
6 | |
7 | #include <QtQuick/private/qquickanimation_p.h> |
8 | #include <QtQuick/private/qquicktransition_p.h> |
9 | #include <QtQuick/private/qquicktransitionmanager_p_p.h> |
10 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | /*! |
14 | \qmltype DelayButton |
15 | \inherits AbstractButton |
16 | //! \instantiates QQuickDelayButton |
17 | \inqmlmodule QtQuick.Controls |
18 | \since 5.9 |
19 | \ingroup qtquickcontrols-buttons |
20 | \brief Check button that triggers when held down long enough. |
21 | |
22 | \image qtquickcontrols-delaybutton.gif |
23 | |
24 | DelayButton is a checkable button that incorporates a delay before the |
25 | button becomes \l {AbstractButton::}{checked} and the \l activated() |
26 | signal is emitted. This delay prevents accidental presses. |
27 | |
28 | The current progress is expressed as a decimal value between \c 0.0 |
29 | and \c 1.0. The time it takes for \l activated() to be emitted is |
30 | measured in milliseconds, and can be set with the \l delay property. |
31 | |
32 | The progress is indicated by a progress indicator on the button. |
33 | |
34 | \sa {Customizing DelayButton}, {Button Controls} |
35 | */ |
36 | |
37 | /*! |
38 | \qmlsignal QtQuick.Controls::DelayButton::activated() |
39 | |
40 | This signal is emitted when \l progress reaches \c 1.0. |
41 | */ |
42 | |
43 | class QQuickDelayTransitionManager; |
44 | |
45 | class QQuickDelayButtonPrivate : public QQuickAbstractButtonPrivate |
46 | { |
47 | Q_DECLARE_PUBLIC(QQuickDelayButton) |
48 | |
49 | public: |
50 | void beginTransition(qreal to); |
51 | void finishTransition(); |
52 | void cancelTransition(); |
53 | |
54 | QPalette defaultPalette() const override { return QQuickTheme::palette(scope: QQuickTheme::Button); } |
55 | |
56 | int delay = 300; |
57 | qreal progress = 0.0; |
58 | QQuickTransition *transition = nullptr; |
59 | QScopedPointer<QQuickDelayTransitionManager> transitionManager; |
60 | }; |
61 | |
62 | class QQuickDelayTransitionManager : public QQuickTransitionManager |
63 | { |
64 | public: |
65 | QQuickDelayTransitionManager(QQuickDelayButton *button) : m_button(button) { } |
66 | |
67 | void transition(QQuickTransition *transition, qreal progress); |
68 | |
69 | protected: |
70 | void finished() override; |
71 | |
72 | private: |
73 | QQuickDelayButton *m_button = nullptr; |
74 | }; |
75 | |
76 | void QQuickDelayTransitionManager::transition(QQuickTransition *transition, qreal progress) |
77 | { |
78 | qmlExecuteDeferred(transition); |
79 | |
80 | QQmlProperty defaultTarget(m_button, QLatin1String("progress" )); |
81 | QQmlListProperty<QQuickAbstractAnimation> animations = transition->animations(); |
82 | const int count = animations.count(&animations); |
83 | for (int i = 0; i < count; ++i) { |
84 | QQuickAbstractAnimation *anim = animations.at(&animations, i); |
85 | anim->setDefaultTarget(defaultTarget); |
86 | } |
87 | |
88 | QList<QQuickStateAction> actions; |
89 | actions << QQuickStateAction(m_button, QLatin1String("progress" ), progress); |
90 | QQuickTransitionManager::transition(actions, transition, defaultTarget: m_button); |
91 | } |
92 | |
93 | void QQuickDelayTransitionManager::finished() |
94 | { |
95 | if (qFuzzyCompare(p1: m_button->progress(), p2: qreal(1.0))) |
96 | emit m_button->activated(); |
97 | } |
98 | |
99 | void QQuickDelayButtonPrivate::beginTransition(qreal to) |
100 | { |
101 | Q_Q(QQuickDelayButton); |
102 | if (!transition) { |
103 | q->setProgress(to); |
104 | finishTransition(); |
105 | return; |
106 | } |
107 | |
108 | if (!transitionManager) |
109 | transitionManager.reset(other: new QQuickDelayTransitionManager(q)); |
110 | |
111 | transitionManager->transition(transition, progress: to); |
112 | } |
113 | |
114 | void QQuickDelayButtonPrivate::finishTransition() |
115 | { |
116 | Q_Q(QQuickDelayButton); |
117 | if (qFuzzyCompare(p1: progress, p2: qreal(1.0))) |
118 | emit q->activated(); |
119 | } |
120 | |
121 | void QQuickDelayButtonPrivate::cancelTransition() |
122 | { |
123 | if (transitionManager) |
124 | transitionManager->cancel(); |
125 | } |
126 | |
127 | QQuickDelayButton::QQuickDelayButton(QQuickItem *parent) |
128 | : QQuickAbstractButton(*(new QQuickDelayButtonPrivate), parent) |
129 | { |
130 | setCheckable(true); |
131 | } |
132 | |
133 | /*! |
134 | \qmlproperty int QtQuick.Controls::DelayButton::delay |
135 | |
136 | This property holds the time it takes (in milliseconds) for \l progress |
137 | to reach \c 1.0 and emit \l activated(). |
138 | |
139 | The default value is \c 3000 ms. |
140 | */ |
141 | int QQuickDelayButton::delay() const |
142 | { |
143 | Q_D(const QQuickDelayButton); |
144 | return d->delay; |
145 | } |
146 | |
147 | void QQuickDelayButton::setDelay(int delay) |
148 | { |
149 | Q_D(QQuickDelayButton); |
150 | if (d->delay == delay) |
151 | return; |
152 | |
153 | d->delay = delay; |
154 | emit delayChanged(); |
155 | } |
156 | |
157 | /*! |
158 | \qmlproperty real QtQuick.Controls::DelayButton::progress |
159 | |
160 | This property holds the current progress as displayed by the progress |
161 | indicator, in the range \c 0.0 - \c 1.0. |
162 | */ |
163 | qreal QQuickDelayButton::progress() const |
164 | { |
165 | Q_D(const QQuickDelayButton); |
166 | return d->progress; |
167 | } |
168 | |
169 | void QQuickDelayButton::setProgress(qreal progress) |
170 | { |
171 | Q_D(QQuickDelayButton); |
172 | if (qFuzzyCompare(p1: d->progress, p2: progress)) |
173 | return; |
174 | |
175 | d->progress = progress; |
176 | emit progressChanged(); |
177 | } |
178 | |
179 | /*! |
180 | \qmlproperty Transition QtQuick.Controls::DelayButton::transition |
181 | |
182 | This property holds the transition that is applied on the \l progress |
183 | property when the button is pressed or released. |
184 | */ |
185 | QQuickTransition *QQuickDelayButton::transition() const |
186 | { |
187 | Q_D(const QQuickDelayButton); |
188 | return d->transition; |
189 | } |
190 | |
191 | void QQuickDelayButton::setTransition(QQuickTransition *transition) |
192 | { |
193 | Q_D(QQuickDelayButton); |
194 | if (d->transition == transition) |
195 | return; |
196 | |
197 | d->transition = transition; |
198 | emit transitionChanged(); |
199 | } |
200 | |
201 | void QQuickDelayButton::buttonChange(ButtonChange change) |
202 | { |
203 | Q_D(QQuickDelayButton); |
204 | switch (change) { |
205 | case ButtonCheckedChange: |
206 | d->cancelTransition(); |
207 | setProgress(d->checked ? 1.0 : 0.0); |
208 | break; |
209 | case ButtonPressedChanged: |
210 | if (!d->checked) |
211 | d->beginTransition(to: d->pressed ? 1.0 : 0.0); |
212 | break; |
213 | default: |
214 | QQuickAbstractButton::buttonChange(change); |
215 | break; |
216 | } |
217 | } |
218 | |
219 | void QQuickDelayButton::nextCheckState() |
220 | { |
221 | Q_D(QQuickDelayButton); |
222 | setChecked(!d->checked && qFuzzyCompare(p1: d->progress, p2: qreal(1.0))); |
223 | } |
224 | |
225 | QFont QQuickDelayButton::defaultFont() const |
226 | { |
227 | return QQuickTheme::font(scope: QQuickTheme::Button); |
228 | } |
229 | |
230 | QT_END_NAMESPACE |
231 | |
232 | #include "moc_qquickdelaybutton_p.cpp" |
233 | |