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:
QQuickDelayTransitionManager(QQuickDelayButton * button)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 
transition(QQuickTransition * transition,qreal progress)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, m_button);
122 }
123 
finished()124 void QQuickDelayTransitionManager::finished()
125 {
126     if (qFuzzyCompare(m_button->progress(), qreal(1.0)))
127         emit m_button->activated();
128 }
129 
beginTransition(qreal to)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(new QQuickDelayTransitionManager(q));
141 
142     transitionManager->transition(transition, to);
143 }
144 
finishTransition()145 void QQuickDelayButtonPrivate::finishTransition()
146 {
147     Q_Q(QQuickDelayButton);
148     if (qFuzzyCompare(progress, qreal(1.0)))
149         emit q->activated();
150 }
151 
cancelTransition()152 void QQuickDelayButtonPrivate::cancelTransition()
153 {
154     if (transitionManager)
155         transitionManager->cancel();
156 }
157 
QQuickDelayButton(QQuickItem * parent)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 */
delay() const172 int QQuickDelayButton::delay() const
173 {
174     Q_D(const QQuickDelayButton);
175     return d->delay;
176 }
177 
setDelay(int delay)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 */
progress() const195 qreal QQuickDelayButton::progress() const
196 {
197     Q_D(const QQuickDelayButton);
198     return d->progress;
199 }
200 
setProgress(qreal progress)201 void 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 */
transition() const217 QQuickTransition *QQuickDelayButton::transition() const
218 {
219     Q_D(const QQuickDelayButton);
220     return d->transition;
221 }
222 
setTransition(QQuickTransition * transition)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 
buttonChange(ButtonChange change)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(d->pressed ? 1.0 : 0.0);
244         break;
245     default:
246         QQuickAbstractButton::buttonChange(change);
247         break;
248     }
249 }
250 
nextCheckState()251 void QQuickDelayButton::nextCheckState()
252 {
253     Q_D(QQuickDelayButton);
254     setChecked(!d->checked && qFuzzyCompare(d->progress, qreal(1.0)));
255 }
256 
defaultFont() const257 QFont QQuickDelayButton::defaultFont() const
258 {
259     return QQuickTheme::font(QQuickTheme::Button);
260 }
261 
defaultPalette() const262 QPalette QQuickDelayButton::defaultPalette() const
263 {
264     return QQuickTheme::palette(QQuickTheme::Button);
265 }
266 
267 QT_END_NAMESPACE
268