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 "qqmlvaluetype_p.h"
41
42#include <QtCore/qmutex.h>
43#include <private/qqmlglobal_p.h>
44#include <QtCore/qdebug.h>
45#include <private/qmetaobjectbuilder_p.h>
46#if QT_CONFIG(qml_itemmodel)
47#include <private/qqmlmodelindexvaluetype_p.h>
48#endif
49#include <private/qmetatype_p.h>
50
51QT_BEGIN_NAMESPACE
52
53namespace {
54
55struct QQmlValueTypeFactoryImpl
56{
57 QQmlValueTypeFactoryImpl();
58 ~QQmlValueTypeFactoryImpl();
59
60 bool isValueType(int idx);
61
62 const QMetaObject *metaObjectForMetaType(int);
63 QQmlValueType *valueType(int);
64
65 QQmlValueType *valueTypes[QVariant::UserType];
66 QHash<int, QQmlValueType *> userTypes;
67 QMutex mutex;
68};
69
70QQmlValueTypeFactoryImpl::QQmlValueTypeFactoryImpl()
71{
72 std::fill_n(valueTypes, int(QVariant::UserType), nullptr);
73
74#if QT_CONFIG(qml_itemmodel)
75 // See types wrapped in qqmlmodelindexvaluetype_p.h
76 qRegisterMetaType<QItemSelectionRange>();
77#endif
78}
79
80QQmlValueTypeFactoryImpl::~QQmlValueTypeFactoryImpl()
81{
82 qDeleteAll(valueTypes, valueTypes + QVariant::UserType);
83 qDeleteAll(userTypes);
84}
85
86bool QQmlValueTypeFactoryImpl::isValueType(int idx)
87{
88 if (idx >= QMetaType::User)
89 return valueType(idx) != nullptr;
90
91 if (idx < 0)
92 return false;
93
94 // Qt internal types
95 switch (idx) {
96 case QMetaType::QStringList:
97 case QMetaType::QObjectStar:
98 case QMetaType::VoidStar:
99 case QMetaType::Nullptr:
100 case QMetaType::QVariant:
101 case QMetaType::QLocale:
102 case QMetaType::QImage: // scarce type, keep as QVariant
103 case QMetaType::QPixmap: // scarce type, keep as QVariant
104 return false;
105 default:
106 return true;
107 }
108}
109
110const QMetaObject *QQmlValueTypeFactoryImpl::metaObjectForMetaType(int t)
111{
112 switch (t) {
113 case QVariant::Point:
114 return &QQmlPointValueType::staticMetaObject;
115 case QVariant::PointF:
116 return &QQmlPointFValueType::staticMetaObject;
117 case QVariant::Size:
118 return &QQmlSizeValueType::staticMetaObject;
119 case QVariant::SizeF:
120 return &QQmlSizeFValueType::staticMetaObject;
121 case QVariant::Rect:
122 return &QQmlRectValueType::staticMetaObject;
123 case QVariant::RectF:
124 return &QQmlRectFValueType::staticMetaObject;
125 case QVariant::EasingCurve:
126 return &QQmlEasingValueType::staticMetaObject;
127#if QT_CONFIG(qml_itemmodel)
128 case QVariant::ModelIndex:
129 return &QQmlModelIndexValueType::staticMetaObject;
130 case QVariant::PersistentModelIndex:
131 return &QQmlPersistentModelIndexValueType::staticMetaObject;
132#endif
133 default:
134#if QT_CONFIG(qml_itemmodel)
135 if (t == qMetaTypeId<QItemSelectionRange>())
136 return &QQmlItemSelectionRangeValueType::staticMetaObject;
137#endif
138
139 if (const QMetaObject *mo = QQml_valueTypeProvider()->metaObjectForMetaType(t))
140 return mo;
141 break;
142 }
143
144 QMetaType metaType(t);
145 if (metaType.flags() & QMetaType::IsGadget)
146 return metaType.metaObject();
147 return nullptr;
148}
149
150QQmlValueType *QQmlValueTypeFactoryImpl::valueType(int idx)
151{
152 if (idx >= (int)QVariant::UserType) {
153 // Protect the hash with a mutex
154 mutex.lock();
155
156 QHash<int, QQmlValueType *>::iterator it = userTypes.find(idx);
157 if (it == userTypes.end()) {
158 QQmlValueType *vt = nullptr;
159 if (const QMetaObject *mo = metaObjectForMetaType(idx))
160 vt = new QQmlValueType(idx, mo);
161 it = userTypes.insert(idx, vt);
162 }
163
164 mutex.unlock();
165 return *it;
166 }
167
168 QQmlValueType *rv = valueTypes[idx];
169 if (!rv) {
170 // No need for mutex protection - the most we can lose is a valueType instance
171
172 // TODO: Investigate the performance/memory characteristics of
173 // removing the preallocated array
174 if (const QMetaObject *mo = metaObjectForMetaType(idx)) {
175 rv = new QQmlValueType(idx, mo);
176 valueTypes[idx] = rv;
177 }
178 }
179
180 return rv;
181}
182
183}
184
185Q_GLOBAL_STATIC(QQmlValueTypeFactoryImpl, factoryImpl);
186
187bool QQmlValueTypeFactory::isValueType(int idx)
188{
189 return factoryImpl()->isValueType(idx);
190}
191
192QQmlValueType *QQmlValueTypeFactory::valueType(int idx)
193{
194 return factoryImpl()->valueType(idx);
195}
196
197const QMetaObject *QQmlValueTypeFactory::metaObjectForMetaType(int type)
198{
199 return factoryImpl()->metaObjectForMetaType(type);
200}
201
202void QQmlValueTypeFactory::registerValueTypes(const char *uri, int versionMajor, int versionMinor)
203{
204 qmlRegisterValueTypeEnums<QQmlEasingValueType>(uri, versionMajor, versionMinor, "Easing");
205}
206
207QQmlValueType::QQmlValueType(int typeId, const QMetaObject *gadgetMetaObject)
208 : gadgetPtr(QMetaType::create(typeId))
209 , metaType(typeId)
210{
211 QObjectPrivate *op = QObjectPrivate::get(this);
212 Q_ASSERT(!op->metaObject);
213 op->metaObject = this;
214
215 QMetaObjectBuilder builder(gadgetMetaObject);
216 _metaObject = builder.toMetaObject();
217
218 *static_cast<QMetaObject*>(this) = *_metaObject;
219}
220
221QQmlValueType::~QQmlValueType()
222{
223 QObjectPrivate *op = QObjectPrivate::get(this);
224 Q_ASSERT(op->metaObject == this);
225 op->metaObject = nullptr;
226 ::free(const_cast<QMetaObject *>(_metaObject));
227 metaType.destroy(gadgetPtr);
228}
229
230void QQmlValueType::read(QObject *obj, int idx)
231{
232 void *a[] = { gadgetPtr, nullptr };
233 QMetaObject::metacall(obj, QMetaObject::ReadProperty, idx, a);
234}
235
236void QQmlValueType::write(QObject *obj, int idx, QQmlPropertyData::WriteFlags flags)
237{
238 Q_ASSERT(gadgetPtr);
239 int status = -1;
240 void *a[] = { gadgetPtr, nullptr, &status, &flags };
241 QMetaObject::metacall(obj, QMetaObject::WriteProperty, idx, a);
242}
243
244QVariant QQmlValueType::value()
245{
246 Q_ASSERT(gadgetPtr);
247 return QVariant(metaType.id(), gadgetPtr);
248}
249
250void QQmlValueType::setValue(const QVariant &value)
251{
252 Q_ASSERT(metaType.id() == value.userType());
253 metaType.destruct(gadgetPtr);
254 metaType.construct(gadgetPtr, value.constData());
255}
256
257QAbstractDynamicMetaObject *QQmlValueType::toDynamicMetaObject(QObject *)
258{
259 return this;
260}
261
262void QQmlValueType::objectDestroyed(QObject *)
263{
264}
265
266int QQmlValueType::metaCall(QObject *, QMetaObject::Call type, int _id, void **argv)
267{
268 const QMetaObject *mo = _metaObject;
269 QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(type, &mo, &_id);
270 mo->d.static_metacall(reinterpret_cast<QObject*>(gadgetPtr), type, _id, argv);
271 return _id;
272}
273
274QString QQmlPointFValueType::toString() const
275{
276 return QString::asprintf("QPointF(%g, %g)", v.x(), v.y());
277}
278
279qreal QQmlPointFValueType::x() const
280{
281 return v.x();
282}
283
284qreal QQmlPointFValueType::y() const
285{
286 return v.y();
287}
288
289void QQmlPointFValueType::setX(qreal x)
290{
291 v.setX(x);
292}
293
294void QQmlPointFValueType::setY(qreal y)
295{
296 v.setY(y);
297}
298
299
300int QQmlPointValueType::x() const
301{
302 return v.x();
303}
304
305int QQmlPointValueType::y() const
306{
307 return v.y();
308}
309
310void QQmlPointValueType::setX(int x)
311{
312 v.setX(x);
313}
314
315void QQmlPointValueType::setY(int y)
316{
317 v.setY(y);
318}
319
320
321QString QQmlSizeFValueType::toString() const
322{
323 return QString::asprintf("QSizeF(%g, %g)", v.width(), v.height());
324}
325
326qreal QQmlSizeFValueType::width() const
327{
328 return v.width();
329}
330
331qreal QQmlSizeFValueType::height() const
332{
333 return v.height();
334}
335
336void QQmlSizeFValueType::setWidth(qreal w)
337{
338 v.setWidth(w);
339}
340
341void QQmlSizeFValueType::setHeight(qreal h)
342{
343 v.setHeight(h);
344}
345
346
347int QQmlSizeValueType::width() const
348{
349 return v.width();
350}
351
352int QQmlSizeValueType::height() const
353{
354 return v.height();
355}
356
357void QQmlSizeValueType::setWidth(int w)
358{
359 v.setWidth(w);
360}
361
362void QQmlSizeValueType::setHeight(int h)
363{
364 v.setHeight(h);
365}
366
367QString QQmlRectFValueType::toString() const
368{
369 return QString::asprintf("QRectF(%g, %g, %g, %g)", v.x(), v.y(), v.width(), v.height());
370}
371
372qreal QQmlRectFValueType::x() const
373{
374 return v.x();
375}
376
377qreal QQmlRectFValueType::y() const
378{
379 return v.y();
380}
381
382void QQmlRectFValueType::setX(qreal x)
383{
384 v.moveLeft(x);
385}
386
387void QQmlRectFValueType::setY(qreal y)
388{
389 v.moveTop(y);
390}
391
392qreal QQmlRectFValueType::width() const
393{
394 return v.width();
395}
396
397qreal QQmlRectFValueType::height() const
398{
399 return v.height();
400}
401
402void QQmlRectFValueType::setWidth(qreal w)
403{
404 v.setWidth(w);
405}
406
407void QQmlRectFValueType::setHeight(qreal h)
408{
409 v.setHeight(h);
410}
411
412qreal QQmlRectFValueType::left() const
413{
414 return v.left();
415}
416
417qreal QQmlRectFValueType::right() const
418{
419 return v.right();
420}
421
422qreal QQmlRectFValueType::top() const
423{
424 return v.top();
425}
426
427qreal QQmlRectFValueType::bottom() const
428{
429 return v.bottom();
430}
431
432int QQmlRectValueType::x() const
433{
434 return v.x();
435}
436
437int QQmlRectValueType::y() const
438{
439 return v.y();
440}
441
442void QQmlRectValueType::setX(int x)
443{
444 v.moveLeft(x);
445}
446
447void QQmlRectValueType::setY(int y)
448{
449 v.moveTop(y);
450}
451
452int QQmlRectValueType::width() const
453{
454 return v.width();
455}
456
457int QQmlRectValueType::height() const
458{
459 return v.height();
460}
461
462void QQmlRectValueType::setWidth(int w)
463{
464 v.setWidth(w);
465}
466
467void QQmlRectValueType::setHeight(int h)
468{
469 v.setHeight(h);
470}
471
472int QQmlRectValueType::left() const
473{
474 return v.left();
475}
476
477int QQmlRectValueType::right() const
478{
479 return v.right();
480}
481
482int QQmlRectValueType::top() const
483{
484 return v.top();
485}
486
487int QQmlRectValueType::bottom() const
488{
489 return v.bottom();
490}
491
492QQmlEasingValueType::Type QQmlEasingValueType::type() const
493{
494 return (QQmlEasingValueType::Type)v.type();
495}
496
497qreal QQmlEasingValueType::amplitude() const
498{
499 return v.amplitude();
500}
501
502qreal QQmlEasingValueType::overshoot() const
503{
504 return v.overshoot();
505}
506
507qreal QQmlEasingValueType::period() const
508{
509 return v.period();
510}
511
512void QQmlEasingValueType::setType(QQmlEasingValueType::Type type)
513{
514 v.setType((QEasingCurve::Type)type);
515}
516
517void QQmlEasingValueType::setAmplitude(qreal amplitude)
518{
519 v.setAmplitude(amplitude);
520}
521
522void QQmlEasingValueType::setOvershoot(qreal overshoot)
523{
524 v.setOvershoot(overshoot);
525}
526
527void QQmlEasingValueType::setPeriod(qreal period)
528{
529 v.setPeriod(period);
530}
531
532void QQmlEasingValueType::setBezierCurve(const QVariantList &customCurveVariant)
533{
534 if (customCurveVariant.isEmpty())
535 return;
536
537 if ((customCurveVariant.count() % 6) != 0)
538 return;
539
540 auto convert = [](const QVariant &v, qreal &r) {
541 bool ok;
542 r = v.toReal(&ok);
543 return ok;
544 };
545
546 QEasingCurve newEasingCurve(QEasingCurve::BezierSpline);
547 for (int i = 0, ei = customCurveVariant.size(); i < ei; i += 6) {
548 qreal c1x, c1y, c2x, c2y, c3x, c3y;
549 if (!convert(customCurveVariant.at(i ), c1x)) return;
550 if (!convert(customCurveVariant.at(i + 1), c1y)) return;
551 if (!convert(customCurveVariant.at(i + 2), c2x)) return;
552 if (!convert(customCurveVariant.at(i + 3), c2y)) return;
553 if (!convert(customCurveVariant.at(i + 4), c3x)) return;
554 if (!convert(customCurveVariant.at(i + 5), c3y)) return;
555
556 const QPointF c1(c1x, c1y);
557 const QPointF c2(c2x, c2y);
558 const QPointF c3(c3x, c3y);
559
560 newEasingCurve.addCubicBezierSegment(c1, c2, c3);
561 }
562
563 v = newEasingCurve;
564}
565
566QVariantList QQmlEasingValueType::bezierCurve() const
567{
568 QVariantList rv;
569 const QVector<QPointF> points = v.toCubicSpline();
570 rv.reserve(points.size() * 2);
571 for (const auto &point : points)
572 rv << QVariant(point.x()) << QVariant(point.y());
573 return rv;
574}
575
576QT_END_NAMESPACE
577
578#include "moc_qqmlvaluetype_p.cpp"
579