OSSIA
Open Scenario System for Interactive Application
Loading...
Searching...
No Matches
js_utilities.hpp
1#pragma once
2#if defined(QT_CORE_LIB)
3#include <ossia/detail/optional.hpp>
4#include <ossia/network/base/node.hpp>
5#include <ossia/network/base/parameter_data.hpp>
6#include <ossia/network/common/parameter_properties.hpp>
8#include <ossia/network/generic/generic_node.hpp>
9#include <ossia/network/value/value.hpp>
10#include <ossia/preset/preset.hpp>
11
12#include <ossia-qt/metatypes.hpp>
13#include <ossia-qt/name_utils.hpp>
14
15#include <QDebug>
16#include <QHash>
17#include <QLineF>
18#include <QMetaEnum>
19#include <QPoint>
20#include <QRectF>
21#include <QString>
22#include <QStringBuilder>
23#include <QTime>
24#include <QVariant>
25#include <QVariantList>
26#include <QtGui/QColor>
27#include <QtGui/QQuaternion>
28#include <QtGui/QVector2D>
29#include <QtGui/QVector3D>
30#include <QtGui/QVector4D>
31
32#if defined(QT_QML_LIB)
33#include <ossia-qt/qml_context.hpp>
34
35#include <QJSEngine>
36#include <QJSValue>
37#include <QJSValueIterator>
38#include <QQmlExtensionPlugin>
39#include <QQmlProperty>
40#endif
41namespace ossia::qt
42{
43
44template <std::size_t N>
45struct QArray
46{
47};
48
49template <>
50struct QArray<2>
51{
52 using type = QVector2D;
53};
54template <>
55struct QArray<3>
56{
57 using type = QVector3D;
58};
59template <>
60struct QArray<4>
61{
62 using type = QVector4D;
63};
64
65#if defined(QT_QML_LIB)
71template <typename T>
72struct matching_ossia_enum
73{
74};
75template <>
76struct matching_ossia_enum<ossia::val_type>
77{
78 using type = qml_val_type::val_type;
79};
80template <>
81struct matching_ossia_enum<ossia::access_mode>
82{
83 using type = qml_access_mode::access_mode;
84};
85template <>
86struct matching_ossia_enum<ossia::bounding_mode>
87{
88 using type = qml_bounding_mode::bounding_mode;
89};
90template <>
91struct matching_ossia_enum<ossia::repetition_filter>
92{
93 using type = qml_rep_filter::repetition_filter;
94};
95
101struct OSSIA_EXPORT js_value_inbound_visitor
102{
103 const QJSValue& val;
104
105public:
106 ossia::value operator()(impulse) const;
107
108 ossia::value operator()(int32_t v) const;
109 ossia::value operator()(float v) const;
110 ossia::value operator()(bool v) const;
111 ossia::value operator()(char v) const;
112
113 ossia::value operator()(const std::string& v) const;
114 ossia::value operator()(const std::vector<ossia::value>& v) const;
115 ossia::value operator()(const value_map_type& v) const;
116
117 ossia::value operator()(vec2f v) const;
118 ossia::value operator()(vec3f v) const;
119 ossia::value operator()(vec4f v) const;
120
121 ossia::value operator()() const;
122};
123#endif
124
125struct OSSIA_EXPORT variant_inbound_visitor
126{
127 const QVariant& val;
128
129public:
130 ossia::value operator()(impulse) const;
131
132 ossia::value operator()(int32_t v) const;
133 ossia::value operator()(float v) const;
134 ossia::value operator()(bool v) const;
135 ossia::value operator()(char v) const;
136
137 ossia::value operator()(const std::string& v) const;
138 ossia::value operator()(const std::vector<ossia::value>& v) const;
139 ossia::value operator()(const value_map_type& v) const;
140
141 ossia::value operator()(vec2f v) const;
142 ossia::value operator()(vec3f v) const;
143 ossia::value operator()(vec4f v) const;
144
145 ossia::value operator()() const;
146};
147
148struct OSSIA_EXPORT qt_to_ossia
149{
150 ossia::value operator()() { return ossia::impulse{}; }
151 ossia::value operator()(bool v) { return v; }
152 ossia::value operator()(QTime v) { return v.msec(); }
153 ossia::value operator()(qint32 v) { return v; }
154 ossia::value operator()(quint32 v) { return (int)v; }
155 ossia::value operator()(qint64 v) { return (int)v; }
156 ossia::value operator()(quint64 v) { return (int)v; }
157 ossia::value operator()(char v) { return v; }
158 ossia::value operator()(QChar v) { return v.toLatin1(); }
159 ossia::value operator()(const QString& v) { return v.toStdString(); }
160 ossia::value operator()(const QByteArray& v) { return v.toStdString(); }
161 ossia::value operator()(double v) { return v; }
162 ossia::value operator()(QColor v)
163 {
164 return make_vec(v.alphaF(), v.redF(), v.greenF(), v.blueF());
165 }
166 ossia::value operator()(QPoint v) { return make_vec(v.x(), v.y()); }
167 ossia::value operator()(QPointF v) { return make_vec(v.x(), v.y()); }
168 ossia::value operator()(QSize v) { return make_vec(v.width(), v.height()); }
169 ossia::value operator()(QSizeF v) { return make_vec(v.width(), v.height()); }
170 ossia::value operator()(QRect v)
171 {
172 return make_vec(v.x(), v.y(), v.width(), v.height());
173 }
174 ossia::value operator()(QRectF v)
175 {
176 return make_vec(v.x(), v.y(), v.width(), v.height());
177 }
178 ossia::value operator()(QLine v)
179 {
180 return make_vec(v.p1().x(), v.p1().y(), v.p2().x(), v.p2().y());
181 }
182 ossia::value operator()(QLineF v)
183 {
184 return make_vec(v.p1().x(), v.p1().y(), v.p2().x(), v.p2().y());
185 }
186 ossia::value operator()(QVector2D v) { return make_vec(v.x(), v.y()); }
187 ossia::value operator()(QVector3D v) { return make_vec(v.x(), v.y(), v.z()); }
188 ossia::value operator()(QVector4D v) { return make_vec(v.x(), v.y(), v.z(), v.w()); }
189 ossia::value operator()(QQuaternion v)
190 {
191 return make_vec(v.scalar(), v.x(), v.y(), v.z());
192 }
193 auto operator()(const QVariantList& v)
194 {
195 std::vector<ossia::value> tpl;
196 tpl.reserve(v.size());
197 for(auto& val : v)
198 {
199 tpl.push_back(qt_to_ossia{}(val));
200 }
201 return tpl;
202 }
203 auto operator()(const QVariantMap& v)
204 {
205 value_map_type tpl;
206 tpl.reserve(v.size());
207 for(auto it = v.cbegin(); it != v.cend(); ++it)
208 {
209 tpl.emplace_back(it.key().toStdString(), qt_to_ossia{}(it.value()));
210 }
211 return tpl;
212 }
213 ossia::value operator()(const QStringList& v)
214 {
215 std::vector<ossia::value> tpl;
216 tpl.reserve(v.size());
217 for(auto& val : v)
218 {
219 tpl.emplace_back(val.toStdString());
220 }
221 return tpl;
222 }
223 ossia::value operator()(const QDate& v) { return v.toString().toStdString(); }
224
225 ossia::value operator()(const QVariant& v);
226};
227
228struct ossia_to_qvariant
229{
230 QVariant operator()(QMetaType::Type type, const ossia::value& ossia_val);
231
232 QVariant operator()(impulse) const { return {}; }
233
234 QVariant operator()(int32_t val) const { return val; }
235
236 QVariant operator()(float val) const { return val; }
237 QVariant operator()(bool val) const { return val; }
238 QVariant operator()(char val) const { return val; }
239
240 QVariant operator()(const std::string& val) const
241 {
242 return QString::fromStdString(val);
243 }
244
245 template <std::size_t N>
246 [[nodiscard]] typename QArray<N>::type
247 make_array(const std::array<float, N>& arr) const
248 {
249 typename QArray<N>::type vec;
250
251 for(std::size_t i = 0U; i < N; i++)
252 vec[i] = arr[i];
253 return vec;
254 }
255
256 QVariant operator()(vec2f val) const { return make_array(val); }
257 QVariant operator()(vec3f val) const { return make_array(val); }
258 QVariant operator()(vec4f val) const { return make_array(val); }
259
260 QVariant operator()() const { return {}; }
261
262 QVariant operator()(const std::vector<ossia::value>& val) const
263 {
264 QVariantList v;
265 v.reserve(val.size());
266 for(const ossia::value& e : val)
267 {
268 v.push_back(e.apply(*this));
269 }
270 return v;
271 }
272
273 QVariant operator()(const value_map_type& val) const
274 {
275 QVariantMap v;
276 for(const auto& [k, e] : val)
277 {
278 v.insert(QString::fromStdString(k), e.apply(*this));
279 }
280 return v;
281 }
282};
283
284#if defined(QT_QML_LIB)
290struct OSSIA_EXPORT js_value_outbound_visitor
291{
292 QJSEngine& engine;
293
294 [[nodiscard]] QJSValue to_enum(qml_val_type::val_type t) const;
295
296 QJSValue operator()(impulse) const;
297
298 QJSValue operator()(int32_t val) const;
299 QJSValue operator()(float val) const;
300 QJSValue operator()(bool val) const;
301
302 QJSValue operator()(const std::string& val) const;
303
304 [[nodiscard]] QJSValue make_list(const std::vector<ossia::value>& arr) const;
305
306 QJSValue operator()(const std::vector<ossia::value>& val) const;
307 QJSValue operator()(const value_map_type& val) const;
308
309 template <std::size_t N>
310 QJSValue make_array(const std::array<float, N>& arr) const
311 {
312 auto array = engine.newArray(arr.size());
313 int i = 0;
314 for(auto child : arr)
315 {
316 array.setProperty(i++, child);
317 }
318
319 return array;
320 }
321
322 QJSValue operator()(vec2f val) const;
323 QJSValue operator()(vec3f val) const;
324 QJSValue operator()(vec4f val) const;
325
326 QJSValue operator()() const;
327};
328
336struct OSSIA_EXPORT js_string_outbound_visitor
337{
338 QString operator()(impulse) const;
339
340 QString operator()(int32_t val) const;
341
342 QString operator()(float val) const;
343 QString operator()(bool val) const;
344 QString operator()(char val) const;
345
346 QString operator()(const std::string& val) const;
347
348 QString operator()(const std::vector<ossia::value>& val) const;
349 QString operator()(const value_map_type& val) const;
350
351 template <std::size_t N>
352 QString make_array(const std::array<float, N>& arr) const
353 {
354 static_assert(N > 0, "N <= 0");
355 QString s = "[";
356
357 s += QString::number(arr[0]);
358 for(std::size_t i = 1; i < N; i++)
359 {
360 s += ", " % QString::number(arr[i]);
361 }
362 s += "]";
363 return s;
364 }
365
366 QString operator()(vec2f val) const;
367 QString operator()(vec3f val) const;
368 QString operator()(vec4f val) const;
369
370 QString operator()() const;
371};
372struct OSSIA_EXPORT js_string_unquoted_outbound_visitor : js_string_outbound_visitor
373{
374 using js_string_outbound_visitor::operator();
375 QString operator()(char val) const;
376 QString operator()(const std::string& val) const;
377};
378
379OSSIA_EXPORT ossia::value value_from_js(const QJSValue& v);
380
381inline ossia::value value_from_js(const ossia::value& cur, const QJSValue& v)
382{
383 return cur.apply(js_value_inbound_visitor{v});
384}
385
386inline QJSValue value_to_js_value(const ossia::value& cur, QJSEngine& engine)
387{
388 return cur.apply(js_value_outbound_visitor{engine});
389}
390
391inline QString value_to_js_string(const ossia::value& cur)
392{
393 return cur.apply(js_string_outbound_visitor{});
394}
395
396inline QString value_to_js_string_unquoted(const ossia::value& cur)
397{
398 return cur.apply(js_string_unquoted_outbound_visitor{});
399}
400
407template <typename T>
408std::optional<T> get_enum(const QJSValue& val)
409{
410 if(val.isNumber())
411 {
412 const int n = val.toInt();
413 if(n >= 0
414 && n < QMetaEnum::fromType<typename matching_ossia_enum<T>::type>().keyCount())
415 {
416 return static_cast<T>(n);
417 }
418 }
419 return {};
420}
421
426OSSIA_EXPORT
427ossia::net::parameter_data make_parameter_data(const QJSValue& js);
428
429void set_parameter_type(QMetaType::Type type, ossia::net::parameter_base& addr);
430
438template <typename Device_T, typename Node_T, typename Protocol_T>
440void create_device(Device_T& device, QJSValue root);
441
442template <typename Device_T, typename Node_T, typename Protocol_T>
444void create_node_rec(QJSValue js, Device_T& device, Node_T& parent);
445
446template <typename Device_T, typename Node_T, typename Protocol_T>
448void create_device(Device_T& device, QJSValue root)
449{
450 if(!root.isArray())
451 return;
452
453 QJSValueIterator it(root);
454 while(it.hasNext())
455 {
456 it.next();
457 create_node_rec<Device_T, Node_T, Protocol_T>(
458 it.value(), device, static_cast<Node_T&>(device.get_root_node()));
459 }
460}
461
462template <typename Device_T, typename Node_T, typename Protocol_T>
464void create_node_rec(QJSValue js, Device_T& device, Node_T& parent)
465{
466 auto data = Protocol_T::read_data(js);
467 if(data.name.empty())
468 return;
469
470 auto node = new Node_T{std::move(data), device, parent};
471 parent.add_child(std::unique_ptr<ossia::net::node_base>(node));
472
473 device.on_node_created(*node);
474
475 QJSValue children = js.property("children");
476 if(!children.isArray())
477 return;
478
479 QJSValueIterator it(children);
480 while(it.hasNext())
481 {
482 it.next();
483 create_node_rec<Device_T, Node_T, Protocol_T>(it.value(), device, *node);
484 }
485}
486
487template <typename Data>
489struct deferred_js_node
490{
491 Data data;
492 std::vector<deferred_js_node> children;
493};
494
495template <typename Data, typename Protocol_T>
497void create_node_deferred_rec(QJSValue js, deferred_js_node<Data>& parent)
498{
499 auto data = Protocol_T::read_data(js);
500 if(data.name.empty())
501 return;
502
503 parent.children.push_back(deferred_js_node<Data>{std::move(data), {}});
504
505 QJSValue children = js.property("children");
506 if(!children.isArray())
507 return;
508
509 deferred_js_node<Data>& node = parent.children.back();
510
511 node.children.reserve(children.property("length").toInt());
512 QJSValueIterator it(children);
513 while(it.hasNext())
514 {
515 it.next();
516 create_node_deferred_rec<Data, Protocol_T>(it.value(), node);
517 }
518}
519
520template <typename Protocol_T>
522auto create_device_nodes_deferred(QJSValue root)
523{
524 using data_type = decltype(Protocol_T::read_data(root));
525 deferred_js_node<data_type> node_root;
526
527 if(!root.isArray())
528 return node_root;
529
530 node_root.children.reserve(root.property("length").toInt());
531 QJSValueIterator it(root);
532 while(it.hasNext())
533 {
534 it.next();
535 create_node_deferred_rec<data_type, Protocol_T>(it.value(), node_root);
536 }
537
538 return node_root;
539}
540
541template <typename Device_T, typename Node_T, typename Data>
543void apply_deferred_device_rec(
544 Device_T& device, Node_T& parent_ossia, deferred_js_node<Data>& node_js)
545{
546 auto node = new Node_T{std::move(node_js.data), device, parent_ossia};
547 parent_ossia.add_child(std::unique_ptr<ossia::net::node_base>(node));
548
549 for(auto& cld : node_js.children)
550 {
551 apply_deferred_device_rec<Device_T, Node_T, Data>(device, *node, cld);
552 }
553}
554
555void device_creation_notify_recursively(ossia::net::device_base& device, auto& root_node)
556{
557 for(auto& node : root_node.children())
558 {
559 device.on_node_created(*node);
560 if(auto p = node->get_parameter())
561 device.on_parameter_created(*p);
562
563 device_creation_notify_recursively(device, *node);
564 }
565}
566
567template <typename Device_T, typename Node_T, typename Data>
569void apply_deferred_device(Device_T& device, deferred_js_node<Data>& root)
570{
571 auto& nroot = static_cast<Node_T&>(device.get_root_node());
572 for(auto& cld : root.children)
573 {
574 apply_deferred_device_rec<Device_T, Node_T, Data>(device, nroot, cld);
575 }
576
577 device_creation_notify_recursively(device, nroot);
578}
579
580template <typename Methods>
581QMetaObject::Connection connectSignalToMatchingMethod(
582 const QMetaMethod& sig, Methods& meth, QObject* source, QObject* target)
583{
584 switch(sig.parameterCount())
585 {
586 case 0: {
587 return QObject::connect(source, sig, target, meth[QMetaType::UnknownType]);
588 }
589 case 1: {
590 auto t = sig.parameterType(0);
591
592 auto method_it = meth.find((QMetaType::Type)t);
593 if(method_it != meth.end())
594 {
595 return QObject::connect(source, sig, target, method_it->second);
596 }
597 break;
598 }
599 }
600 return {};
601}
602
603template <typename Methods>
604QMetaObject::Connection connectSignalToMatchingMethod(
605 const QQmlProperty& prop, Methods& methods, QMetaMethod variantMethod,
606 QObject* source, QObject* target)
607{
608 auto meth = prop.method();
609
610 switch(meth.parameterCount())
611 {
612 case 0: {
613 return QObject::connect(source, prop.method(), target, variantMethod);
614 }
615 case 1: {
616 auto t = meth.parameterType(0);
617
618 auto method_it = methods.find((QMetaType::Type)t);
619 if(method_it != methods.end())
620 {
621 return QObject::connect(source, prop.method(), target, method_it->second);
622 }
623 break;
624 }
625 }
626 return {};
627}
628#endif
629}
630
631OSSIA_EXPORT QDebug operator<<(QDebug s, const ossia::value& v);
632
633W_REGISTER_ARGTYPE(QJSValue)
634
635Q_DECLARE_METATYPE(QJSValueList)
636W_REGISTER_ARGTYPE(QJSValueList)
637
638#else
639#error This file requires Qt.
640#endif
Root of a device tree.
Definition ossia/network/base/device.hpp:58
The parameter_base class.
Definition ossia/network/base/parameter.hpp:48
The value class.
Definition value.hpp:173
Definition git_info.h:7
val_type
Enum to represent the types that a value can take.
Definition parameter_properties.hpp:16
repetition_filter
If enabled, sending twice the same value will only send it once by network.
Definition parameter_properties.hpp:70
bounding_mode
Address behaviors at crossing domain boundaries.
Definition parameter_properties.hpp:56
access_mode
Address behaviors at crossing domain boundaries time.
Definition parameter_properties.hpp:46
The data that can be found inside a parameter.
Definition parameter_data.hpp:21