qml 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (170) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +46 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +15 -0
  5. data/.yardopts +4 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +351 -0
  9. data/Rakefile +6 -0
  10. data/examples/assets/fonts/fontawesome-webfont.ttf +0 -0
  11. data/examples/fizzbuzz/fizzbuzz.rb +43 -0
  12. data/examples/fizzbuzz/main.qml +38 -0
  13. data/examples/imageprovider/imageprovider.rb +57 -0
  14. data/examples/imageprovider/main.qml +51 -0
  15. data/examples/todo/main.qml +70 -0
  16. data/examples/todo/todo.rb +36 -0
  17. data/examples/twitter/main.qml +36 -0
  18. data/examples/twitter/twitter.rb +55 -0
  19. data/ext/qml/accessclass.cpp +71 -0
  20. data/ext/qml/accessclass.h +19 -0
  21. data/ext/qml/accessobject.cpp +30 -0
  22. data/ext/qml/accessobject.h +22 -0
  23. data/ext/qml/application.cpp +54 -0
  24. data/ext/qml/application.h +17 -0
  25. data/ext/qml/common.h +18 -0
  26. data/ext/qml/ext_accesssupport.cpp +77 -0
  27. data/ext/qml/ext_accesssupport.h +42 -0
  28. data/ext/qml/ext_gcmarker.cpp +39 -0
  29. data/ext/qml/ext_gcmarker.h +27 -0
  30. data/ext/qml/ext_kernel.cpp +62 -0
  31. data/ext/qml/ext_kernel.h +11 -0
  32. data/ext/qml/ext_metaobject.cpp +410 -0
  33. data/ext/qml/ext_metaobject.h +62 -0
  34. data/ext/qml/ext_pluginloader.cpp +55 -0
  35. data/ext/qml/ext_pluginloader.h +32 -0
  36. data/ext/qml/ext_pointer.cpp +134 -0
  37. data/ext/qml/ext_pointer.h +43 -0
  38. data/ext/qml/ext_testutil.cpp +42 -0
  39. data/ext/qml/ext_testutil.h +11 -0
  40. data/ext/qml/extconf.rb +84 -0
  41. data/ext/qml/foreignclass.cpp +72 -0
  42. data/ext/qml/foreignclass.h +88 -0
  43. data/ext/qml/foreignmetaobject.cpp +345 -0
  44. data/ext/qml/foreignmetaobject.h +46 -0
  45. data/ext/qml/foreignobject.cpp +22 -0
  46. data/ext/qml/foreignobject.h +21 -0
  47. data/ext/qml/functioninfo.h +16 -0
  48. data/ext/qml/init.cpp +69 -0
  49. data/ext/qml/listmodel.cpp +112 -0
  50. data/ext/qml/listmodel.h +43 -0
  51. data/ext/qml/markable.h +12 -0
  52. data/ext/qml/objectdata.cpp +26 -0
  53. data/ext/qml/objectdata.h +20 -0
  54. data/ext/qml/objectgc.cpp +69 -0
  55. data/ext/qml/objectgc.h +28 -0
  56. data/ext/qml/plugins/core/applicationextension.cpp +34 -0
  57. data/ext/qml/plugins/core/applicationextension.h +28 -0
  58. data/ext/qml/plugins/core/componentextension.cpp +41 -0
  59. data/ext/qml/plugins/core/componentextension.h +28 -0
  60. data/ext/qml/plugins/core/contextextension.cpp +39 -0
  61. data/ext/qml/plugins/core/contextextension.h +29 -0
  62. data/ext/qml/plugins/core/core.pro +29 -0
  63. data/ext/qml/plugins/core/coreplugin.cpp +87 -0
  64. data/ext/qml/plugins/core/coreplugin.h +49 -0
  65. data/ext/qml/plugins/core/engineextension.cpp +27 -0
  66. data/ext/qml/plugins/core/engineextension.h +28 -0
  67. data/ext/qml/plugins/core/imageprovider.cpp +38 -0
  68. data/ext/qml/plugins/core/imageprovider.h +18 -0
  69. data/ext/qml/plugins/core/imagerequestpromise.cpp +19 -0
  70. data/ext/qml/plugins/core/imagerequestpromise.h +21 -0
  71. data/ext/qml/plugins/core/qmlexception.cpp +11 -0
  72. data/ext/qml/plugins/core/qmlexception.h +17 -0
  73. data/ext/qml/plugins/testutil/objectlifechecker.cpp +17 -0
  74. data/ext/qml/plugins/testutil/objectlifechecker.h +24 -0
  75. data/ext/qml/plugins/testutil/ownershiptest.cpp +26 -0
  76. data/ext/qml/plugins/testutil/ownershiptest.h +30 -0
  77. data/ext/qml/plugins/testutil/testobject.cpp +6 -0
  78. data/ext/qml/plugins/testutil/testobject.h +108 -0
  79. data/ext/qml/plugins/testutil/testobjectsubclass.cpp +10 -0
  80. data/ext/qml/plugins/testutil/testobjectsubclass.h +19 -0
  81. data/ext/qml/plugins/testutil/testutil.pro +20 -0
  82. data/ext/qml/plugins/testutil/testutilplugin.cpp +47 -0
  83. data/ext/qml/plugins/testutil/testutilplugin.h +32 -0
  84. data/ext/qml/qmltyperegisterer.cpp +74 -0
  85. data/ext/qml/qmltyperegisterer.h +30 -0
  86. data/ext/qml/rubyclass.cpp +94 -0
  87. data/ext/qml/rubyclass.h +234 -0
  88. data/ext/qml/rubyvalue.cpp +690 -0
  89. data/ext/qml/rubyvalue.h +256 -0
  90. data/ext/qml/signalforwarder.cpp +66 -0
  91. data/ext/qml/signalforwarder.h +29 -0
  92. data/ext/qml/util.cpp +120 -0
  93. data/ext/qml/util.h +101 -0
  94. data/ext/qml/valuereference.cpp +50 -0
  95. data/ext/qml/valuereference.h +22 -0
  96. data/ext/qml/weakvaluereference.cpp +27 -0
  97. data/ext/qml/weakvaluereference.h +19 -0
  98. data/lib/qml.rb +41 -0
  99. data/lib/qml/access.rb +137 -0
  100. data/lib/qml/application.rb +139 -0
  101. data/lib/qml/class_builder.rb +126 -0
  102. data/lib/qml/component.rb +53 -0
  103. data/lib/qml/context.rb +71 -0
  104. data/lib/qml/data.rb +2 -0
  105. data/lib/qml/data/array_model.rb +103 -0
  106. data/lib/qml/data/error.rb +5 -0
  107. data/lib/qml/data/list_model.rb +146 -0
  108. data/lib/qml/dispatchable.rb +34 -0
  109. data/lib/qml/dispatcher.rb +61 -0
  110. data/lib/qml/engine.rb +54 -0
  111. data/lib/qml/error_converter.rb +15 -0
  112. data/lib/qml/errors.rb +26 -0
  113. data/lib/qml/geometry.rb +3 -0
  114. data/lib/qml/geometry/point.rb +5 -0
  115. data/lib/qml/geometry/rectangle.rb +5 -0
  116. data/lib/qml/geometry/size.rb +5 -0
  117. data/lib/qml/image_provider.rb +87 -0
  118. data/lib/qml/meta_object.rb +20 -0
  119. data/lib/qml/models.rb +1 -0
  120. data/lib/qml/name_helper.rb +12 -0
  121. data/lib/qml/platform.rb +15 -0
  122. data/lib/qml/plugin_loader.rb +46 -0
  123. data/lib/qml/plugins.rb +26 -0
  124. data/lib/qml/qml.rb +1 -0
  125. data/lib/qml/qt.rb +6 -0
  126. data/lib/qml/qt_classes.rb +9 -0
  127. data/lib/qml/qt_object_base.rb +108 -0
  128. data/lib/qml/reactive.rb +8 -0
  129. data/lib/qml/reactive/bindable.rb +79 -0
  130. data/lib/qml/reactive/chained_signal.rb +25 -0
  131. data/lib/qml/reactive/error.rb +5 -0
  132. data/lib/qml/reactive/object.rb +278 -0
  133. data/lib/qml/reactive/property.rb +19 -0
  134. data/lib/qml/reactive/signal.rb +116 -0
  135. data/lib/qml/reactive/signal_spy.rb +27 -0
  136. data/lib/qml/reactive/signals/map_signal.rb +21 -0
  137. data/lib/qml/reactive/signals/merge_signal.rb +21 -0
  138. data/lib/qml/reactive/signals/select_signal.rb +21 -0
  139. data/lib/qml/reactive/simple_property.rb +17 -0
  140. data/lib/qml/reactive/unbound_property.rb +42 -0
  141. data/lib/qml/reactive/unbound_signal.rb +51 -0
  142. data/lib/qml/root_path.rb +3 -0
  143. data/lib/qml/test_util.rb +1 -0
  144. data/lib/qml/test_util/object_life_checker.rb +17 -0
  145. data/lib/qml/version.rb +3 -0
  146. data/lib/qml/wrappable.rb +9 -0
  147. data/qml.gemspec +28 -0
  148. data/spec/assets/testobj.qml +5 -0
  149. data/spec/qml/.access_spec.rb.swp +0 -0
  150. data/spec/qml/access_spec.rb +162 -0
  151. data/spec/qml/application_spec.rb +43 -0
  152. data/spec/qml/component_spec.rb +44 -0
  153. data/spec/qml/context_spec.rb +43 -0
  154. data/spec/qml/conversion_spec.rb +59 -0
  155. data/spec/qml/data/array_model_spec.rb +215 -0
  156. data/spec/qml/dispatchable_spec.rb +26 -0
  157. data/spec/qml/dispatcher_spec.rb +48 -0
  158. data/spec/qml/geometry/point_spec.rb +4 -0
  159. data/spec/qml/geometry/rectangle_spec.rb +4 -0
  160. data/spec/qml/geometry/size_spec.rb +4 -0
  161. data/spec/qml/plugin_loader_spec.rb +33 -0
  162. data/spec/qml/qt_object_base_spec.rb +119 -0
  163. data/spec/qml/reactive/object_spec.rb +273 -0
  164. data/spec/qml/reactive/property_spec.rb +70 -0
  165. data/spec/qml/reactive/signal_spec.rb +191 -0
  166. data/spec/qml/reactive/signal_spy_spec.rb +26 -0
  167. data/spec/qml/test_object_spec.rb +186 -0
  168. data/spec/qml_spec.rb +7 -0
  169. data/spec/spec_helper.rb +5 -0
  170. metadata +321 -0
@@ -0,0 +1,690 @@
1
+ #include "rubyvalue.h"
2
+ #include "util.h"
3
+ #include "ext_pointer.h"
4
+ #include "ext_metaobject.h"
5
+ #include "objectdata.h"
6
+ #include "accessobject.h"
7
+ #include "listmodel.h"
8
+ #include "ext_accesssupport.h"
9
+ #include <ruby/intern.h>
10
+ #define ONIG_ESCAPE_UCHAR_COLLISION
11
+ #include <ruby/encoding.h>
12
+ #include <QVariant>
13
+ #include <QDateTime>
14
+ #include <QRect>
15
+ #include <QSet>
16
+
17
+ namespace RubyQml {
18
+
19
+ namespace detail {
20
+
21
+ namespace {
22
+
23
+ RubyValue convertToString(RubyValue x)
24
+ {
25
+ return protect([&] {
26
+ if (rb_type(x) == T_SYMBOL) {
27
+ x = rb_sym_to_s(x);
28
+ }
29
+ return rb_convert_type(x, T_STRING, "String", "to_str");
30
+ });
31
+ }
32
+
33
+ struct ConverterHash
34
+ {
35
+ using FromRuby = QVariant (*)(RubyValue);
36
+ using ToRuby = RubyValue (*)(const QVariant &);
37
+
38
+ ConverterHash()
39
+ {
40
+ add<bool>();
41
+
42
+ add<char>();
43
+ add<short>();
44
+ add<long>();
45
+ add<long long>();
46
+ add<unsigned char>();
47
+ add<unsigned short>();
48
+ add<unsigned long>();
49
+ add<unsigned long long>();
50
+
51
+ add<int>();
52
+ add<unsigned int>();
53
+
54
+ add<float>();
55
+ add<double>();
56
+
57
+ add<QString>();
58
+ add<QByteArray>();
59
+
60
+ add<QVariant>();
61
+ add<QVariantList>();
62
+ add<QVariantHash>();
63
+ add<QVariantMap>();
64
+
65
+ add<QDateTime>();
66
+ add<QDate>();
67
+
68
+ add<QPoint>();
69
+ add<QPointF>();
70
+ add<QSize>();
71
+ add<QSizeF>();
72
+ add<QRect>();
73
+ add<QRectF>();
74
+
75
+ add<QObject *>();
76
+ add<const QMetaObject *>();
77
+
78
+ fromRubyHash[QMetaType::UnknownType] = [](RubyValue value) {
79
+ Q_UNUSED(value);
80
+ return QVariant();
81
+ };
82
+ toRubyHash[QMetaType::UnknownType] = [](const QVariant &variant) {
83
+ Q_UNUSED(variant);
84
+ return RubyValue();
85
+ };
86
+ }
87
+
88
+ template <typename T>
89
+ void add()
90
+ {
91
+ addFromRuby<T>();
92
+ addToRuby<T>();
93
+ }
94
+ template <typename T>
95
+ void addFromRuby()
96
+ {
97
+ fromRubyHash[qMetaTypeId<T>()] = [](RubyValue value) {
98
+ return QVariant::fromValue(value.to<T>());
99
+ };
100
+ }
101
+ template <typename T>
102
+ void addToRuby()
103
+ {
104
+ toRubyHash[qMetaTypeId<T>()] = [](const QVariant &variant) {
105
+ return RubyValue::from(variant.value<T>());
106
+ };
107
+ }
108
+
109
+ QHash<int, FromRuby> fromRubyHash;
110
+ QHash<int, ToRuby> toRubyHash;
111
+ };
112
+
113
+ Q_GLOBAL_STATIC(ConverterHash, converterHash)
114
+
115
+ } // unnamed namespace
116
+
117
+ RubyValue Conversion<const char *>::to(const char *str)
118
+ {
119
+ return protect([&] {
120
+ return rb_enc_str_new(str, strlen(str), rb_utf8_encoding());
121
+ });
122
+ }
123
+
124
+ template <> QByteArray Conversion<QByteArray>::from(RubyValue x)
125
+ {
126
+ x = convertToString(x);
127
+ return QByteArray(RSTRING_PTR(VALUE(x)), RSTRING_LEN(VALUE(x)));
128
+ }
129
+
130
+ template <> RubyValue Conversion<QByteArray>::to(const QByteArray &str)
131
+ {
132
+ return RubyValue::from(str.constData());
133
+ }
134
+
135
+ template <> QString Conversion<QString>::from(RubyValue x)
136
+ {
137
+ x = convertToString(x);
138
+ return QString::fromUtf8(RSTRING_PTR(VALUE(x)), RSTRING_LEN(VALUE(x)));
139
+ }
140
+
141
+ template <> RubyValue Conversion<QString>::to(const QString &str)
142
+ {
143
+ return RubyValue::from(str.toUtf8().constData());
144
+ }
145
+
146
+ template <> QDateTime Conversion<QDateTime>::from(RubyValue time)
147
+ {
148
+ timeval at;
149
+ int offset;
150
+ protect([&] {
151
+ if (rb_obj_is_kind_of(time, rb_cTime)) {
152
+ offset = NUM2INT(rb_funcall(time, RUBYQML_INTERN("gmt_offset"), 0));
153
+ } else { // DateTime
154
+ auto dayOffset = rb_funcall(time, RUBYQML_INTERN("offset"), 0);
155
+ offset = NUM2INT(RRATIONAL(dayOffset)->num) * 24 * 60 * 60 / NUM2INT(RRATIONAL(dayOffset)->den);
156
+ time = rb_funcall(time, RUBYQML_INTERN("to_time"), 0);
157
+ }
158
+ at = rb_time_timeval(time);
159
+ });
160
+ QDateTime dateTime;
161
+ dateTime.setOffsetFromUtc(offset);
162
+ dateTime.setMSecsSinceEpoch(at.tv_sec * 1000 + at.tv_usec / 1000);
163
+ return dateTime;
164
+ }
165
+
166
+ template <> RubyValue Conversion<QDateTime>::to(const QDateTime &dateTime) {
167
+ RubyValue ret;
168
+ protect([&] {
169
+ auto sec = rb_rational_new(LL2NUM(dateTime.toMSecsSinceEpoch()), INT2NUM(1000));
170
+ auto time = rb_time_num_new(sec, INT2NUM(dateTime.offsetFromUtc()));
171
+ ret = rb_funcall(time, RUBYQML_INTERN("to_datetime"), 0);
172
+ });
173
+ return ret;
174
+ }
175
+
176
+ template <> QDate Conversion<QDate>::from(RubyValue x)
177
+ {
178
+ int y, m, d;
179
+ protect([&] {
180
+ y = NUM2INT(rb_funcall(x, RUBYQML_INTERN("year"), 0));
181
+ m = NUM2INT(rb_funcall(x, RUBYQML_INTERN("month"), 0));
182
+ d = NUM2INT(rb_funcall(x, RUBYQML_INTERN("day"), 0));
183
+ });
184
+ return QDate(y, m, d);
185
+ }
186
+
187
+ template <> RubyValue Conversion<QDate>::to(const QDate &date)
188
+ {
189
+ static auto klass = RubyClass::fromPath("Date");
190
+ return protect([&] {
191
+ return rb_funcall(klass, RUBYQML_INTERN("new"), 3,
192
+ INT2NUM(date.year()), INT2NUM(date.month()), INT2NUM(date.day()));
193
+ });
194
+ }
195
+
196
+ template <> QPoint Conversion<QPoint>::from(RubyValue value)
197
+ {
198
+ int x, y;
199
+ protect([&] {
200
+ x = NUM2INT(rb_funcall(value, RUBYQML_INTERN("x"), 0));
201
+ y = NUM2INT(rb_funcall(value, RUBYQML_INTERN("y"), 0));
202
+ });
203
+ return QPoint(x, y);
204
+ }
205
+
206
+ template <> RubyValue Conversion<QPoint>::to(const QPoint &point)
207
+ {
208
+ static auto klass = RubyClass::fromPath("QML::Geometry::Point");
209
+ return protect([&] {
210
+ return rb_funcall(klass, RUBYQML_INTERN("new"), 2,
211
+ INT2NUM(point.x()), INT2NUM(point.y()));
212
+ });
213
+ }
214
+
215
+ template <> QPointF Conversion<QPointF>::from(RubyValue value)
216
+ {
217
+ double x, y;
218
+ protect([&] {
219
+ x = rb_num2dbl(rb_funcall(value, RUBYQML_INTERN("x"), 0));
220
+ y = rb_num2dbl(rb_funcall(value, RUBYQML_INTERN("y"), 0));
221
+ });
222
+ return QPointF(x, y);
223
+ }
224
+
225
+ template <> RubyValue Conversion<QPointF>::to(const QPointF &point)
226
+ {
227
+ static auto klass = RubyClass::fromPath("QML::Geometry::Point");
228
+ return protect([&] {
229
+ return rb_funcall(klass, RUBYQML_INTERN("new"), 2,
230
+ rb_float_new(point.x()), rb_float_new(point.y()));
231
+ });
232
+ }
233
+
234
+ template <> QSize Conversion<QSize>::from(RubyValue value)
235
+ {
236
+ int w, h;
237
+ protect([&] {
238
+ w = NUM2INT(rb_funcall(value, RUBYQML_INTERN("width"), 0));
239
+ h = NUM2INT(rb_funcall(value, RUBYQML_INTERN("height"), 0));
240
+ });
241
+ return QSize(w, h);
242
+ }
243
+
244
+ template <> RubyValue Conversion<QSize>::to(const QSize &size)
245
+ {
246
+ static auto klass = RubyClass::fromPath("QML::Geometry::Size");
247
+ return protect([&] {
248
+ return rb_funcall(klass, RUBYQML_INTERN("new"), 2,
249
+ INT2NUM(size.width()), INT2NUM(size.height()));
250
+ });
251
+ }
252
+
253
+ template <> QSizeF Conversion<QSizeF>::from(RubyValue value)
254
+ {
255
+ double w, h;
256
+ protect([&] {
257
+ w = rb_num2dbl(rb_funcall(value, RUBYQML_INTERN("width"), 0));
258
+ h = rb_num2dbl(rb_funcall(value, RUBYQML_INTERN("height"), 0));
259
+ });
260
+ return QSizeF(w, h);
261
+ }
262
+
263
+ template <> RubyValue Conversion<QSizeF>::to(const QSizeF &size)
264
+ {
265
+ static auto klass = RubyClass::fromPath("QML::Geometry::Size");
266
+ return protect([&] {
267
+ return rb_funcall(klass, RUBYQML_INTERN("new"), 2,
268
+ rb_float_new(size.width()), rb_float_new(size.height()));
269
+ });
270
+ }
271
+
272
+ template <> QRect Conversion<QRect>::from(RubyValue value)
273
+ {
274
+ int x, y, w, h;
275
+ protect([&] {
276
+ x = NUM2INT(rb_funcall(value, RUBYQML_INTERN("x"), 0));
277
+ y = NUM2INT(rb_funcall(value, RUBYQML_INTERN("y"), 0));
278
+ w = NUM2INT(rb_funcall(value, RUBYQML_INTERN("width"), 0));
279
+ h = NUM2INT(rb_funcall(value, RUBYQML_INTERN("height"), 0));
280
+ });
281
+ return QRect(QPoint(x, y), QSize(w, h));
282
+ }
283
+
284
+ template <> RubyValue Conversion<QRect>::to(const QRect &rect)
285
+ {
286
+ static auto klass = RubyClass::fromPath("QML::Geometry::Rectangle");
287
+ return protect([&] {
288
+ return rb_funcall(klass, RUBYQML_INTERN("new"), 4,
289
+ INT2NUM(rect.x()), INT2NUM(rect.y()),
290
+ INT2NUM(rect.width()), INT2NUM(rect.height()));
291
+ });
292
+ }
293
+
294
+ template <> QRectF Conversion<QRectF>::from(RubyValue value)
295
+ {
296
+ double x, y, w, h;
297
+ protect([&] {
298
+ x = rb_num2dbl(rb_funcall(value, RUBYQML_INTERN("x"), 0));
299
+ y = rb_num2dbl(rb_funcall(value, RUBYQML_INTERN("y"), 0));
300
+ w = rb_num2dbl(rb_funcall(value, RUBYQML_INTERN("width"), 0));
301
+ h = rb_num2dbl(rb_funcall(value, RUBYQML_INTERN("height"), 0));
302
+ });
303
+ return QRectF(QPoint(x, y), QSize(w, h));
304
+ }
305
+
306
+ template <> RubyValue Conversion<QRectF>::to(const QRectF &rect)
307
+ {
308
+ static auto klass = RubyClass::fromPath("QML::Geometry::Rectangle");
309
+ return protect([&] {
310
+ return rb_funcall(klass, RUBYQML_INTERN("new"), 4,
311
+ rb_float_new(rect.x()), rb_float_new(rect.y()),
312
+ rb_float_new(rect.width()), rb_float_new(rect.height()));
313
+ });
314
+ }
315
+
316
+ template <> QVariant Conversion<QVariant>::from(RubyValue x)
317
+ {
318
+ return x.toVariant();
319
+ }
320
+
321
+ template <> RubyValue Conversion<QVariant>::to(const QVariant &variant)
322
+ {
323
+ return RubyValue::fromVariant(variant);
324
+ }
325
+
326
+ template <> const QMetaObject *Conversion<const QMetaObject *>::from(RubyValue x)
327
+ {
328
+ if (x == RubyValue()) {
329
+ return nullptr;
330
+ }
331
+ return wrapperRubyClass<Ext::MetaObject>().unwrap(x)->metaObject();
332
+ }
333
+
334
+ template <> RubyValue Conversion<const QMetaObject *>::to(const QMetaObject *metaobj)
335
+ {
336
+ if (!metaobj) {
337
+ return Qnil;
338
+ }
339
+ return Ext::MetaObject::fromMetaObject(metaobj);
340
+ }
341
+
342
+ } // namespace detail
343
+
344
+ Q_GLOBAL_STATIC(QSet<int>, enumeratorMetaTypes)
345
+
346
+ bool RubyValue::isKindOf(const RubyModule &module) const
347
+ {
348
+ return protect([&] {
349
+ return rb_obj_is_kind_of(mValue, module);
350
+ });
351
+ }
352
+
353
+ bool RubyValue::isConvertibleTo(int metaType) const
354
+ {
355
+ auto x = *this;
356
+ if (metaType == QMetaType::QVariant) {
357
+ return true;
358
+ }
359
+ switch (rb_type(x)) {
360
+ case T_NIL:
361
+ return metaType == QMetaType::QObjectStar || metaType == QMetaType::type("const QMetaObject*");
362
+ case T_TRUE:
363
+ case T_FALSE:
364
+ return metaType == QMetaType::Bool;
365
+ case T_FIXNUM:
366
+ case T_BIGNUM:
367
+ switch (metaType) {
368
+ case QMetaType::Char:
369
+ case QMetaType::Short:
370
+ case QMetaType::Long:
371
+ case QMetaType::LongLong:
372
+
373
+ case QMetaType::UChar:
374
+ case QMetaType::UShort:
375
+ case QMetaType::ULong:
376
+ case QMetaType::ULongLong:
377
+
378
+ case QMetaType::Int:
379
+ case QMetaType::UInt:
380
+
381
+ case QMetaType::Float:
382
+ case QMetaType::Double:
383
+ return true;
384
+ default:
385
+ if (enumeratorMetaTypes->contains(metaType)) {
386
+ return true;
387
+ }
388
+ return false;
389
+ }
390
+ case T_FLOAT:
391
+ switch (metaType) {
392
+ case QMetaType::Float:
393
+ case QMetaType::Double:
394
+ return true;
395
+ default:
396
+ return false;
397
+ }
398
+ case T_SYMBOL:
399
+ case T_STRING:
400
+ switch (metaType) {
401
+ case QMetaType::QByteArray:
402
+ case QMetaType::QString:
403
+ return true;
404
+ default:
405
+ return false;
406
+ }
407
+ case T_ARRAY:
408
+ switch (metaType) {
409
+ case QMetaType::QVariantList:
410
+ return true;
411
+ default:
412
+ return false;
413
+ }
414
+ case T_HASH:
415
+ switch (metaType) {
416
+ case QMetaType::QVariantHash:
417
+ case QMetaType::QVariantMap:
418
+ return true;
419
+ default:
420
+ return false;
421
+ }
422
+ default:
423
+
424
+ break;
425
+ }
426
+
427
+ return protect([&] {
428
+ if (rb_obj_is_kind_of(x, rb_cTime)) {
429
+ return metaType == QMetaType::QDateTime;
430
+ }
431
+ static auto dateTimeClass = RubyClass::fromPath("DateTime");
432
+ if (rb_obj_is_kind_of(x, dateTimeClass)) {
433
+ return metaType == QMetaType::QDateTime;
434
+ }
435
+ static auto dateClass = RubyClass::fromPath("Date");
436
+ if (rb_obj_is_kind_of(x, dateClass)) {
437
+ return metaType == QMetaType::QDate || metaType == QMetaType::QDateTime;
438
+ }
439
+ static auto objectBaseClass = RubyClass::fromPath("QML::QtObjectBase");
440
+ if (rb_obj_is_kind_of(x, objectBaseClass)) {
441
+ if (metaType == QMetaType::QObjectStar) {
442
+ return true;
443
+ }
444
+ if (QMetaType::metaObjectForType(metaType)) {
445
+ auto metaObj = QMetaType::metaObjectForType(metaType);
446
+ if (x.to<QObject *>()->inherits(metaObj->className())) {
447
+ return true;
448
+ }
449
+ }
450
+ return false;
451
+ }
452
+ if (rb_obj_is_kind_of(x, wrapperRubyClass<Ext::MetaObject>())) {
453
+ return metaType == QMetaType::type("const QMetaObject*");
454
+ }
455
+ auto accessModule = RubyModule::fromPath("QML::Access");
456
+ if (rb_obj_is_kind_of(x, accessModule)) {
457
+ return metaType == QMetaType::QObjectStar;
458
+ }
459
+
460
+ static auto pointClass = RubyModule::fromPath("QML::Geometry::Point");
461
+ if (rb_obj_is_kind_of(x, pointClass)) {
462
+ return metaType == QMetaType::QPoint || metaType == QMetaType::QPointF;
463
+ }
464
+ static auto sizeClass = RubyModule::fromPath("QML::Geometry::Size");
465
+ if (rb_obj_is_kind_of(x, sizeClass)) {
466
+ return metaType == QMetaType::QSize || metaType == QMetaType::QSizeF;
467
+ }
468
+ static auto rectClass = RubyModule::fromPath("QML::Geometry::Rectangle");
469
+ if (rb_obj_is_kind_of(x, rectClass)) {
470
+ return metaType == QMetaType::QRect || metaType == QMetaType::QRectF;
471
+ }
472
+ static auto listModelClass = RubyModule::fromPath("QML::Data::ListModel");
473
+
474
+ if (rb_obj_is_kind_of(x, listModelClass)) {
475
+ return metaType == QMetaType::QObjectStar;
476
+ }
477
+ return false;
478
+ });
479
+ }
480
+
481
+ int RubyValue::defaultMetaType() const
482
+ {
483
+ auto x = *this;
484
+ switch (rb_type(x)) {
485
+ case T_NIL:
486
+ return QMetaType::UnknownType;
487
+ case T_TRUE:
488
+ case T_FALSE:
489
+ return QMetaType::Bool;
490
+ case T_FIXNUM:
491
+ case T_BIGNUM:
492
+ return QMetaType::Int;
493
+ case T_FLOAT:
494
+ return QMetaType::Double;
495
+ case T_SYMBOL:
496
+ return QMetaType::QString;
497
+ case T_STRING:
498
+ if (rb_enc_get_index(x) == rb_ascii8bit_encindex()) {
499
+ return QMetaType::QByteArray;
500
+ } else {
501
+ return QMetaType::QString;
502
+ }
503
+ case T_ARRAY:
504
+ return QMetaType::QVariantList;
505
+ case T_HASH:
506
+ return QMetaType::QVariantHash;
507
+ default:
508
+ break;
509
+ }
510
+ return protect([&]() -> int {
511
+ if (rb_obj_is_kind_of(x, rb_cTime)) {
512
+ return QMetaType::QDateTime;
513
+ }
514
+ static auto dateTimeClass = RubyModule::fromPath("DateTime");
515
+ if (rb_obj_is_kind_of(x, dateTimeClass)) {
516
+ return QMetaType::QDateTime;
517
+ }
518
+ static auto dateClass = RubyModule::fromPath("Date");
519
+ if (rb_obj_is_kind_of(x, dateClass)) {
520
+ return QMetaType::QDate;
521
+ }
522
+
523
+ static auto objectBaseClass = RubyClass::fromPath("QML::QtObjectBase");
524
+ if (rb_obj_is_kind_of(x, objectBaseClass)) {
525
+ return QMetaType::QObjectStar;
526
+ }
527
+ if (rb_obj_is_kind_of(x, wrapperRubyClass<Ext::MetaObject>())) {
528
+ return QMetaType::type("const QMetaObject*");
529
+ }
530
+ auto accessModule = RubyModule::fromPath("QML::Access");
531
+ if (rb_obj_is_kind_of(x, accessModule)) {
532
+ return QMetaType::QObjectStar;
533
+ }
534
+
535
+ static auto pointClass = RubyModule::fromPath("QML::Geometry::Point");
536
+ if (rb_obj_is_kind_of(x, pointClass)) {
537
+ return QMetaType::QPointF;
538
+ }
539
+ static auto sizeClass = RubyModule::fromPath("QML::Geometry::Size");
540
+ if (rb_obj_is_kind_of(x, sizeClass)) {
541
+ return QMetaType::QSizeF;
542
+ }
543
+ static auto rectClass = RubyModule::fromPath("QML::Geometry::Rectangle");
544
+ if (rb_obj_is_kind_of(x, rectClass)) {
545
+ return QMetaType::QRectF;
546
+ }
547
+
548
+ static auto listModelClass = RubyModule::fromPath("QML::Data::ListModel");
549
+ if (rb_obj_is_kind_of(x, listModelClass)) {
550
+ return QMetaType::QObjectStar;
551
+ }
552
+ return QMetaType::UnknownType;
553
+ });
554
+ }
555
+
556
+ RubyValue RubyValue::fromVariant(const QVariant &variant)
557
+ {
558
+ auto &hash = detail::converterHash->toRubyHash;
559
+ auto type = variant.userType();
560
+ if (QMetaType::metaObjectForType(type)) {
561
+ type = QMetaType::QObjectStar;
562
+ }
563
+ if (enumeratorMetaTypes->contains(type)) {
564
+ auto intValue = *static_cast<const int *>(variant.data());
565
+ return from(intValue);
566
+ }
567
+ if (!hash.contains(type)) {
568
+ fail("QML::ConversionError",
569
+ QString("failed to convert QVariant value (%1)")
570
+ .arg(QMetaType::typeName(type)));
571
+ }
572
+ return hash[type](variant);
573
+ }
574
+
575
+ QVariant RubyValue::toVariant() const
576
+ {
577
+ return toVariant(defaultMetaType());
578
+ }
579
+
580
+ QVariant RubyValue::toVariant(int type) const
581
+ {
582
+ auto x = *this;
583
+ auto &hash = detail::converterHash->fromRubyHash;
584
+ if (!hash.contains(type)) {
585
+ auto metaobj = QMetaType::metaObjectForType(type);
586
+ if (metaobj) {
587
+ auto qobj = hash[QMetaType::QObjectStar](x).value<QObject *>();
588
+ if (qobj->inherits(metaobj->className())) {
589
+ return QVariant::fromValue(qobj);
590
+ }
591
+ fail("QML::ConversionError",
592
+ QString("failed to convert QObject value (%1 to %2)")
593
+ .arg(qobj->metaObject()->className())
594
+ .arg(metaobj->className()));
595
+ }
596
+ if (enumeratorMetaTypes->contains(type)) {
597
+ auto intValue = to<int>();
598
+ auto data = QMetaType::create(type);
599
+ *static_cast<int *>(data) = intValue;
600
+ return QVariant(type, data);
601
+ }
602
+ fail("QML::ConversionError",
603
+ QString("failed to convert Ruby value (%1 to %2)")
604
+ .arg(rb_obj_classname(x))
605
+ .arg(QMetaType::typeName(type)));
606
+ return QVariant();
607
+ }
608
+ return hash[type](x);
609
+ }
610
+
611
+ RubyValue RubyValue::fromQObject(QObject *obj, bool implicit)
612
+ {
613
+ if (!obj) {
614
+ return Qnil;
615
+ }
616
+
617
+ if (implicit) {
618
+ auto accessObj = dynamic_cast<AccessWrapper *>(obj);
619
+ if (accessObj) {
620
+ return accessObj->wrappedValue();
621
+ }
622
+ auto listModel = dynamic_cast<ListModel *>(obj);
623
+ if (listModel) {
624
+ return listModel->rubyModel();
625
+ }
626
+ }
627
+
628
+ auto data = ObjectData::getOrCreate(obj);
629
+ if (data->rubyObject) {
630
+ return data->rubyObject;
631
+ }
632
+
633
+ auto metaobj = Ext::MetaObject::fromMetaObject(obj->metaObject());
634
+ auto pointer = Ext::Pointer::fromQObject(obj, false);
635
+
636
+ RubyValue wrapper;
637
+ protect([&] {
638
+ auto klass = rb_funcall(metaobj, RUBYQML_INTERN("build_class"), 0);
639
+ wrapper = rb_obj_alloc(klass);
640
+ rb_funcall(wrapper, RUBYQML_INTERN("pointer="), 1, pointer);
641
+ rb_obj_call_init(wrapper, 0, nullptr);
642
+ });
643
+ data->rubyObject = wrapper;
644
+ return wrapper;
645
+ }
646
+
647
+ QObject *RubyValue::toQObject() const
648
+ {
649
+ auto x = *this;
650
+
651
+ if (x == RubyValue()) {
652
+ return nullptr;
653
+ }
654
+ static auto accessModule = RubyModule::fromPath("QML::Access");
655
+ if (x.isKindOf(accessModule)) {
656
+ auto wrapperFactory = protect([&] {
657
+ return rb_funcall(rb_obj_class(x), RUBYQML_INTERN("access_wrapper_factory"), 0);
658
+ });
659
+ return wrapperRubyClass<Ext::AccessWrapperFactory>().unwrap(wrapperFactory)->create(x);
660
+ }
661
+ static auto listModelClass = RubyModule::fromPath("QML::Data::ListModel");
662
+ if (x.isKindOf(listModelClass)) {
663
+ return new ListModel(x);
664
+ }
665
+
666
+ static auto objectBaseClass = RubyClass::fromPath("QML::QtObjectBase");
667
+ if (!x.isKindOf(objectBaseClass)) {
668
+ fail("QML::ConversionError",
669
+ QString("expected QML::QtObjectbase, got %1")
670
+ .arg(x.send("class").send("name").to<QString>()));
671
+ }
672
+ auto objptr = x.send(RUBYQML_INTERN("pointer"));
673
+ auto obj = wrapperRubyClass<Ext::Pointer>().unwrap(objptr)->fetchQObject();
674
+ Ext::MetaObject::fromMetaObject(obj->metaObject()).send(RUBYQML_INTERN("build_class"));
675
+ return obj;
676
+ }
677
+
678
+ ID RubyValue::toID() const
679
+ {
680
+ return protect([&] {
681
+ return SYM2ID(rb_convert_type(*this, T_SYMBOL, "Symbol", "to_sym"));
682
+ });
683
+ }
684
+
685
+ void RubyValue::addEnumeratorMetaType(int metaType)
686
+ {
687
+ *enumeratorMetaTypes << metaType;
688
+ }
689
+
690
+ } // namespace RubyQml