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
307#if defined(QT_QML_LIB)
313struct OSSIA_EXPORT js_value_outbound_visitor
314{
315 QJSEngine& engine;
316
317 [[nodiscard]] QJSValue to_enum(qml_val_type::val_type t) const;
318
319 QJSValue operator()(impulse) const;
320
321 QJSValue operator()(int32_t val) const;
322 QJSValue operator()(float val) const;
323 QJSValue operator()(bool val) const;
324
325 QJSValue operator()(const std::string& val) const;
326
327 [[nodiscard]] QJSValue make_list(const std::vector<ossia::value>& arr) const;
328
329 QJSValue operator()(const std::vector<ossia::value>& val) const;
330 QJSValue operator()(const value_map_type& val) const;
331
332 template <std::size_t N>
333 QJSValue make_array(const std::array<float, N>& arr) const
334 {
335 auto array = engine.newArray(arr.size());
336 int i = 0;
337 for(auto child : arr)
338 {
339 array.setProperty(i++, child);
340 }
341
342 return array;
343 }
344
345 QJSValue operator()(vec2f val) const;
346 QJSValue operator()(vec3f val) const;
347 QJSValue operator()(vec4f val) const;
348
349 QJSValue operator()() const;
350};
351
359struct OSSIA_EXPORT js_string_outbound_visitor
360{
361 QString operator()(impulse) const;
362
363 QString operator()(int32_t val) const;
364
365 QString operator()(float val) const;
366 QString operator()(bool val) const;
367 QString operator()(char val) const;
368
369 QString operator()(const std::string& val) const;
370
371 QString operator()(const std::vector<ossia::value>& val) const;
372 QString operator()(const value_map_type& val) const;
373
374 template <std::size_t N>
375 QString make_array(const std::array<float, N>& arr) const
376 {
377 static_assert(N > 0, "N <= 0");
378 QString s = "[";
379
380 s += QString::number(arr[0]);
381 for(std::size_t i = 1; i < N; i++)
382 {
383 s += ", " % QString::number(arr[i]);
384 }
385 s += "]";
386 return s;
387 }
388
389 QString operator()(vec2f val) const;
390 QString operator()(vec3f val) const;
391 QString operator()(vec4f val) const;
392
393 QString operator()() const;
394};
395struct OSSIA_EXPORT js_string_unquoted_outbound_visitor : js_string_outbound_visitor
396{
397 using js_string_outbound_visitor::operator();
398 QString operator()(char val) const;
399 QString operator()(const std::string& val) const;
400};
401
402OSSIA_EXPORT ossia::value value_from_js(const QJSValue& v);
403
404inline ossia::value value_from_js(const ossia::value& cur, const QJSValue& v)
405{
406 return cur.apply(js_value_inbound_visitor{v});
407}
408
409inline QJSValue value_to_js_value(const ossia::value& cur, QJSEngine& engine)
410{
411 return cur.apply(js_value_outbound_visitor{engine});
412}
413
414inline QString value_to_js_string(const ossia::value& cur)
415{
416 return cur.apply(js_string_outbound_visitor{});
417}
418
419inline QString value_to_js_string_unquoted(const ossia::value& cur)
420{
421 return cur.apply(js_string_unquoted_outbound_visitor{});
422}
423
430template <typename T>
431std::optional<T> get_enum(const QJSValue& val)
432{
433 if(val.isNumber())
434 {
435 const int n = val.toInt();
436 if(n >= 0
437 && n < QMetaEnum::fromType<typename matching_ossia_enum<T>::type>().keyCount())
438 {
439 return static_cast<T>(n);
440 }
441 }
442 return {};
443}
444
449OSSIA_EXPORT
450ossia::net::parameter_data make_parameter_data(const QJSValue& js);
451
452void set_parameter_type(QMetaType::Type type, ossia::net::parameter_base& addr);
453
461template <typename Device_T, typename Node_T, typename Protocol_T>
463void create_device(Device_T& device, QJSValue root);
464
465template <typename Device_T, typename Node_T, typename Protocol_T>
467void create_node_rec(QJSValue js, Device_T& device, Node_T& parent);
468
469template <typename Device_T, typename Node_T, typename Protocol_T>
471void create_device(Device_T& device, QJSValue root)
472{
473 if(!root.isArray())
474 return;
475
476 QJSValueIterator it(root);
477 while(it.hasNext())
478 {
479 it.next();
480 create_node_rec<Device_T, Node_T, Protocol_T>(
481 it.value(), device, static_cast<Node_T&>(device.get_root_node()));
482 }
483}
484
485template <typename Device_T, typename Node_T, typename Protocol_T>
487void create_node_rec(QJSValue js, Device_T& device, Node_T& parent)
488{
489 auto data = Protocol_T::read_data(js);
490 if(data.name.empty())
491 return;
492
493 auto node = new Node_T{std::move(data), device, parent};
494 parent.add_child(std::unique_ptr<ossia::net::node_base>(node));
495
496 device.on_node_created(*node);
497
498 QJSValue children = js.property("children");
499 if(!children.isArray())
500 return;
501
502 QJSValueIterator it(children);
503 while(it.hasNext())
504 {
505 it.next();
506 create_node_rec<Device_T, Node_T, Protocol_T>(it.value(), device, *node);
507 }
508}
509
510template <typename Data>
512struct deferred_js_node
513{
514 Data data;
515 std::vector<deferred_js_node> children;
516};
517
518template <typename Data, typename Protocol_T>
520void create_node_deferred_rec(QJSValue js, deferred_js_node<Data>& parent)
521{
522 auto data = Protocol_T::read_data(js);
523 if(data.name.empty())
524 return;
525
526 parent.children.push_back(deferred_js_node<Data>{std::move(data), {}});
527
528 QJSValue children = js.property("children");
529 if(!children.isArray())
530 return;
531
532 deferred_js_node<Data>& node = parent.children.back();
533
534 node.children.reserve(children.property("length").toInt());
535 QJSValueIterator it(children);
536 while(it.hasNext())
537 {
538 it.next();
539 create_node_deferred_rec<Data, Protocol_T>(it.value(), node);
540 }
541}
542
543template <typename Protocol_T>
545auto create_device_nodes_deferred(QJSValue root)
546{
547 using data_type = decltype(Protocol_T::read_data(root));
548 deferred_js_node<data_type> node_root;
549
550 if(!root.isArray())
551 return node_root;
552
553 node_root.children.reserve(root.property("length").toInt());
554 QJSValueIterator it(root);
555 while(it.hasNext())
556 {
557 it.next();
558 create_node_deferred_rec<data_type, Protocol_T>(it.value(), node_root);
559 }
560
561 return node_root;
562}
563
564template <typename Device_T, typename Node_T, typename Data>
566void apply_deferred_device_rec(
567 Device_T& device, Node_T& parent_ossia, deferred_js_node<Data>& node_js)
568{
569 auto node = new Node_T{std::move(node_js.data), device, parent_ossia};
570 parent_ossia.add_child(std::unique_ptr<ossia::net::node_base>(node));
571
572 for(auto& cld : node_js.children)
573 {
574 apply_deferred_device_rec<Device_T, Node_T, Data>(device, *node, cld);
575 }
576}
577
578void device_creation_notify_recursively(ossia::net::device_base& device, auto& root_node)
579{
580 for(auto& node : root_node.children())
581 {
582 device.on_node_created(*node);
583 if(auto p = node->get_parameter())
584 device.on_parameter_created(*p);
585
586 device_creation_notify_recursively(device, *node);
587 }
588}
589
590template <typename Device_T, typename Node_T, typename Data>
592void apply_deferred_device(Device_T& device, deferred_js_node<Data>& root)
593{
594 auto& nroot = static_cast<Node_T&>(device.get_root_node());
595 for(auto& cld : root.children)
596 {
597 apply_deferred_device_rec<Device_T, Node_T, Data>(device, nroot, cld);
598 }
599
600 device_creation_notify_recursively(device, nroot);
601}
602
603template <typename Methods>
604QMetaObject::Connection connectSignalToMatchingMethod(
605 const QMetaMethod& sig, Methods& meth, QObject* source, QObject* target)
606{
607 switch(sig.parameterCount())
608 {
609 case 0: {
610 return QObject::connect(source, sig, target, meth[QMetaType::UnknownType]);
611 }
612 case 1: {
613 auto t = sig.parameterType(0);
614
615 auto method_it = meth.find((QMetaType::Type)t);
616 if(method_it != meth.end())
617 {
618 return QObject::connect(source, sig, target, method_it->second);
619 }
620 break;
621 }
622 }
623 return {};
624}
625
626template <typename Methods>
627QMetaObject::Connection connectSignalToMatchingMethod(
628 const QQmlProperty& prop, Methods& methods, QMetaMethod variantMethod,
629 QObject* source, QObject* target)
630{
631 auto meth = prop.method();
632
633 switch(meth.parameterCount())
634 {
635 case 0: {
636 return QObject::connect(source, prop.method(), target, variantMethod);
637 }
638 case 1: {
639 auto t = meth.parameterType(0);
640
641 auto method_it = methods.find((QMetaType::Type)t);
642 if(method_it != methods.end())
643 {
644 return QObject::connect(source, prop.method(), target, method_it->second);
645 }
646 break;
647 }
648 }
649 return {};
650}
651#endif
652}
653
654OSSIA_EXPORT QDebug operator<<(QDebug s, const ossia::value& v);
655
656W_REGISTER_ARGTYPE(QJSValue)
657
658Q_DECLARE_METATYPE(QJSValueList)
659W_REGISTER_ARGTYPE(QJSValueList)
660
661#else
662#error This file requires Qt.
663#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