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

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