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()(unsigned char v) { return (char)v; }
159 ossia::value operator()(signed char v) { return (char)v; }
160 ossia::value operator()(QChar v) { return v.toLatin1(); }
161 ossia::value operator()(const QString& v) { return v.toStdString(); }
162 ossia::value operator()(const QByteArray& v) { return v.toStdString(); }
163 ossia::value operator()(const QByteArrayList& v)
164 {
165 std::vector<ossia::value> tpl;
166 tpl.reserve(v.size());
167 for(auto& val : v)
168 {
169 tpl.push_back(val.toStdString());
170 }
171 return tpl;
172 }
173 ossia::value operator()(float v) { return v; }
174 ossia::value operator()(double v) { return v; }
175 ossia::value operator()(QColor v)
176 {
177 return make_vec(v.alphaF(), v.redF(), v.greenF(), v.blueF());
178 }
179 ossia::value operator()(QPoint v) { return make_vec(v.x(), v.y()); }
180 ossia::value operator()(QPointF v) { return make_vec(v.x(), v.y()); }
181 ossia::value operator()(QSize v) { return make_vec(v.width(), v.height()); }
182 ossia::value operator()(QSizeF v) { return make_vec(v.width(), v.height()); }
183 ossia::value operator()(QRect v)
184 {
185 return make_vec(v.x(), v.y(), v.width(), v.height());
186 }
187 ossia::value operator()(QRectF v)
188 {
189 return make_vec(v.x(), v.y(), v.width(), v.height());
190 }
191 ossia::value operator()(QLine v)
192 {
193 return make_vec(v.p1().x(), v.p1().y(), v.p2().x(), v.p2().y());
194 }
195 ossia::value operator()(QLineF v)
196 {
197 return make_vec(v.p1().x(), v.p1().y(), v.p2().x(), v.p2().y());
198 }
199 ossia::value operator()(QVector2D v) { return make_vec(v.x(), v.y()); }
200 ossia::value operator()(QVector3D v) { return make_vec(v.x(), v.y(), v.z()); }
201 ossia::value operator()(QVector4D v) { return make_vec(v.x(), v.y(), v.z(), v.w()); }
202 ossia::value operator()(QQuaternion v)
203 {
204 return make_vec(v.scalar(), v.x(), v.y(), v.z());
205 }
206 auto operator()(const QVariantList& v)
207 {
208 std::vector<ossia::value> tpl;
209 tpl.reserve(v.size());
210 for(auto& val : v)
211 {
212 tpl.push_back(qt_to_ossia{}(val));
213 }
214 return tpl;
215 }
216 auto operator()(const QVariantMap& v)
217 {
218 value_map_type tpl;
219 tpl.reserve(v.size());
220 for(auto it = v.cbegin(); it != v.cend(); ++it)
221 {
222 tpl.emplace_back(it.key().toStdString(), qt_to_ossia{}(it.value()));
223 }
224 return tpl;
225 }
226 auto operator()(const QVariantHash& v)
227 {
228 value_map_type tpl;
229 tpl.reserve(v.size());
230 for(auto it = v.cbegin(); it != v.cend(); ++it)
231 {
232 tpl.emplace_back(it.key().toStdString(), qt_to_ossia{}(it.value()));
233 }
234 return tpl;
235 }
236 ossia::value operator()(const QStringList& v)
237 {
238 std::vector<ossia::value> tpl;
239 tpl.reserve(v.size());
240 for(auto& val : v)
241 {
242 tpl.emplace_back(val.toStdString());
243 }
244 return tpl;
245 }
246 ossia::value operator()(const QDate& v) { return v.toString().toStdString(); }
247
248 ossia::value operator()(const QVariant& v);
249};
250
251struct ossia_to_qvariant
252{
253 QVariant operator()(QMetaType::Type type, const ossia::value& ossia_val);
254
255 QVariant operator()(impulse) const { return {}; }
256
257 QVariant operator()(int32_t val) const { return val; }
258
259 QVariant operator()(float val) const { return val; }
260 QVariant operator()(bool val) const { return val; }
261 QVariant operator()(char val) const { return val; }
262
263 QVariant operator()(const std::string& val) const
264 {
265 return QString::fromStdString(val);
266 }
267
268 template <std::size_t N>
269 [[nodiscard]] typename QArray<N>::type
270 make_array(const std::array<float, N>& arr) const
271 {
272 typename QArray<N>::type vec;
273
274 for(std::size_t i = 0U; i < N; i++)
275 vec[i] = arr[i];
276 return vec;
277 }
278
279 QVariant operator()(vec2f val) const { return make_array(val); }
280 QVariant operator()(vec3f val) const { return make_array(val); }
281 QVariant operator()(vec4f val) const { return make_array(val); }
282
283 QVariant operator()() const { return {}; }
284
285 QVariant operator()(const std::vector<ossia::value>& val) const
286 {
287 QVariantList v;
288 v.reserve(val.size());
289 for(const ossia::value& e : val)
290 {
291 v.push_back(e.apply(*this));
292 }
293 return v;
294 }
295
296 QVariant operator()(const value_map_type& val) const
297 {
298 QVariantMap v;
299 for(const auto& [k, e] : val)
300 {
301 v.insert(QString::fromStdString(k), e.apply(*this));
302 }
303 return v;
304 }
305};
306
307OSSIA_EXPORT void registerQVariantConverters();
308
309#if defined(QT_QML_LIB)
315struct OSSIA_EXPORT js_value_outbound_visitor
316{
317 QJSEngine& engine;
318
319 [[nodiscard]] QJSValue to_enum(qml_val_type::val_type t) const;
320
321 QJSValue operator()(impulse) const;
322
323 QJSValue operator()(int32_t val) const;
324 QJSValue operator()(float val) const;
325 QJSValue operator()(bool val) const;
326
327 QJSValue operator()(const std::string& val) const;
328
329 [[nodiscard]] QJSValue make_list(const std::vector<ossia::value>& arr) const;
330
331 QJSValue operator()(const std::vector<ossia::value>& val) const;
332 QJSValue operator()(const value_map_type& val) const;
333
334 template <std::size_t N>
335 QJSValue make_array(const std::array<float, N>& arr) const
336 {
337 auto array = engine.newArray(arr.size());
338 int i = 0;
339 for(auto child : arr)
340 {
341 array.setProperty(i++, child);
342 }
343
344 return array;
345 }
346
347 QJSValue operator()(vec2f val) const;
348 QJSValue operator()(vec3f val) const;
349 QJSValue operator()(vec4f val) const;
350
351 QJSValue operator()() const;
352};
353
361struct OSSIA_EXPORT js_string_outbound_visitor
362{
363 QString operator()(impulse) const;
364
365 QString operator()(int32_t val) const;
366
367 QString operator()(float val) const;
368 QString operator()(bool val) const;
369 QString operator()(char val) const;
370
371 QString operator()(const std::string& val) const;
372
373 QString operator()(const std::vector<ossia::value>& val) const;
374 QString operator()(const value_map_type& val) const;
375
376 template <std::size_t N>
377 QString make_array(const std::array<float, N>& arr) const
378 {
379 static_assert(N > 0, "N <= 0");
380 QString s = "[";
381
382 s += QString::number(arr[0]);
383 for(std::size_t i = 1; i < N; i++)
384 {
385 s += ", " % QString::number(arr[i]);
386 }
387 s += "]";
388 return s;
389 }
390
391 QString operator()(vec2f val) const;
392 QString operator()(vec3f val) const;
393 QString operator()(vec4f val) const;
394
395 QString operator()() const;
396};
397struct OSSIA_EXPORT js_string_unquoted_outbound_visitor : js_string_outbound_visitor
398{
399 using js_string_outbound_visitor::operator();
400 QString operator()(char val) const;
401 QString operator()(const std::string& val) const;
402};
403
404OSSIA_EXPORT ossia::value value_from_js(const QJSValue& v);
405
406inline ossia::value value_from_js(const ossia::value& cur, const QJSValue& v)
407{
408 return cur.apply(js_value_inbound_visitor{v});
409}
410
411inline QJSValue value_to_js_value(const ossia::value& cur, QJSEngine& engine)
412{
413 return cur.apply(js_value_outbound_visitor{engine});
414}
415
416inline QString value_to_js_string(const ossia::value& cur)
417{
418 return cur.apply(js_string_outbound_visitor{});
419}
420
421inline QString value_to_js_string_unquoted(const ossia::value& cur)
422{
423 return cur.apply(js_string_unquoted_outbound_visitor{});
424}
425
432template <typename T>
433std::optional<T> get_enum(const QJSValue& val)
434{
435 if(val.isNumber())
436 {
437 const int n = val.toInt();
438 if(n >= 0
439 && n < QMetaEnum::fromType<typename matching_ossia_enum<T>::type>().keyCount())
440 {
441 return static_cast<T>(n);
442 }
443 }
444 return {};
445}
446
451OSSIA_EXPORT
452ossia::net::parameter_data make_parameter_data(const QJSValue& js);
453
454void set_parameter_type(QMetaType::Type type, ossia::net::parameter_base& addr);
455
463template <typename Device_T, typename Node_T, typename Protocol_T>
465void create_device(Device_T& device, QJSValue root);
466
467template <typename Device_T, typename Node_T, typename Protocol_T>
469void create_node_rec(QJSValue js, Device_T& device, Node_T& parent);
470
471template <typename Device_T, typename Node_T, typename Protocol_T>
473void create_device(Device_T& device, QJSValue root)
474{
475 if(!root.isArray())
476 return;
477
478 QJSValueIterator it(root);
479 while(it.hasNext())
480 {
481 it.next();
482 create_node_rec<Device_T, Node_T, Protocol_T>(
483 it.value(), device, static_cast<Node_T&>(device.get_root_node()));
484 }
485}
486
487template <typename Device_T, typename Node_T, typename Protocol_T>
489void create_node_rec(QJSValue js, Device_T& device, Node_T& parent)
490{
491 auto data = Protocol_T::read_data(js);
492 if(data.name.empty())
493 return;
494
495 auto node = new Node_T{std::move(data), device, parent};
496 parent.add_child(std::unique_ptr<ossia::net::node_base>(node));
497
498 device.on_node_created(*node);
499
500 QJSValue children = js.property("children");
501 if(!children.isArray())
502 return;
503
504 QJSValueIterator it(children);
505 while(it.hasNext())
506 {
507 it.next();
508 create_node_rec<Device_T, Node_T, Protocol_T>(it.value(), device, *node);
509 }
510}
511
512template <typename Data>
514struct deferred_js_node
515{
516 Data data;
517 std::vector<deferred_js_node> children;
518};
519
520template <typename Data, typename Protocol_T>
522void create_node_deferred_rec(QJSValue js, deferred_js_node<Data>& parent)
523{
524 auto data = Protocol_T::read_data(js);
525 if(data.name.empty())
526 return;
527
528 parent.children.push_back(deferred_js_node<Data>{std::move(data), {}});
529
530 QJSValue children = js.property("children");
531 if(!children.isArray())
532 return;
533
534 deferred_js_node<Data>& node = parent.children.back();
535
536 node.children.reserve(children.property("length").toInt());
537 QJSValueIterator it(children);
538 while(it.hasNext())
539 {
540 it.next();
541 create_node_deferred_rec<Data, Protocol_T>(it.value(), node);
542 }
543}
544
545template <typename Protocol_T>
547auto create_device_nodes_deferred(QJSValue root)
548{
549 using data_type = decltype(Protocol_T::read_data(root));
550 deferred_js_node<data_type> node_root;
551
552 if(!root.isArray())
553 return node_root;
554
555 node_root.children.reserve(root.property("length").toInt());
556 QJSValueIterator it(root);
557 while(it.hasNext())
558 {
559 it.next();
560 create_node_deferred_rec<data_type, Protocol_T>(it.value(), node_root);
561 }
562
563 return node_root;
564}
565
566template <typename Device_T, typename Node_T, typename Data>
568void apply_deferred_device_rec(
569 Device_T& device, Node_T& parent_ossia, deferred_js_node<Data>& node_js)
570{
571 auto node = new Node_T{std::move(node_js.data), device, parent_ossia};
572 parent_ossia.add_child(std::unique_ptr<ossia::net::node_base>(node));
573
574 for(auto& cld : node_js.children)
575 {
576 apply_deferred_device_rec<Device_T, Node_T, Data>(device, *node, cld);
577 }
578}
579
580void device_creation_notify_recursively(ossia::net::device_base& device, auto& root_node)
581{
582 for(auto& node : root_node.children())
583 {
584 device.on_node_created(*node);
585 if(auto p = node->get_parameter())
586 device.on_parameter_created(*p);
587
588 device_creation_notify_recursively(device, *node);
589 }
590}
591
592template <typename Device_T, typename Node_T, typename Data>
594void apply_deferred_device(Device_T& device, deferred_js_node<Data>& root)
595{
596 auto& nroot = static_cast<Node_T&>(device.get_root_node());
597 for(auto& cld : root.children)
598 {
599 apply_deferred_device_rec<Device_T, Node_T, Data>(device, nroot, cld);
600 }
601
602 device_creation_notify_recursively(device, nroot);
603}
604
605template <typename Methods>
606QMetaObject::Connection connectSignalToMatchingMethod(
607 const QMetaMethod& sig, Methods& meth, QObject* source, QObject* target)
608{
609 switch(sig.parameterCount())
610 {
611 case 0: {
612 return QObject::connect(source, sig, target, meth[QMetaType::UnknownType]);
613 }
614 case 1: {
615 auto t = sig.parameterType(0);
616
617 auto method_it = meth.find((QMetaType::Type)t);
618 if(method_it != meth.end())
619 {
620 return QObject::connect(source, sig, target, method_it->second);
621 }
622 break;
623 }
624 }
625 return {};
626}
627
628template <typename Methods>
629QMetaObject::Connection connectSignalToMatchingMethod(
630 const QQmlProperty& prop, Methods& methods, QMetaMethod variantMethod,
631 QObject* source, QObject* target)
632{
633 auto meth = prop.method();
634
635 switch(meth.parameterCount())
636 {
637 case 0: {
638 return QObject::connect(source, prop.method(), target, variantMethod);
639 }
640 case 1: {
641 auto t = meth.parameterType(0);
642
643 auto method_it = methods.find((QMetaType::Type)t);
644 if(method_it != methods.end())
645 {
646 return QObject::connect(source, prop.method(), target, method_it->second);
647 }
648 break;
649 }
650 }
651 return {};
652}
653#endif
654}
655
656OSSIA_EXPORT QDebug operator<<(QDebug s, const ossia::value& v);
657
658W_REGISTER_ARGTYPE(QJSValue)
659
660Q_DECLARE_METATYPE(QJSValueList)
661W_REGISTER_ARGTYPE(QJSValueList)
662
663#else
664#error This file requires Qt.
665#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 qml_device.cpp:43
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