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
44QT_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
76class QQuickDelayTransitionManager;
77
78class QQuickDelayButtonPrivate : public QQuickAbstractButtonPrivate
79{
80 Q_DECLARE_PUBLIC(QQuickDelayButton)
81
82public:
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
93class QQuickDelayTransitionManager : public QQuickTransitionManager
94{
95public:
96 QQuickDelayTransitionManager(QQuickDelayButton *button) : m_button(button) { }
97
98 void transition(QQuickTransition *transition, qreal progress);
99
100protected:
101 void finished() override;
102
103private:
104 QQuickDelayButton *m_button = nullptr;
105};
106
107void 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, m_button);
122}
123
124void QQuickDelayTransitionManager::finished()
125{
126 if (qFuzzyCompare(m_button->progress(), qreal(1.0)))
127 emit m_button->activated();
128}
129
130void 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(new QQuickDelayTransitionManager(q));
141
142 transitionManager->transition(transition, to);
143}
144
145void QQuickDelayButtonPrivate::finishTransition()
146{
147 Q_Q(QQuickDelayButton);
148 if (qFuzzyCompare(progress, qreal(1.0)))
149 emit q->activated();
150}
151
152void QQuickDelayButtonPrivate::cancelTransition()
153{
154 if (transitionManager)
155 transitionManager->cancel();
156}
157
158QQuickDelayButton::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*/
172int QQuickDelayButton::delay() const
173{
174 Q_D(const QQuickDelayButton);
175 return d->delay;
176}
177
178void 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*/
195qreal QQuickDelayButton::progress() const
196{
197 Q_D(const QQuickDelayButton);
198 return d->progress;
199}
200
201void QQuickDelayButton::setProgress(qreal progress)
202{
203 Q_D(QQuickDelayButton);
204 if (qFuzzyCompare(d->progress, 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*/
217QQuickTransition *QQuickDelayButton::transition() const
218{
219 Q_D(const QQuickDelayButton);
220 return d->transition;
221}
222
223void 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
233void 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(d->pressed ? 1.0 : 0.0);
244 break;
245 default:
246 QQuickAbstractButton::buttonChange(change);
247 break;
248 }
249}
250
251void QQuickDelayButton::nextCheckState()
252{
253 Q_D(QQuickDelayButton);
254 setChecked(!d->checked && qFuzzyCompare(d->progress, qreal(1.0)));
255}
256
257QFont QQuickDelayButton::defaultFont() const
258{
259 return QQuickTheme::font(QQuickTheme::Button);
260}
261
262QPalette QQuickDelayButton::defaultPalette() const
263{
264 return QQuickTheme::palette(QQuickTheme::Button);
265}
266
267QT_END_NAMESPACE
268