OSSIA
Open Scenario System for Interactive Application
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
41 namespace ossia::qt
42 {
43 
44 template <std::size_t N>
45 struct QArray
46 {
47 };
48 
49 template <>
50 struct QArray<2>
51 {
52  using type = QVector2D;
53 };
54 template <>
55 struct QArray<3>
56 {
57  using type = QVector3D;
58 };
59 template <>
60 struct QArray<4>
61 {
62  using type = QVector4D;
63 };
64 
65 #if defined(QT_QML_LIB)
71 template <typename T>
72 struct matching_ossia_enum
73 {
74 };
75 template <>
76 struct matching_ossia_enum<ossia::val_type>
77 {
78  using type = qml_val_type::val_type;
79 };
80 template <>
81 struct matching_ossia_enum<ossia::access_mode>
82 {
83  using type = qml_access_mode::access_mode;
84 };
85 template <>
86 struct matching_ossia_enum<ossia::bounding_mode>
87 {
89 };
90 template <>
91 struct matching_ossia_enum<ossia::repetition_filter>
92 {
93  using type = qml_rep_filter::repetition_filter;
94 };
95 
101 struct OSSIA_EXPORT js_value_inbound_visitor
102 {
103  const QJSValue& val;
104 
105 public:
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 
125 struct OSSIA_EXPORT variant_inbound_visitor
126 {
127  const QVariant& val;
128 
129 public:
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 
148 struct 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 
228 struct 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)
290 struct 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 
336 struct 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 };
372 struct 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 
379 OSSIA_EXPORT ossia::value value_from_js(const QJSValue& v);
380 
381 inline ossia::value value_from_js(const ossia::value& cur, const QJSValue& v)
382 {
383  return cur.apply(js_value_inbound_visitor{v});
384 }
385 
386 inline QJSValue value_to_js_value(const ossia::value& cur, QJSEngine& engine)
387 {
388  return cur.apply(js_value_outbound_visitor{engine});
389 }
390 
391 inline QString value_to_js_string(const ossia::value& cur)
392 {
393  return cur.apply(js_string_outbound_visitor{});
394 }
395 
396 inline QString value_to_js_string_unquoted(const ossia::value& cur)
397 {
398  return cur.apply(js_string_unquoted_outbound_visitor{});
399 }
400 
407 template <typename T>
408 std::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 
426 OSSIA_EXPORT
427 ossia::net::parameter_data make_parameter_data(const QJSValue& js);
428 
429 void set_parameter_type(QMetaType::Type type, ossia::net::parameter_base& addr);
430 
438 template <typename Device_T, typename Node_T, typename Protocol_T>
440 void create_device(Device_T& device, QJSValue root);
441 
442 template <typename Device_T, typename Node_T, typename Protocol_T>
444 void create_node_rec(QJSValue js, Device_T& device, Node_T& parent);
445 
446 template <typename Device_T, typename Node_T, typename Protocol_T>
448 void 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 
462 template <typename Device_T, typename Node_T, typename Protocol_T>
464 void 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 
487 template <typename Data>
489 struct deferred_js_node
490 {
491  Data data;
492  std::vector<deferred_js_node> children;
493 };
494 
495 template <typename Data, typename Protocol_T>
497 void 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 
520 template <typename Protocol_T>
522 auto 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 
541 template <typename Device_T, typename Node_T, typename Data>
543 void 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 
555 void 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 
567 template <typename Device_T, typename Node_T, typename Data>
569 void 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 
580 template <typename Methods>
581 QMetaObject::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 
603 template <typename Methods>
604 QMetaObject::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 
631 OSSIA_EXPORT QDebug operator<<(QDebug s, const ossia::value& v);
632 
633 #else
634 #error This file requires Qt.
635 #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
access_mode
Defines Write (Set), Read (Get) or Read/Write (Bi) access to the parameter's value.
Definition: ossia-cpp98.hpp:66
bounding_mode
Behaviour at the bounds of the value.
Definition: ossia-cpp98.hpp:75
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