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

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