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 | |
48 | QT_BEGIN_NAMESPACE |
49 | |
50 | namespace { |
51 | const QEvent::Type QEvent_MaybeTick = QEvent::Type(QEvent::User + 1); |
52 | const QEvent::Type QEvent_Triggered = QEvent::Type(QEvent::User + 2); |
53 | } |
54 | |
55 | class QQmlTimerPrivate : public QObjectPrivate, public QAnimationJobChangeListener |
56 | { |
57 | Q_DECLARE_PUBLIC(QQmlTimer) |
58 | public: |
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 | |
125 | QQmlTimer::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 | */ |
141 | void 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 | |
151 | int 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 | */ |
168 | bool QQmlTimer::isRunning() const |
169 | { |
170 | Q_D(const QQmlTimer); |
171 | return d->running; |
172 | } |
173 | |
174 | void 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 | */ |
196 | bool QQmlTimer::isRepeating() const |
197 | { |
198 | Q_D(const QQmlTimer); |
199 | return d->repeating; |
200 | } |
201 | |
202 | void 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 | */ |
229 | bool QQmlTimer::triggeredOnStart() const |
230 | { |
231 | Q_D(const QQmlTimer); |
232 | return d->triggeredOnStart; |
233 | } |
234 | |
235 | void 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 | */ |
252 | void 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 | */ |
264 | void 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 | */ |
277 | void QQmlTimer::restart() |
278 | { |
279 | setRunning(false); |
280 | setRunning(true); |
281 | } |
282 | |
283 | void 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 | |
299 | void QQmlTimer::classBegin() |
300 | { |
301 | Q_D(QQmlTimer); |
302 | d->classBegun = true; |
303 | } |
304 | |
305 | void 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 | */ |
317 | void 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 | */ |
328 | bool 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 | |
346 | void 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 | |
355 | QT_END_NAMESPACE |
356 | |
357 | #include "moc_qqmltimer_p.cpp" |
358 | |