Loading...
Searching...
No Matches
QmlObjects.hpp
1#pragma once
2#include <State/Domain.hpp>
3
4#include <Process/Dataflow/Port.hpp>
5#include <Process/Dataflow/WidgetInlets.hpp>
6
7#include <JS/Qml/QtMetatypes.hpp>
8
9#include <libremidi/detail/conversion.hpp>
10
11#if defined(SCORE_HAS_GPU_JS)
12#include <Gfx/TexturePort.hpp>
13
14#include <QQuickItem>
15#endif
16
17#include <score/tools/Debug.hpp>
18
19#include <ossia/detail/math.hpp>
20#include <ossia/detail/ssize.hpp>
21#include <ossia/network/domain/domain.hpp>
22
23#include <ossia-qt/js_utilities.hpp>
24
25#include <QJSValue>
26#include <QObject>
27#include <QQmlListProperty>
28#include <QVariant>
29#include <QVector>
30
31#include <libremidi/message.hpp>
32#include <libremidi/ump.hpp>
33
34#include <score_plugin_js_export.h>
35#include <wobjectimpl.h>
36
37#include <verdigris>
38
39class QQuickItem;
40namespace JS
41{
42class SCORE_PLUGIN_JS_EXPORT Inlet : public QObject
43{
44 W_OBJECT(Inlet)
45
46public:
47 using QObject::QObject;
48 virtual ~Inlet() override;
49 virtual Process::Inlet* make(Id<Process::Port>&& id, QObject*) = 0;
50 virtual bool isEvent() const { return false; }
51
52 W_INLINE_PROPERTY_CREF(QString, address, {}, address, setAddress, addressChanged)
53};
54
55class SCORE_PLUGIN_JS_EXPORT Outlet : public QObject
56{
57 W_OBJECT(Outlet)
58
59public:
60 using QObject::QObject;
61 virtual ~Outlet() override;
62 virtual Process::Outlet* make(Id<Process::Port>&& id, QObject*) = 0;
63
64 W_INLINE_PROPERTY_CREF(QString, address, {}, address, setAddress, addressChanged)
65};
66
67struct SCORE_PLUGIN_JS_EXPORT InValueMessage
68{
69 W_GADGET(InValueMessage)
70
71public:
72 qreal timestamp;
73 QVariant value;
74 W_PROPERTY(qreal, timestamp MEMBER timestamp)
75 W_PROPERTY(QVariant, value MEMBER value)
76};
77
78struct SCORE_PLUGIN_JS_EXPORT OutValueMessage
79{
80 W_GADGET(OutValueMessage)
81
82public:
83 qreal timestamp;
84 QJSValue value;
85 W_PROPERTY(qreal, timestamp MEMBER timestamp)
86 W_PROPERTY(QJSValue, value MEMBER value)
87};
88
89class SCORE_PLUGIN_JS_EXPORT ValueInlet : public Inlet
90{
91 W_OBJECT(ValueInlet)
92
93 QVariant m_value;
94 QVariantList m_values;
95
96public:
97 explicit ValueInlet(QObject* parent = nullptr);
98 virtual ~ValueInlet() override;
99 QVariant value() const;
100 QVariantList values() const { return m_values; }
101
102 Process::Inlet* make(Id<Process::Port>&& id, QObject* parent) override
103 {
104 return new Process::ValueInlet(id, parent);
105 }
106
107 int length() const noexcept;
108 W_SLOT(length)
109
110 QVariant at(int index) const noexcept;
111 W_SLOT(at)
112
113 void clear() { m_values.clear(); }
114 void setValue(QVariant value);
115 void addValue(QVariant&& val) { m_values.append(std::move(val)); }
116 void valueChanged(QVariant value) W_SIGNAL(valueChanged, value);
117
118 W_PROPERTY(QVariantList, values READ values)
119 W_PROPERTY(QVariant, value READ value NOTIFY valueChanged)
120};
121
122class SCORE_PLUGIN_JS_EXPORT ControlInlet : public Inlet
123{
124 W_OBJECT(ControlInlet)
125
126 QVariant m_value;
127
128public:
129 explicit ControlInlet(QObject* parent = nullptr);
130 virtual ~ControlInlet() override;
131 QVariant value() const noexcept;
132
133 Process::Inlet* make(Id<Process::Port>&& id, QObject* parent) override
134 {
135 return new Process::ControlInlet(id, parent);
136 }
137
138 void clear() { m_value = QVariant{}; }
139 virtual void setValue(QVariant value);
140 void valueChanged(QVariant value) W_SIGNAL(valueChanged, value);
141
142 W_PROPERTY(QVariant, value READ value NOTIFY valueChanged)
143};
144
145template <typename Impl, typename ValueType>
146class SCORE_PLUGIN_JS_EXPORT GenericControlInlet : public ControlInlet
147{
148 W_OBJECT(GenericControlInlet)
149
150public:
151 using ControlInlet::ControlInlet;
152 virtual ~GenericControlInlet() override = default;
153 bool isEvent() const override { return true; }
154 Process::Inlet* make(Id<Process::Port>&& id, QObject* parent) override
155 {
156 return new Impl(id, parent);
157 }
158
159 void clear() { m_value = {}; }
160 ValueType value() const noexcept { return m_value; }
161 void setValue(QVariant value) override
162 {
163 auto conv = value.value<ValueType>();
164 if(m_value == conv)
165 return;
166
167 m_value = std::move(conv);
168 valueChanged(m_value);
169 }
170 void setValue(ValueType value)
171 {
172 if(m_value == value)
173 return;
174
175 m_value = value;
176 valueChanged(m_value);
177 }
178 void valueChanged(ValueType value) W_SIGNAL(valueChanged, value);
179
180 W_PROPERTY(ValueType, value READ value NOTIFY valueChanged)
181
182private:
183 ValueType m_value{};
184};
185W_OBJECT_IMPL((GenericControlInlet<A, B>), template <typename A, typename B>)
186struct SCORE_PLUGIN_JS_EXPORT FloatRangeSpinBox
188{
189 W_OBJECT(FloatRangeSpinBox);
190 using GenericControlInlet::GenericControlInlet;
191};
192struct SCORE_PLUGIN_JS_EXPORT IntRangeSlider
193 : JS::GenericControlInlet<Process::IntRangeSlider, QVector2D>
194{
195 W_OBJECT(IntRangeSlider);
196 using GenericControlInlet::GenericControlInlet;
197};
198struct SCORE_PLUGIN_JS_EXPORT IntRangeSpinBox
199 : JS::GenericControlInlet<Process::IntRangeSpinBox, QVector2D>
200{
201 W_OBJECT(IntRangeSpinBox);
202 using GenericControlInlet::GenericControlInlet;
203};
204struct SCORE_PLUGIN_JS_EXPORT HSVSlider
205 : JS::GenericControlInlet<Process::HSVSlider, QVector4D>
206{
207 W_OBJECT(HSVSlider);
208 using GenericControlInlet::GenericControlInlet;
209};
210struct SCORE_PLUGIN_JS_EXPORT XYSlider
211 : JS::GenericControlInlet<Process::XYSlider, QVector2D>
212{
213 W_OBJECT(XYSlider);
214 using GenericControlInlet::GenericControlInlet;
215};
216struct SCORE_PLUGIN_JS_EXPORT XYZSlider
217 : JS::GenericControlInlet<Process::XYZSlider, QVector3D>
218{
219 W_OBJECT(XYZSlider);
220 using GenericControlInlet::GenericControlInlet;
221};
222struct SCORE_PLUGIN_JS_EXPORT XYSpinboxes
223 : JS::GenericControlInlet<Process::XYSpinboxes, QVector2D>
224{
225 W_OBJECT(XYSpinboxes);
226 using GenericControlInlet::GenericControlInlet;
227};
228struct SCORE_PLUGIN_JS_EXPORT XYZSpinboxes
229 : JS::GenericControlInlet<Process::XYZSpinboxes, QVector3D>
230{
231 W_OBJECT(XYZSpinboxes);
232 using GenericControlInlet::GenericControlInlet;
233};
234struct SCORE_PLUGIN_JS_EXPORT MultiSlider
235 : JS::GenericControlInlet<Process::MultiSlider, QVector<qreal>>
236{
237 W_OBJECT(MultiSlider);
238 using GenericControlInlet::GenericControlInlet;
239};
240struct SCORE_PLUGIN_JS_EXPORT FileChooser
241 : JS::GenericControlInlet<Process::FileChooser, QString>
242{
243 W_OBJECT(FileChooser);
244 using GenericControlInlet::GenericControlInlet;
245};
246struct SCORE_PLUGIN_JS_EXPORT AudioFileChooser
247 : JS::GenericControlInlet<Process::AudioFileChooser, QString>
248{
249 W_OBJECT(AudioFileChooser);
250 using GenericControlInlet::GenericControlInlet;
251};
252struct SCORE_PLUGIN_JS_EXPORT VideoFileChooser
253 : JS::GenericControlInlet<Process::VideoFileChooser, QString>
254{
255 W_OBJECT(VideoFileChooser);
256 using GenericControlInlet::GenericControlInlet;
257};
258
259template <typename Impl = Process::FloatSlider>
260class SCORE_PLUGIN_JS_EXPORT FloatSlider : public GenericControlInlet<Impl, float>
261{
262 W_OBJECT(FloatSlider)
263
264public:
265 using GenericControlInlet<Impl, float>::GenericControlInlet;
266 virtual ~FloatSlider() override = default;
267 bool isEvent() const override { return true; }
268
269 Process::Inlet* make(Id<Process::Port>&& id, QObject* parent) override
270 {
271 return new Impl{(float)m_min, (float)m_max, (float)m_init,
272 this->objectName(), id, parent};
273 }
274
275 W_INLINE_PROPERTY_VALUE(qreal, init, {0.5}, init, setInit, initChanged)
276 W_INLINE_PROPERTY_VALUE(qreal, min, {0.}, getMin, setMin, minChanged)
277 W_INLINE_PROPERTY_VALUE(qreal, max, {1.}, getMax, setMax, maxChanged)
278};
279W_OBJECT_IMPL(JS::FloatSlider<Impl>, template <typename Impl>)
280
281template <typename Impl = Process::IntSlider>
282class SCORE_PLUGIN_JS_EXPORT IntSlider : public GenericControlInlet<Impl, int>
283{
284 W_OBJECT(IntSlider)
285
286public:
287 using GenericControlInlet<Impl, int>::GenericControlInlet;
288 virtual ~IntSlider() override = default;
289 bool isEvent() const override { return true; }
290
291 Process::Inlet* make(Id<Process::Port>&& id, QObject* parent) override
292 {
293 return new Impl{m_min, m_max, m_init, this->objectName(), id, parent};
294 }
295
296 W_INLINE_PROPERTY_VALUE(int, init, {0}, init, setInit, initChanged)
297 W_INLINE_PROPERTY_VALUE(int, min, {0}, getMin, setMin, minChanged)
298 W_INLINE_PROPERTY_VALUE(int, max, {127}, getMax, setMax, maxChanged)
299};
300W_OBJECT_IMPL(JS::IntSlider<Impl>, template <typename Impl>)
301
302class SCORE_PLUGIN_JS_EXPORT Enum : public ControlInlet
303{
304 W_OBJECT(Enum)
305
306public:
307 using ControlInlet::ControlInlet;
308 virtual ~Enum() override;
309 bool isEvent() const override { return true; }
310
311 Process::Inlet* make(Id<Process::Port>&& id, QObject* parent) override
312 {
313 return new Process::Enum{m_choices, {}, current(), objectName(), id, parent};
314 }
315
316 auto getValues() const { return choices(); }
317
318 std::string current() const
319 {
320 if(!m_choices.isEmpty() && ossia::valid_index(m_index, m_choices))
321 {
322 return m_choices[m_index].toStdString();
323 }
324 return {};
325 }
326
327 W_INLINE_PROPERTY_VALUE(int, index, {}, index, setIndex, indexChanged)
328 W_INLINE_PROPERTY_CREF(QStringList, choices, {}, choices, setChoices, choicesChanged)
329};
330
331class SCORE_PLUGIN_JS_EXPORT Toggle : public ControlInlet
332{
333 W_OBJECT(Toggle)
334
335public:
336 using ControlInlet::ControlInlet;
337 virtual ~Toggle() override;
338 bool isEvent() const override { return true; }
339 Process::Inlet* make(Id<Process::Port>&& id, QObject* parent) override
340 {
341 return new Process::Toggle{m_checked, objectName(), id, parent};
342 }
343
344 W_INLINE_PROPERTY_VALUE(bool, checked, {}, checked, setChecked, checkedChanged)
345};
346
347class SCORE_PLUGIN_JS_EXPORT Button : public ControlInlet
348{
349 W_OBJECT(Button)
350
351public:
352 using ControlInlet::ControlInlet;
353 virtual ~Button() override;
354 bool isEvent() const override { return true; }
355 Process::Inlet* make(Id<Process::Port>&& id, QObject* parent) override
356 {
357 return new Process::Button{objectName(), id, parent};
358 }
359
360 W_INLINE_PROPERTY_VALUE(bool, checked, {}, checked, setChecked, checkedChanged)
361};
362
363class SCORE_PLUGIN_JS_EXPORT Impulse : public ControlInlet
364{
365 W_OBJECT(Impulse)
366
367public:
368 using ControlInlet::ControlInlet;
369 virtual ~Impulse() override;
370 bool isEvent() const override { return false; }
371 Process::Inlet* make(Id<Process::Port>&& id, QObject* parent) override
372 {
373 return new Process::ImpulseButton{objectName(), id, parent};
374 }
375
376 void impulse() W_SIGNAL(impulse);
377};
378
379class SCORE_PLUGIN_JS_EXPORT LineEdit : public ControlInlet
380{
381 W_OBJECT(LineEdit)
382
383public:
384 using ControlInlet::ControlInlet;
385 virtual ~LineEdit() override;
386 bool isEvent() const override { return true; }
387 Process::Inlet* make(Id<Process::Port>&& id, QObject* parent) override
388 {
389 return new Process::LineEdit{m_text, objectName(), id, parent};
390 }
391
392 W_INLINE_PROPERTY_CREF(QString, text, {}, text, setText, textChanged)
393};
394
395class SCORE_PLUGIN_JS_EXPORT ValueOutlet : public Outlet
396{
397 W_OBJECT(ValueOutlet)
398
399 QJSValue m_value;
400
401public:
402 std::vector<OutValueMessage> values;
403
404 explicit ValueOutlet(QObject* parent = nullptr);
405 virtual ~ValueOutlet() override;
406 const QJSValue& value() const;
407 void clear()
408 {
409 m_value = QJSValue{};
410 values.clear();
411 }
412 Process::Outlet* make(Id<Process::Port>&& id, QObject* parent) override
413 {
414 return new Process::ValueOutlet(id, parent);
415 }
416
417public:
418 void setValue(const QJSValue& value);
419 W_SLOT(setValue);
420 void addValue(qreal timestamp, QJSValue t);
421 W_SLOT(addValue);
422
423 W_PROPERTY(QJSValue, value READ value WRITE setValue)
424};
425
426class SCORE_PLUGIN_JS_EXPORT AudioInlet : public Inlet
427{
428 W_OBJECT(AudioInlet)
429
430public:
431 explicit AudioInlet(QObject* parent = nullptr);
432 virtual ~AudioInlet() override;
433 const QVector<QVector<double>>& audio() const;
434 void setAudio(const QVector<QVector<double>>& audio);
435
436 QVector<double> channel(int i) const
437 {
438 if(m_audio.size() > i)
439 return m_audio[i];
440 return {};
441 }
442 W_INVOKABLE(channel);
443
444 Process::Inlet* make(Id<Process::Port>&& id, QObject* parent) override
445 {
446 return new Process::AudioInlet(id, parent);
447 }
448
449private:
450 QVector<QVector<double>> m_audio;
451};
452
453class SCORE_PLUGIN_JS_EXPORT AudioOutlet : public Outlet
454{
455 W_OBJECT(AudioOutlet)
456
457public:
458 explicit AudioOutlet(QObject* parent = nullptr);
459 virtual ~AudioOutlet() override;
460 Process::Outlet* make(Id<Process::Port>&& id, QObject* parent) override
461 {
462 auto p = new Process::AudioOutlet(id, parent);
463 if(id.val() == 0)
464 p->setPropagate(true);
465 return p;
466 }
467
468 const QVector<QVector<double>>& audio() const;
469
470 void setChannel(int i, const QJSValue& v);
471 W_INVOKABLE(setChannel)
472private:
473 QVector<QVector<double>> m_audio;
474};
475
476class SCORE_PLUGIN_JS_EXPORT MidiMessage
477{
478 W_GADGET(MidiMessage)
479
480public:
481 QByteArray bytes;
482
483 W_PROPERTY(QByteArray, bytes MEMBER bytes)
484};
485
486class SCORE_PLUGIN_JS_EXPORT MidiInlet : public Inlet
487{
488 W_OBJECT(MidiInlet)
489
490public:
491 explicit MidiInlet(QObject* parent = nullptr);
492 virtual ~MidiInlet() override;
493 template <typename T>
494 void setMidi(const T& arr)
495 {
496 m_midi.clear();
497 for(const libremidi::ump& mess : arr)
498 {
499 auto m1 = libremidi::midi1_from_ump(mess);
500 const auto N = mess.size();
501 QVector<int> m;
502 m.resize(N);
503
504 for(std::size_t i = 0; i < N; i++)
505 m[i] = m1.bytes[i];
506
507 m_midi.push_back(QVariant::fromValue(m));
508 }
509 }
510
511 QVariantList messages() const { return m_midi; }
512 W_INVOKABLE(messages);
513
514 Process::Inlet* make(Id<Process::Port>&& id, QObject* parent) override
515 {
516 return new Process::MidiInlet(id, parent);
517 }
518
519private:
520 QVariantList m_midi;
521};
522
523class SCORE_PLUGIN_JS_EXPORT MidiOutlet : public Outlet
524{
525 W_OBJECT(MidiOutlet)
526
527public:
528 explicit MidiOutlet(QObject* parent = nullptr);
529 virtual ~MidiOutlet() override;
530 Process::Outlet* make(Id<Process::Port>&& id, QObject* parent) override
531 {
532 return new Process::MidiOutlet(id, parent);
533 }
534
535 void clear();
536 const QVector<QVector<int>>& midi() const;
537
538 void setMessages(const QVariantList m)
539 {
540 m_midi.clear();
541 for(auto& v : m)
542 {
543 if(v.canConvert<QVector<int>>())
544 m_midi.push_back(v.value<QVector<int>>());
545 }
546 }
547 W_INVOKABLE(setMessages);
548
549 void add(QVector<int> m) { m_midi.push_back(std::move(m)); }
550 W_INVOKABLE(add);
551
552private:
553 QVector<QVector<int>> m_midi;
554};
555
556#if defined(SCORE_HAS_GPU_JS)
557class TextureOutlet : public Outlet
558{
559 W_OBJECT(TextureOutlet)
560
561public:
562 explicit TextureOutlet(QObject* parent = nullptr);
563 virtual ~TextureOutlet() override;
564 Process::Outlet* make(Id<Process::Port>&& id, QObject* parent) override
565 {
566 auto p = new Gfx::TextureOutlet(id, parent);
567 return p;
568 }
569
570 QQuickItem* item() /*Qt6: const*/ noexcept { return m_item; }
571 void setItem(QQuickItem* v) { m_item = v; }
572
573 W_PROPERTY(QQuickItem*, item READ item WRITE setItem CONSTANT)
574private:
575 QQuickItem* m_item{};
576};
577#endif
578
579class Script : public QObject
580{
581 W_OBJECT(Script)
582 W_CLASSINFO("DefaultProperty", "data")
583 W_CLASSINFO(
584 "qt_QmlJSWrapperFactoryMethod", "_q_createJSWrapper(QV4::ExecutionEngine*)")
585
586public:
587 QQmlListProperty<QObject> data() noexcept { return {this, &m_data}; }
588
589 QJSValue& tick() /*Qt6: const*/ noexcept { return m_tick; }
590 void setTick(const QJSValue& v) { m_tick = v; }
591 QJSValue& start() /*Qt6: const*/ noexcept { return m_start; }
592 void setStart(const QJSValue& v) { m_start = v; }
593 QJSValue& stop() /*Qt6: const*/ noexcept { return m_stop; }
594 void setStop(const QJSValue& v) { m_stop = v; }
595 QJSValue& pause() /*Qt6: const*/ noexcept { return m_pause; }
596 void setPause(const QJSValue& v) { m_pause = v; }
597 QJSValue& resume() /*Qt6: const*/ noexcept { return m_resume; }
598 void setResume(const QJSValue& v) { m_resume = v; }
599 W_PROPERTY(QJSValue, tick READ tick WRITE setTick CONSTANT)
600 W_PROPERTY(QJSValue, start READ start WRITE setStart CONSTANT)
601 W_PROPERTY(QJSValue, stop READ stop WRITE setStop CONSTANT)
602 W_PROPERTY(QJSValue, pause READ pause WRITE setPause CONSTANT)
603 W_PROPERTY(QJSValue, resume READ resume WRITE setResume CONSTANT)
604 W_PROPERTY(QQmlListProperty<QObject>, data READ data)
605
606private:
607 QList<QObject*> m_data;
608 QJSValue m_tick;
609 QJSValue m_start;
610 QJSValue m_stop;
611 QJSValue m_pause;
612 QJSValue m_resume;
613};
614}
615
616inline QDataStream& operator<<(QDataStream& i, const JS::MidiMessage& sel)
617{
618 SCORE_ABORT;
619 return i;
620}
621inline QDataStream& operator>>(QDataStream& i, JS::MidiMessage& sel)
622{
623 SCORE_ABORT;
624 return i;
625}
626inline QDataStream& operator<<(QDataStream& i, const JS::InValueMessage& sel)
627{
628 SCORE_ABORT;
629 return i;
630}
631inline QDataStream& operator>>(QDataStream& i, JS::InValueMessage& sel)
632{
633 SCORE_ABORT;
634 return i;
635}
636inline QDataStream& operator<<(QDataStream& i, const JS::OutValueMessage& sel)
637{
638 SCORE_ABORT;
639 return i;
640}
641inline QDataStream& operator>>(QDataStream& i, JS::OutValueMessage& sel)
642{
643 SCORE_ABORT;
644 return i;
645}
646Q_DECLARE_METATYPE(JS::ValueInlet*)
647Q_DECLARE_METATYPE(JS::ValueOutlet*)
648Q_DECLARE_METATYPE(JS::AudioInlet*)
649Q_DECLARE_METATYPE(JS::AudioOutlet*)
650Q_DECLARE_METATYPE(JS::MidiMessage)
651Q_DECLARE_METATYPE(JS::MidiInlet*)
652Q_DECLARE_METATYPE(JS::MidiOutlet*)
653
654W_REGISTER_ARGTYPE(JS::ValueInlet*)
655W_REGISTER_ARGTYPE(JS::ValueOutlet*)
656W_REGISTER_ARGTYPE(JS::AudioInlet*)
657W_REGISTER_ARGTYPE(JS::AudioOutlet*)
658W_REGISTER_ARGTYPE(JS::MidiMessage)
659W_REGISTER_ARGTYPE(JS::MidiInlet*)
660W_REGISTER_ARGTYPE(JS::MidiOutlet*)
Definition QmlObjects.hpp:427
Definition QmlObjects.hpp:454
Definition QmlObjects.hpp:348
Definition QmlObjects.hpp:123
Definition QmlObjects.hpp:303
Definition QmlObjects.hpp:261
Definition QmlObjects.hpp:147
Definition QmlObjects.hpp:364
Definition QmlObjects.hpp:43
Definition QmlObjects.hpp:283
Definition QmlObjects.hpp:380
Definition QmlObjects.hpp:487
Definition QmlObjects.hpp:477
Definition QmlObjects.hpp:524
Definition QmlObjects.hpp:56
Definition QmlObjects.hpp:580
Definition QmlObjects.hpp:332
Definition QmlObjects.hpp:90
Definition QmlObjects.hpp:396
Definition Port.hpp:300
Definition Port.hpp:323
Definition Port.hpp:203
Definition Port.hpp:177
Definition Port.hpp:379
Definition Port.hpp:402
Definition Port.hpp:273
Definition Port.hpp:492
Definition Port.hpp:515
The id_base_t class.
Definition Identifier.hpp:57
Base classes and tools to implement processes and layers.
Definition JSONVisitor.hpp:1116
Definition QmlObjects.hpp:248
Definition QmlObjects.hpp:242
Definition QmlObjects.hpp:188
Definition QmlObjects.hpp:206
Definition QmlObjects.hpp:68
Definition QmlObjects.hpp:194
Definition QmlObjects.hpp:200
Definition QmlObjects.hpp:236
Definition QmlObjects.hpp:79
Definition QmlObjects.hpp:254
Definition QmlObjects.hpp:212
Definition QmlObjects.hpp:224
Definition QmlObjects.hpp:218
Definition QmlObjects.hpp:230