1// Copyright (C) 2016 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 "qqmltimer_p.h"
5
6#include <QtCore/qcoreapplication.h>
7#include "private/qpauseanimationjob_p.h"
8#include <qdebug.h>
9
10#include <private/qobject_p.h>
11
12QT_BEGIN_NAMESPACE
13
14namespace {
15 const QEvent::Type QEvent_MaybeTick = QEvent::Type(QEvent::User + 1);
16 const QEvent::Type QEvent_Triggered = QEvent::Type(QEvent::User + 2);
17}
18
19class QQmlTimerPrivate : public QObjectPrivate, public QAnimationJobChangeListener
20{
21 Q_DECLARE_PUBLIC(QQmlTimer)
22public:
23 QQmlTimerPrivate()
24 : running(false), repeating(false), triggeredOnStart(false)
25 , classBegun(false), componentComplete(false), firstTick(true), awaitingTick(false) {}
26
27 void animationFinished(QAbstractAnimationJob *) override;
28 void animationCurrentLoopChanged(QAbstractAnimationJob *) override { maybeTick(); }
29
30 void maybeTick() {
31 Q_Q(QQmlTimer);
32 if (!awaitingTick) {
33 awaitingTick = true;
34 QCoreApplication::postEvent(receiver: q, event: new QEvent(QEvent_MaybeTick));
35 }
36 }
37
38 int interval = 1000;
39 QPauseAnimationJob pause;
40 bool running : 1;
41 bool repeating : 1;
42 bool triggeredOnStart : 1;
43 bool classBegun : 1;
44 bool componentComplete : 1;
45 bool firstTick : 1;
46 bool awaitingTick : 1;
47};
48
49/*!
50 \qmltype Timer
51 \instantiates QQmlTimer
52 \inqmlmodule QtQml
53 \ingroup qtquick-interceptors
54 \brief Triggers a handler at a specified interval.
55
56 A Timer can be used to trigger an action either once, or repeatedly
57 at a given interval.
58
59 Here is a Timer that shows the current date and time, and updates
60 the text every 500 milliseconds. It uses the JavaScript \c Date
61 object to access the current time.
62
63 \qml
64 import QtQuick 2.0
65
66 Item {
67 Timer {
68 interval: 500; running: true; repeat: true
69 onTriggered: time.text = Date().toString()
70 }
71
72 Text { id: time }
73 }
74 \endqml
75
76 The Timer type is synchronized with the animation timer. Since the animation
77 timer is usually set to 60fps, the resolution of Timer will be
78 at best 16ms.
79
80 If the Timer is running and one of its properties is changed, the
81 elapsed time will be reset. For example, if a Timer with interval of
82 1000ms has its \e repeat property changed 500ms after starting, the
83 elapsed time will be reset to 0, and the Timer will be triggered
84 1000ms later.
85
86 \sa {Qt Quick Demo - Clocks}
87*/
88
89QQmlTimer::QQmlTimer(QObject *parent)
90 : QObject(*(new QQmlTimerPrivate), parent)
91{
92 Q_D(QQmlTimer);
93 d->pause.addAnimationChangeListener(listener: d, QAbstractAnimationJob::Completion | QAbstractAnimationJob::CurrentLoop);
94 d->pause.setLoopCount(1);
95 d->pause.setDuration(d->interval);
96}
97
98/*!
99 \qmlproperty int QtQml::Timer::interval
100
101 Sets the \a interval between triggers, in milliseconds.
102
103 The default interval is 1000 milliseconds.
104*/
105void QQmlTimer::setInterval(int interval)
106{
107 Q_D(QQmlTimer);
108 if (interval != d->interval) {
109 d->interval = interval;
110 update();
111 emit intervalChanged();
112 }
113}
114
115int QQmlTimer::interval() const
116{
117 Q_D(const QQmlTimer);
118 return d->interval;
119}
120
121/*!
122 \qmlproperty bool QtQml::Timer::running
123
124 If set to true, starts the timer; otherwise stops the timer.
125 For a non-repeating timer, \a running is set to false after the
126 timer has been triggered.
127
128 \a running defaults to false.
129
130 \sa repeat
131*/
132bool QQmlTimer::isRunning() const
133{
134 Q_D(const QQmlTimer);
135 return d->running;
136}
137
138void QQmlTimer::setRunning(bool running)
139{
140 Q_D(QQmlTimer);
141 if (d->running != running) {
142 d->running = running;
143 d->firstTick = true;
144 emit runningChanged();
145 update();
146 }
147}
148
149/*!
150 \qmlproperty bool QtQml::Timer::repeat
151
152 If \a repeat is true the timer is triggered repeatedly at the
153 specified interval; otherwise, the timer will trigger once at the
154 specified interval and then stop (i.e. running will be set to false).
155
156 \a repeat defaults to false.
157
158 \sa running
159*/
160bool QQmlTimer::isRepeating() const
161{
162 Q_D(const QQmlTimer);
163 return d->repeating;
164}
165
166void QQmlTimer::setRepeating(bool repeating)
167{
168 Q_D(QQmlTimer);
169 if (repeating != d->repeating) {
170 d->repeating = repeating;
171 update();
172 emit repeatChanged();
173 }
174}
175
176/*!
177 \qmlproperty bool QtQml::Timer::triggeredOnStart
178
179 When a timer is started, the first trigger is usually after the specified
180 interval has elapsed. It is sometimes desirable to trigger immediately
181 when the timer is started; for example, to establish an initial
182 state.
183
184 If \a triggeredOnStart is true, the timer is triggered immediately
185 when started, and subsequently at the specified interval. Note that if
186 \e repeat is set to false, the timer is triggered twice; once on start,
187 and again at the interval.
188
189 \a triggeredOnStart defaults to false.
190
191 \sa running
192*/
193bool QQmlTimer::triggeredOnStart() const
194{
195 Q_D(const QQmlTimer);
196 return d->triggeredOnStart;
197}
198
199void QQmlTimer::setTriggeredOnStart(bool triggeredOnStart)
200{
201 Q_D(QQmlTimer);
202 if (d->triggeredOnStart != triggeredOnStart) {
203 d->triggeredOnStart = triggeredOnStart;
204 update();
205 emit triggeredOnStartChanged();
206 }
207}
208
209/*!
210 \qmlmethod QtQml::Timer::start()
211 \brief Starts the timer
212
213 If the timer is already running, calling this method has no effect. The
214 \c running property will be true following a call to \c start().
215*/
216void QQmlTimer::start()
217{
218 setRunning(true);
219}
220
221/*!
222 \qmlmethod QtQml::Timer::stop()
223 \brief Stops the timer
224
225 If the timer is not running, calling this method has no effect. The
226 \c running property will be false following a call to \c stop().
227*/
228void QQmlTimer::stop()
229{
230 setRunning(false);
231}
232
233/*!
234 \qmlmethod QtQml::Timer::restart()
235 \brief Restarts the timer
236
237 If the Timer is not running it will be started, otherwise it will be
238 stopped, reset to initial state and started. The \c running property
239 will be true following a call to \c restart().
240*/
241void QQmlTimer::restart()
242{
243 setRunning(false);
244 setRunning(true);
245}
246
247void QQmlTimer::update()
248{
249 Q_D(QQmlTimer);
250 if (d->classBegun && !d->componentComplete)
251 return;
252 d->pause.stop();
253 if (d->running) {
254 d->pause.setCurrentTime(0);
255 d->pause.setLoopCount(d->repeating ? -1 : 1);
256 d->pause.setDuration(d->interval);
257 d->pause.start();
258 if (d->triggeredOnStart && d->firstTick)
259 d->maybeTick();
260 }
261}
262
263void QQmlTimer::classBegin()
264{
265 Q_D(QQmlTimer);
266 d->classBegun = true;
267}
268
269void QQmlTimer::componentComplete()
270{
271 Q_D(QQmlTimer);
272 d->componentComplete = true;
273 update();
274}
275
276/*!
277 \qmlsignal QtQml::Timer::triggered()
278
279 This signal is emitted when the Timer times out.
280*/
281void QQmlTimer::ticked()
282{
283 Q_D(QQmlTimer);
284 if (d->running && (d->pause.currentTime() > 0 || (d->triggeredOnStart && d->firstTick)))
285 emit triggered();
286 d->firstTick = false;
287}
288
289/*!
290 \internal
291 */
292bool QQmlTimer::event(QEvent *e)
293{
294 Q_D(QQmlTimer);
295 if (e->type() == QEvent_MaybeTick) {
296 d->awaitingTick = false;
297 ticked();
298 return true;
299 } else if (e->type() == QEvent_Triggered) {
300 if (d->running && d->pause.isStopped()) {
301 d->running = false;
302 emit triggered();
303 emit runningChanged();
304 }
305 return true;
306 }
307 return QObject::event(event: e);
308}
309
310void QQmlTimerPrivate::animationFinished(QAbstractAnimationJob *)
311{
312 Q_Q(QQmlTimer);
313 if (repeating || !running)
314 return;
315 firstTick = false;
316 QCoreApplication::postEvent(receiver: q, event: new QEvent(QEvent_Triggered));
317}
318
319QT_END_NAMESPACE
320
321#include "moc_qqmltimer_p.cpp"
322

source code of qtdeclarative/src/qml/types/qqmltimer.cpp