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