1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 The Qt Company Ltd. |
4 | ** Contact: http://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL3$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see http://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at http://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or later as published by the Free |
28 | ** Software Foundation and appearing in the file LICENSE.GPL included in |
29 | ** the packaging of this file. Please review the following information to |
30 | ** ensure the GNU General Public License version 2.0 requirements will be |
31 | ** met: http://www.gnu.org/licenses/gpl-2.0.html. |
32 | ** |
33 | ** $QT_END_LICENSE$ |
34 | ** |
35 | ****************************************************************************/ |
36 | |
37 | #include "qquickdelaybutton_p.h" |
38 | #include "qquickabstractbutton_p_p.h" |
39 | |
40 | #include <QtQuick/private/qquickanimation_p.h> |
41 | #include <QtQuick/private/qquicktransition_p.h> |
42 | #include <QtQuick/private/qquicktransitionmanager_p_p.h> |
43 | |
44 | QT_BEGIN_NAMESPACE |
45 | |
46 | /*! |
47 | \qmltype DelayButton |
48 | \inherits AbstractButton |
49 | //! \instantiates QQuickDelayButton |
50 | \inqmlmodule QtQuick.Controls |
51 | \since 5.9 |
52 | \ingroup qtquickcontrols2-buttons |
53 | \brief Check button that triggers when held down long enough. |
54 | |
55 | \image qtquickcontrols2-delaybutton.gif |
56 | |
57 | DelayButton is a checkable button that incorporates a delay before the |
58 | button becomes \l {AbstractButton::}{checked} and the \l activated() |
59 | signal is emitted. This delay prevents accidental presses. |
60 | |
61 | The current progress is expressed as a decimal value between \c 0.0 |
62 | and \c 1.0. The time it takes for \l activated() to be emitted is |
63 | measured in milliseconds, and can be set with the \l delay property. |
64 | |
65 | The progress is indicated by a progress indicator on the button. |
66 | |
67 | \sa {Customizing DelayButton}, {Button Controls} |
68 | */ |
69 | |
70 | /*! |
71 | \qmlsignal QtQuick.Controls::DelayButton::activated() |
72 | |
73 | This signal is emitted when \l progress reaches \c 1.0. |
74 | */ |
75 | |
76 | class QQuickDelayTransitionManager; |
77 | |
78 | class QQuickDelayButtonPrivate : public QQuickAbstractButtonPrivate |
79 | { |
80 | Q_DECLARE_PUBLIC(QQuickDelayButton) |
81 | |
82 | public: |
83 | void beginTransition(qreal to); |
84 | void finishTransition(); |
85 | void cancelTransition(); |
86 | |
87 | int delay = 300; |
88 | qreal progress = 0.0; |
89 | QQuickTransition *transition = nullptr; |
90 | QScopedPointer<QQuickDelayTransitionManager> transitionManager; |
91 | }; |
92 | |
93 | class QQuickDelayTransitionManager : public QQuickTransitionManager |
94 | { |
95 | public: |
96 | QQuickDelayTransitionManager(QQuickDelayButton *button) : m_button(button) { } |
97 | |
98 | void transition(QQuickTransition *transition, qreal progress); |
99 | |
100 | protected: |
101 | void finished() override; |
102 | |
103 | private: |
104 | QQuickDelayButton *m_button = nullptr; |
105 | }; |
106 | |
107 | void QQuickDelayTransitionManager::transition(QQuickTransition *transition, qreal progress) |
108 | { |
109 | qmlExecuteDeferred(transition); |
110 | |
111 | QQmlProperty defaultTarget(m_button, QLatin1String("progress" )); |
112 | QQmlListProperty<QQuickAbstractAnimation> animations = transition->animations(); |
113 | const int count = animations.count(&animations); |
114 | for (int i = 0; i < count; ++i) { |
115 | QQuickAbstractAnimation *anim = animations.at(&animations, i); |
116 | anim->setDefaultTarget(defaultTarget); |
117 | } |
118 | |
119 | QList<QQuickStateAction> actions; |
120 | actions << QQuickStateAction(m_button, QLatin1String("progress" ), progress); |
121 | QQuickTransitionManager::transition(actions, transition, defaultTarget: m_button); |
122 | } |
123 | |
124 | void QQuickDelayTransitionManager::finished() |
125 | { |
126 | if (qFuzzyCompare(p1: m_button->progress(), p2: qreal(1.0))) |
127 | emit m_button->activated(); |
128 | } |
129 | |
130 | void QQuickDelayButtonPrivate::beginTransition(qreal to) |
131 | { |
132 | Q_Q(QQuickDelayButton); |
133 | if (!transition) { |
134 | q->setProgress(to); |
135 | finishTransition(); |
136 | return; |
137 | } |
138 | |
139 | if (!transitionManager) |
140 | transitionManager.reset(other: new QQuickDelayTransitionManager(q)); |
141 | |
142 | transitionManager->transition(transition, progress: to); |
143 | } |
144 | |
145 | void QQuickDelayButtonPrivate::finishTransition() |
146 | { |
147 | Q_Q(QQuickDelayButton); |
148 | if (qFuzzyCompare(p1: progress, p2: qreal(1.0))) |
149 | emit q->activated(); |
150 | } |
151 | |
152 | void QQuickDelayButtonPrivate::cancelTransition() |
153 | { |
154 | if (transitionManager) |
155 | transitionManager->cancel(); |
156 | } |
157 | |
158 | QQuickDelayButton::QQuickDelayButton(QQuickItem *parent) |
159 | : QQuickAbstractButton(*(new QQuickDelayButtonPrivate), parent) |
160 | { |
161 | setCheckable(true); |
162 | } |
163 | |
164 | /*! |
165 | \qmlproperty int QtQuick.Controls::DelayButton::delay |
166 | |
167 | This property holds the time it takes (in milliseconds) for \l progress |
168 | to reach \c 1.0 and emit \l activated(). |
169 | |
170 | The default value is \c 3000 ms. |
171 | */ |
172 | int QQuickDelayButton::delay() const |
173 | { |
174 | Q_D(const QQuickDelayButton); |
175 | return d->delay; |
176 | } |
177 | |
178 | void QQuickDelayButton::setDelay(int delay) |
179 | { |
180 | Q_D(QQuickDelayButton); |
181 | if (d->delay == delay) |
182 | return; |
183 | |
184 | d->delay = delay; |
185 | emit delayChanged(); |
186 | } |
187 | |
188 | /*! |
189 | \qmlproperty real QtQuick.Controls::DelayButton::progress |
190 | \readonly |
191 | |
192 | This property holds the current progress as displayed by the progress |
193 | indicator, in the range \c 0.0 - \c 1.0. |
194 | */ |
195 | qreal QQuickDelayButton::progress() const |
196 | { |
197 | Q_D(const QQuickDelayButton); |
198 | return d->progress; |
199 | } |
200 | |
201 | void QQuickDelayButton::setProgress(qreal progress) |
202 | { |
203 | Q_D(QQuickDelayButton); |
204 | if (qFuzzyCompare(p1: d->progress, p2: progress)) |
205 | return; |
206 | |
207 | d->progress = progress; |
208 | emit progressChanged(); |
209 | } |
210 | |
211 | /*! |
212 | \qmlproperty Transition QtQuick.Controls::DelayButton::transition |
213 | |
214 | This property holds the transition that is applied on the \l progress |
215 | property when the button is pressed or released. |
216 | */ |
217 | QQuickTransition *QQuickDelayButton::transition() const |
218 | { |
219 | Q_D(const QQuickDelayButton); |
220 | return d->transition; |
221 | } |
222 | |
223 | void QQuickDelayButton::setTransition(QQuickTransition *transition) |
224 | { |
225 | Q_D(QQuickDelayButton); |
226 | if (d->transition == transition) |
227 | return; |
228 | |
229 | d->transition = transition; |
230 | emit transitionChanged(); |
231 | } |
232 | |
233 | void QQuickDelayButton::buttonChange(ButtonChange change) |
234 | { |
235 | Q_D(QQuickDelayButton); |
236 | switch (change) { |
237 | case ButtonCheckedChange: |
238 | d->cancelTransition(); |
239 | setProgress(d->checked ? 1.0 : 0.0); |
240 | break; |
241 | case ButtonPressedChanged: |
242 | if (!d->checked) |
243 | d->beginTransition(to: d->pressed ? 1.0 : 0.0); |
244 | break; |
245 | default: |
246 | QQuickAbstractButton::buttonChange(change); |
247 | break; |
248 | } |
249 | } |
250 | |
251 | void QQuickDelayButton::nextCheckState() |
252 | { |
253 | Q_D(QQuickDelayButton); |
254 | setChecked(!d->checked && qFuzzyCompare(p1: d->progress, p2: qreal(1.0))); |
255 | } |
256 | |
257 | QFont QQuickDelayButton::defaultFont() const |
258 | { |
259 | return QQuickTheme::font(scope: QQuickTheme::Button); |
260 | } |
261 | |
262 | QPalette QQuickDelayButton::defaultPalette() const |
263 | { |
264 | return QQuickTheme::palette(scope: QQuickTheme::Button); |
265 | } |
266 | |
267 | QT_END_NAMESPACE |
268 | |