qml 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +46 -0
- data/.rspec +2 -0
- data/.travis.yml +15 -0
- data/.yardopts +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +351 -0
- data/Rakefile +6 -0
- data/examples/assets/fonts/fontawesome-webfont.ttf +0 -0
- data/examples/fizzbuzz/fizzbuzz.rb +43 -0
- data/examples/fizzbuzz/main.qml +38 -0
- data/examples/imageprovider/imageprovider.rb +57 -0
- data/examples/imageprovider/main.qml +51 -0
- data/examples/todo/main.qml +70 -0
- data/examples/todo/todo.rb +36 -0
- data/examples/twitter/main.qml +36 -0
- data/examples/twitter/twitter.rb +55 -0
- data/ext/qml/accessclass.cpp +71 -0
- data/ext/qml/accessclass.h +19 -0
- data/ext/qml/accessobject.cpp +30 -0
- data/ext/qml/accessobject.h +22 -0
- data/ext/qml/application.cpp +54 -0
- data/ext/qml/application.h +17 -0
- data/ext/qml/common.h +18 -0
- data/ext/qml/ext_accesssupport.cpp +77 -0
- data/ext/qml/ext_accesssupport.h +42 -0
- data/ext/qml/ext_gcmarker.cpp +39 -0
- data/ext/qml/ext_gcmarker.h +27 -0
- data/ext/qml/ext_kernel.cpp +62 -0
- data/ext/qml/ext_kernel.h +11 -0
- data/ext/qml/ext_metaobject.cpp +410 -0
- data/ext/qml/ext_metaobject.h +62 -0
- data/ext/qml/ext_pluginloader.cpp +55 -0
- data/ext/qml/ext_pluginloader.h +32 -0
- data/ext/qml/ext_pointer.cpp +134 -0
- data/ext/qml/ext_pointer.h +43 -0
- data/ext/qml/ext_testutil.cpp +42 -0
- data/ext/qml/ext_testutil.h +11 -0
- data/ext/qml/extconf.rb +84 -0
- data/ext/qml/foreignclass.cpp +72 -0
- data/ext/qml/foreignclass.h +88 -0
- data/ext/qml/foreignmetaobject.cpp +345 -0
- data/ext/qml/foreignmetaobject.h +46 -0
- data/ext/qml/foreignobject.cpp +22 -0
- data/ext/qml/foreignobject.h +21 -0
- data/ext/qml/functioninfo.h +16 -0
- data/ext/qml/init.cpp +69 -0
- data/ext/qml/listmodel.cpp +112 -0
- data/ext/qml/listmodel.h +43 -0
- data/ext/qml/markable.h +12 -0
- data/ext/qml/objectdata.cpp +26 -0
- data/ext/qml/objectdata.h +20 -0
- data/ext/qml/objectgc.cpp +69 -0
- data/ext/qml/objectgc.h +28 -0
- data/ext/qml/plugins/core/applicationextension.cpp +34 -0
- data/ext/qml/plugins/core/applicationextension.h +28 -0
- data/ext/qml/plugins/core/componentextension.cpp +41 -0
- data/ext/qml/plugins/core/componentextension.h +28 -0
- data/ext/qml/plugins/core/contextextension.cpp +39 -0
- data/ext/qml/plugins/core/contextextension.h +29 -0
- data/ext/qml/plugins/core/core.pro +29 -0
- data/ext/qml/plugins/core/coreplugin.cpp +87 -0
- data/ext/qml/plugins/core/coreplugin.h +49 -0
- data/ext/qml/plugins/core/engineextension.cpp +27 -0
- data/ext/qml/plugins/core/engineextension.h +28 -0
- data/ext/qml/plugins/core/imageprovider.cpp +38 -0
- data/ext/qml/plugins/core/imageprovider.h +18 -0
- data/ext/qml/plugins/core/imagerequestpromise.cpp +19 -0
- data/ext/qml/plugins/core/imagerequestpromise.h +21 -0
- data/ext/qml/plugins/core/qmlexception.cpp +11 -0
- data/ext/qml/plugins/core/qmlexception.h +17 -0
- data/ext/qml/plugins/testutil/objectlifechecker.cpp +17 -0
- data/ext/qml/plugins/testutil/objectlifechecker.h +24 -0
- data/ext/qml/plugins/testutil/ownershiptest.cpp +26 -0
- data/ext/qml/plugins/testutil/ownershiptest.h +30 -0
- data/ext/qml/plugins/testutil/testobject.cpp +6 -0
- data/ext/qml/plugins/testutil/testobject.h +108 -0
- data/ext/qml/plugins/testutil/testobjectsubclass.cpp +10 -0
- data/ext/qml/plugins/testutil/testobjectsubclass.h +19 -0
- data/ext/qml/plugins/testutil/testutil.pro +20 -0
- data/ext/qml/plugins/testutil/testutilplugin.cpp +47 -0
- data/ext/qml/plugins/testutil/testutilplugin.h +32 -0
- data/ext/qml/qmltyperegisterer.cpp +74 -0
- data/ext/qml/qmltyperegisterer.h +30 -0
- data/ext/qml/rubyclass.cpp +94 -0
- data/ext/qml/rubyclass.h +234 -0
- data/ext/qml/rubyvalue.cpp +690 -0
- data/ext/qml/rubyvalue.h +256 -0
- data/ext/qml/signalforwarder.cpp +66 -0
- data/ext/qml/signalforwarder.h +29 -0
- data/ext/qml/util.cpp +120 -0
- data/ext/qml/util.h +101 -0
- data/ext/qml/valuereference.cpp +50 -0
- data/ext/qml/valuereference.h +22 -0
- data/ext/qml/weakvaluereference.cpp +27 -0
- data/ext/qml/weakvaluereference.h +19 -0
- data/lib/qml.rb +41 -0
- data/lib/qml/access.rb +137 -0
- data/lib/qml/application.rb +139 -0
- data/lib/qml/class_builder.rb +126 -0
- data/lib/qml/component.rb +53 -0
- data/lib/qml/context.rb +71 -0
- data/lib/qml/data.rb +2 -0
- data/lib/qml/data/array_model.rb +103 -0
- data/lib/qml/data/error.rb +5 -0
- data/lib/qml/data/list_model.rb +146 -0
- data/lib/qml/dispatchable.rb +34 -0
- data/lib/qml/dispatcher.rb +61 -0
- data/lib/qml/engine.rb +54 -0
- data/lib/qml/error_converter.rb +15 -0
- data/lib/qml/errors.rb +26 -0
- data/lib/qml/geometry.rb +3 -0
- data/lib/qml/geometry/point.rb +5 -0
- data/lib/qml/geometry/rectangle.rb +5 -0
- data/lib/qml/geometry/size.rb +5 -0
- data/lib/qml/image_provider.rb +87 -0
- data/lib/qml/meta_object.rb +20 -0
- data/lib/qml/models.rb +1 -0
- data/lib/qml/name_helper.rb +12 -0
- data/lib/qml/platform.rb +15 -0
- data/lib/qml/plugin_loader.rb +46 -0
- data/lib/qml/plugins.rb +26 -0
- data/lib/qml/qml.rb +1 -0
- data/lib/qml/qt.rb +6 -0
- data/lib/qml/qt_classes.rb +9 -0
- data/lib/qml/qt_object_base.rb +108 -0
- data/lib/qml/reactive.rb +8 -0
- data/lib/qml/reactive/bindable.rb +79 -0
- data/lib/qml/reactive/chained_signal.rb +25 -0
- data/lib/qml/reactive/error.rb +5 -0
- data/lib/qml/reactive/object.rb +278 -0
- data/lib/qml/reactive/property.rb +19 -0
- data/lib/qml/reactive/signal.rb +116 -0
- data/lib/qml/reactive/signal_spy.rb +27 -0
- data/lib/qml/reactive/signals/map_signal.rb +21 -0
- data/lib/qml/reactive/signals/merge_signal.rb +21 -0
- data/lib/qml/reactive/signals/select_signal.rb +21 -0
- data/lib/qml/reactive/simple_property.rb +17 -0
- data/lib/qml/reactive/unbound_property.rb +42 -0
- data/lib/qml/reactive/unbound_signal.rb +51 -0
- data/lib/qml/root_path.rb +3 -0
- data/lib/qml/test_util.rb +1 -0
- data/lib/qml/test_util/object_life_checker.rb +17 -0
- data/lib/qml/version.rb +3 -0
- data/lib/qml/wrappable.rb +9 -0
- data/qml.gemspec +28 -0
- data/spec/assets/testobj.qml +5 -0
- data/spec/qml/.access_spec.rb.swp +0 -0
- data/spec/qml/access_spec.rb +162 -0
- data/spec/qml/application_spec.rb +43 -0
- data/spec/qml/component_spec.rb +44 -0
- data/spec/qml/context_spec.rb +43 -0
- data/spec/qml/conversion_spec.rb +59 -0
- data/spec/qml/data/array_model_spec.rb +215 -0
- data/spec/qml/dispatchable_spec.rb +26 -0
- data/spec/qml/dispatcher_spec.rb +48 -0
- data/spec/qml/geometry/point_spec.rb +4 -0
- data/spec/qml/geometry/rectangle_spec.rb +4 -0
- data/spec/qml/geometry/size_spec.rb +4 -0
- data/spec/qml/plugin_loader_spec.rb +33 -0
- data/spec/qml/qt_object_base_spec.rb +119 -0
- data/spec/qml/reactive/object_spec.rb +273 -0
- data/spec/qml/reactive/property_spec.rb +70 -0
- data/spec/qml/reactive/signal_spec.rb +191 -0
- data/spec/qml/reactive/signal_spy_spec.rb +26 -0
- data/spec/qml/test_object_spec.rb +186 -0
- data/spec/qml_spec.rb +7 -0
- data/spec/spec_helper.rb +5 -0
- metadata +321 -0
data/ext/qml/rubyvalue.h
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#include "util.h"
|
|
3
|
+
#include <ruby.h>
|
|
4
|
+
#include <array>
|
|
5
|
+
|
|
6
|
+
class QVariant;
|
|
7
|
+
|
|
8
|
+
namespace RubyQml {
|
|
9
|
+
|
|
10
|
+
namespace detail {
|
|
11
|
+
template <typename T, typename Enable = void> struct Conversion
|
|
12
|
+
{
|
|
13
|
+
static T from(RubyValue x);
|
|
14
|
+
static RubyValue to(typename std::conditional<std::is_scalar<T>::value, T, const T &>::type str);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
#define RUBYQML_INTERN(str) \
|
|
20
|
+
[] { static auto id = rb_intern(str); return id; }()
|
|
21
|
+
|
|
22
|
+
class RubyModule;
|
|
23
|
+
|
|
24
|
+
class RubyValue
|
|
25
|
+
{
|
|
26
|
+
public:
|
|
27
|
+
RubyValue() = default;
|
|
28
|
+
RubyValue(VALUE value) : mValue(value) {}
|
|
29
|
+
|
|
30
|
+
template <typename T> static RubyValue from(const T &value)
|
|
31
|
+
{
|
|
32
|
+
return detail::Conversion<T>::to(value);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
template <typename T> T to() const
|
|
36
|
+
{
|
|
37
|
+
return detail::Conversion<T>::from(*this);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
operator VALUE() const { return mValue; }
|
|
41
|
+
|
|
42
|
+
template <typename ... TArgs>
|
|
43
|
+
RubyValue send(ID method, TArgs ... args) const
|
|
44
|
+
{
|
|
45
|
+
constexpr int argc = sizeof...(args);
|
|
46
|
+
std::array<VALUE, argc> argv = {{ VALUE(args)... }};
|
|
47
|
+
return protect([&] {
|
|
48
|
+
return rb_funcall2(mValue, method, argc, argv.data());
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
template <typename ... TArgs>
|
|
53
|
+
RubyValue send(const char *method, TArgs ... args) const
|
|
54
|
+
{
|
|
55
|
+
return send(rb_intern(method), args...);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
bool operator==(const RubyValue &other) const { return mValue == other.mValue; }
|
|
59
|
+
bool operator!=(const RubyValue &other) const { return !operator==(other); }
|
|
60
|
+
explicit operator bool() const { return RTEST(mValue); }
|
|
61
|
+
|
|
62
|
+
bool isKindOf(const RubyModule &module) const;
|
|
63
|
+
bool isConvertibleTo(int metaType) const;
|
|
64
|
+
int defaultMetaType() const;
|
|
65
|
+
|
|
66
|
+
static RubyValue fromVariant(const QVariant &value);
|
|
67
|
+
QVariant toVariant() const;
|
|
68
|
+
QVariant toVariant(int type) const;
|
|
69
|
+
static RubyValue fromQObject(QObject *obj, bool implicit = true);
|
|
70
|
+
QObject *toQObject() const;
|
|
71
|
+
|
|
72
|
+
ID toID() const;
|
|
73
|
+
static RubyValue fromID(ID id) { return ID2SYM(id); }
|
|
74
|
+
|
|
75
|
+
static void addEnumeratorMetaType(int metaType);
|
|
76
|
+
|
|
77
|
+
private:
|
|
78
|
+
volatile VALUE mValue = Qnil;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
namespace detail {
|
|
82
|
+
|
|
83
|
+
template <>
|
|
84
|
+
struct Conversion<RubyValue>
|
|
85
|
+
{
|
|
86
|
+
static RubyValue from(RubyValue x) { return x; }
|
|
87
|
+
static RubyValue to(RubyValue x) { return x; }
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
template <>
|
|
91
|
+
struct Conversion<bool>
|
|
92
|
+
{
|
|
93
|
+
static bool from(RubyValue value) { return value; }
|
|
94
|
+
static RubyValue to(bool x) { return x ? Qtrue : Qfalse; }
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// signed integers
|
|
98
|
+
|
|
99
|
+
template <typename T>
|
|
100
|
+
struct Conversion<T, typename std::enable_if<std::is_signed<T>::value && std::is_integral<T>::value>::type>
|
|
101
|
+
{
|
|
102
|
+
static T from(RubyValue x)
|
|
103
|
+
{
|
|
104
|
+
return protect([&] {
|
|
105
|
+
return NUM2LL(x);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
static RubyValue to(T x)
|
|
109
|
+
{
|
|
110
|
+
return LL2NUM(x);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// unsigned integers
|
|
115
|
+
|
|
116
|
+
template <typename T>
|
|
117
|
+
struct Conversion<T, typename std::enable_if<std::is_unsigned<T>::value && std::is_integral<T>::value>::type>
|
|
118
|
+
{
|
|
119
|
+
static T from(RubyValue x)
|
|
120
|
+
{
|
|
121
|
+
return protect([&] {
|
|
122
|
+
return NUM2ULL(x);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
static RubyValue to(T x)
|
|
126
|
+
{
|
|
127
|
+
return ULL2NUM(x);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// floating point values
|
|
132
|
+
|
|
133
|
+
template <typename T>
|
|
134
|
+
struct Conversion<T, typename std::enable_if<std::is_floating_point<T>::value>::type>
|
|
135
|
+
{
|
|
136
|
+
static T from(RubyValue x)
|
|
137
|
+
{
|
|
138
|
+
return protect([&] {
|
|
139
|
+
auto type = rb_type(x);
|
|
140
|
+
if (type == T_FIXNUM || type == T_BIGNUM) {
|
|
141
|
+
return double(NUM2LL(x));
|
|
142
|
+
} else {
|
|
143
|
+
return rb_float_value(x);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
static RubyValue to(T x)
|
|
148
|
+
{
|
|
149
|
+
return rb_float_new(x);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// QList and QVector
|
|
154
|
+
|
|
155
|
+
template <class T> struct IsQListLike : std::false_type {};
|
|
156
|
+
template <class V> struct IsQListLike<QList<V>> : std::true_type {};
|
|
157
|
+
template <class V> struct IsQListLike<QVector<V>> : std::true_type {};
|
|
158
|
+
|
|
159
|
+
template <template<class> class T, class V>
|
|
160
|
+
struct Conversion<T<V>, typename std::enable_if<IsQListLike<T<V>>::value>::type>
|
|
161
|
+
{
|
|
162
|
+
static T<V> from(RubyValue x)
|
|
163
|
+
{
|
|
164
|
+
protect([&] {
|
|
165
|
+
x = rb_convert_type(x, T_ARRAY, "Array", "to_ary");
|
|
166
|
+
});
|
|
167
|
+
int length = RARRAY_LEN(VALUE(x));
|
|
168
|
+
T<V> list;
|
|
169
|
+
list.reserve(length);
|
|
170
|
+
for (int i = 0; i < length; ++i) {
|
|
171
|
+
list << RubyValue(RARRAY_AREF(VALUE(x), i)).to<V>();
|
|
172
|
+
}
|
|
173
|
+
return list;
|
|
174
|
+
}
|
|
175
|
+
static RubyValue to(const T<V> &list)
|
|
176
|
+
{
|
|
177
|
+
RubyValue ary = protect([&] {
|
|
178
|
+
return rb_ary_new();
|
|
179
|
+
});
|
|
180
|
+
for (const auto &elem : list) {
|
|
181
|
+
auto x = RubyValue::from(elem);
|
|
182
|
+
protect([&] {
|
|
183
|
+
rb_ary_push(ary, x);
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
return ary;
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// QHash and QMap
|
|
191
|
+
|
|
192
|
+
template <class T> struct IsQHashLike : std::false_type {};
|
|
193
|
+
template <class K, class V> struct IsQHashLike<QHash<K, V>> : std::true_type {};
|
|
194
|
+
template <class K, class V> struct IsQHashLike<QMap<K, V>> : std::true_type {};
|
|
195
|
+
|
|
196
|
+
template <template<class, class> class T, class K, class V>
|
|
197
|
+
struct Conversion<T<K, V>, typename std::enable_if<IsQHashLike<T<K, V>>::value>::type>
|
|
198
|
+
{
|
|
199
|
+
static T<K, V> from(RubyValue x)
|
|
200
|
+
{
|
|
201
|
+
protect([&] {
|
|
202
|
+
x = rb_convert_type(x, T_HASH, "Hash", "to_hash");
|
|
203
|
+
});
|
|
204
|
+
T<K, V> hash;
|
|
205
|
+
protect([&] {
|
|
206
|
+
auto each = [](VALUE key, VALUE value, VALUE arg) -> int {
|
|
207
|
+
auto &hash = *reinterpret_cast<T<K, V> *>(arg);
|
|
208
|
+
unprotect([&] {
|
|
209
|
+
hash[RubyValue(key).to<K>()] = RubyValue(value).to<V>();
|
|
210
|
+
});
|
|
211
|
+
return ST_CONTINUE;
|
|
212
|
+
};
|
|
213
|
+
auto eachPtr = (int (*)(VALUE, VALUE, VALUE))each;
|
|
214
|
+
rb_hash_foreach(x, (int (*)(...))eachPtr, (VALUE)(&hash));
|
|
215
|
+
});
|
|
216
|
+
return hash;
|
|
217
|
+
}
|
|
218
|
+
static RubyValue to(const T<K, V> &hash)
|
|
219
|
+
{
|
|
220
|
+
RubyValue rubyHash = protect([&] {
|
|
221
|
+
return rb_hash_new();
|
|
222
|
+
});
|
|
223
|
+
for (auto i = hash.begin(); i != hash.end(); ++i) {
|
|
224
|
+
auto k = RubyValue::from(i.key());
|
|
225
|
+
auto v = RubyValue::from(i.value());
|
|
226
|
+
protect([&] {
|
|
227
|
+
rb_hash_aset(rubyHash, k, v);
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
return rubyHash;
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
// QObject-derived
|
|
235
|
+
|
|
236
|
+
template <typename T>
|
|
237
|
+
struct Conversion<
|
|
238
|
+
T *,
|
|
239
|
+
typename std::enable_if<
|
|
240
|
+
std::is_base_of<QObject, T>::value
|
|
241
|
+
>::type
|
|
242
|
+
>
|
|
243
|
+
{
|
|
244
|
+
static T *from(RubyValue value) { return dynamic_cast<T *>(value.toQObject()); }
|
|
245
|
+
static RubyValue to(T *value) { return RubyValue::fromQObject(value); }
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
template <>
|
|
249
|
+
struct Conversion<const char *>
|
|
250
|
+
{
|
|
251
|
+
static RubyValue to(const char *str);
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
} // namespace detail
|
|
255
|
+
|
|
256
|
+
} // namespace RubyQml
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#include "signalforwarder.h"
|
|
2
|
+
#include "util.h"
|
|
3
|
+
#include <QtCore/QDebug>
|
|
4
|
+
|
|
5
|
+
namespace RubyQml {
|
|
6
|
+
|
|
7
|
+
SignalForwarder::SignalForwarder(QObject *obj, const QMetaMethod &signal, RubyValue proc) :
|
|
8
|
+
QObject(obj),
|
|
9
|
+
mSignal(signal),
|
|
10
|
+
mProcRef(proc)
|
|
11
|
+
{
|
|
12
|
+
QMetaObject::connect(obj, signal.methodIndex(), this, QObject::staticMetaObject.methodCount());
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
SignalForwarder::~SignalForwarder()
|
|
16
|
+
{
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
int SignalForwarder::qt_metacall(QMetaObject::Call call, int id, void **args)
|
|
20
|
+
{
|
|
21
|
+
id = QObject::qt_metacall(call, id, args);
|
|
22
|
+
if (id < 0) {
|
|
23
|
+
return id;
|
|
24
|
+
}
|
|
25
|
+
if (call == QMetaObject::InvokeMetaMethod) {
|
|
26
|
+
if (id == 0) {
|
|
27
|
+
forwardArgs(args);
|
|
28
|
+
}
|
|
29
|
+
--id;
|
|
30
|
+
}
|
|
31
|
+
return id;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
void SignalForwarder::gc_mark()
|
|
35
|
+
{
|
|
36
|
+
rb_gc_mark(mProcRef.value());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
void SignalForwarder::forwardArgs(void **args)
|
|
40
|
+
{
|
|
41
|
+
QVariantList list;
|
|
42
|
+
list.reserve(mSignal.parameterCount());
|
|
43
|
+
for (int i = 0; i < mSignal.parameterCount(); ++i) {
|
|
44
|
+
auto type = mSignal.parameterType(i);
|
|
45
|
+
if (type == QMetaType::QVariant) {
|
|
46
|
+
list << *static_cast<QVariant *>(args[i + 1]);
|
|
47
|
+
} else {
|
|
48
|
+
list << QVariant(type, args[i + 1]);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
callProc(list);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
void SignalForwarder::callProc(const QVariantList &list)
|
|
55
|
+
{
|
|
56
|
+
if (mProcRef.hasValue()) {
|
|
57
|
+
withGvl([&] {
|
|
58
|
+
auto args = RubyValue::from(list);
|
|
59
|
+
protect([&] {
|
|
60
|
+
rb_funcall2(mProcRef.value(), rb_intern("call"), RARRAY_LEN(VALUE(args)), RARRAY_PTR(VALUE(args)));
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
} // namespace RubyQml
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#include "rubyvalue.h"
|
|
3
|
+
#include "weakvaluereference.h"
|
|
4
|
+
#include "markable.h"
|
|
5
|
+
#include <QObject>
|
|
6
|
+
#include <QMetaMethod>
|
|
7
|
+
|
|
8
|
+
namespace RubyQml {
|
|
9
|
+
|
|
10
|
+
class SignalForwarder : public QObject, public Markable
|
|
11
|
+
{
|
|
12
|
+
public:
|
|
13
|
+
SignalForwarder(QObject *obj, const QMetaMethod &signal, RubyValue proc);
|
|
14
|
+
~SignalForwarder();
|
|
15
|
+
|
|
16
|
+
int qt_metacall(QMetaObject::Call call, int id, void **args) override;
|
|
17
|
+
|
|
18
|
+
void gc_mark() override;
|
|
19
|
+
|
|
20
|
+
private:
|
|
21
|
+
void forwardArgs(void **args);
|
|
22
|
+
void callProc(const QVariantList &list);
|
|
23
|
+
|
|
24
|
+
QMetaMethod mSignal;
|
|
25
|
+
WeakValueReference mProcRef;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
} // namespace RubyQml
|
|
29
|
+
|
data/ext/qml/util.cpp
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#include "util.h"
|
|
2
|
+
#include "rubyvalue.h"
|
|
3
|
+
#include <QString>
|
|
4
|
+
#include <ruby/thread.h>
|
|
5
|
+
#include <string>
|
|
6
|
+
#include <memory>
|
|
7
|
+
#include <cxxabi.h>
|
|
8
|
+
|
|
9
|
+
namespace RubyQml {
|
|
10
|
+
|
|
11
|
+
void protect(const std::function<void ()> &doAction)
|
|
12
|
+
{
|
|
13
|
+
auto callback = [](VALUE data) {
|
|
14
|
+
auto &doAction= *reinterpret_cast<const std::function<void ()> *>(data);
|
|
15
|
+
doAction();
|
|
16
|
+
return Qnil;
|
|
17
|
+
};
|
|
18
|
+
int state;
|
|
19
|
+
rb_protect(callback, reinterpret_cast<VALUE>(&doAction), &state);
|
|
20
|
+
if (state) {
|
|
21
|
+
throw RubyException(state);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
void unprotect(const std::function<void ()> &doAction) noexcept
|
|
26
|
+
{
|
|
27
|
+
int state = 0;
|
|
28
|
+
bool cppErrorOccured= false;
|
|
29
|
+
VALUE cppErrorClassName = Qnil;
|
|
30
|
+
VALUE cppErrorMessage = Qnil;
|
|
31
|
+
try {
|
|
32
|
+
doAction();
|
|
33
|
+
}
|
|
34
|
+
catch (const RubyException &ex) {
|
|
35
|
+
state = ex.state();
|
|
36
|
+
}
|
|
37
|
+
catch (const std::exception &ex) {
|
|
38
|
+
cppErrorOccured = true;
|
|
39
|
+
int status;
|
|
40
|
+
auto classname = abi::__cxa_demangle(typeid(ex).name(), nullptr, nullptr, &status);
|
|
41
|
+
cppErrorClassName = rb_str_new_cstr(classname);
|
|
42
|
+
free(classname);
|
|
43
|
+
cppErrorMessage = rb_str_new_cstr(ex.what());
|
|
44
|
+
}
|
|
45
|
+
if (state) {
|
|
46
|
+
rb_jump_tag(state);
|
|
47
|
+
}
|
|
48
|
+
if (cppErrorOccured) {
|
|
49
|
+
auto patterns = rb_funcall(rb_path2class("QML::ErrorConverter"), rb_intern("patterns"), 0);
|
|
50
|
+
auto rubyClass = rb_hash_aref(patterns, cppErrorClassName);
|
|
51
|
+
VALUE exc;
|
|
52
|
+
if (RTEST(rubyClass)) {
|
|
53
|
+
exc = rb_funcall(rubyClass, rb_intern("new"), 1, cppErrorMessage);
|
|
54
|
+
} else {
|
|
55
|
+
exc = rb_funcall(rb_path2class("QML::CppError"), rb_intern("new"), 2, cppErrorClassName, cppErrorMessage);
|
|
56
|
+
}
|
|
57
|
+
rb_exc_raise(exc);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
void rescue(const std::function<void ()> &doAction, const std::function<void (RubyValue)> &handleException)
|
|
62
|
+
{
|
|
63
|
+
VALUE (*callback)(VALUE) = [](VALUE data) {
|
|
64
|
+
(*reinterpret_cast<std::function<void ()> *>(data))();
|
|
65
|
+
return Qnil;
|
|
66
|
+
};
|
|
67
|
+
VALUE (*rescueCallback)(VALUE, VALUE) = [](VALUE data, VALUE excObject) {
|
|
68
|
+
(*reinterpret_cast<std::function<void (VALUE)>*>(data))(excObject);
|
|
69
|
+
return Qnil;
|
|
70
|
+
};
|
|
71
|
+
rb_rescue((VALUE (*)(...))callback, (VALUE)&doAction, (VALUE (*)(...))rescueCallback, (VALUE)&handleException);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
namespace {
|
|
75
|
+
|
|
76
|
+
void changeGvl(const std::function<void ()> &doAction, bool gvl)
|
|
77
|
+
{
|
|
78
|
+
auto actionPtr = const_cast<void *>(static_cast<const void *>(&doAction));
|
|
79
|
+
auto f = [](void *data) -> void * {
|
|
80
|
+
auto &doAction= *static_cast<const std::function<void ()> *>(data);
|
|
81
|
+
try {
|
|
82
|
+
doAction();
|
|
83
|
+
} catch (...) {
|
|
84
|
+
return new std::exception_ptr(std::current_exception());
|
|
85
|
+
}
|
|
86
|
+
return nullptr;
|
|
87
|
+
};
|
|
88
|
+
void *result;
|
|
89
|
+
if (gvl) {
|
|
90
|
+
result = rb_thread_call_with_gvl(f, actionPtr );
|
|
91
|
+
} else {
|
|
92
|
+
result = rb_thread_call_without_gvl(f, actionPtr , RUBY_UBF_IO, nullptr);
|
|
93
|
+
}
|
|
94
|
+
std::unique_ptr<std::exception_ptr> exc(static_cast<std::exception_ptr *>(result));
|
|
95
|
+
if (exc && *exc) {
|
|
96
|
+
std::rethrow_exception(*exc);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
void withoutGvl(const std::function<void ()> &doAction)
|
|
103
|
+
{
|
|
104
|
+
changeGvl(doAction, false);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
void withGvl(const std::function<void ()> &doAction)
|
|
108
|
+
{
|
|
109
|
+
changeGvl(doAction, true);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
void fail(const char *errorClassName, const QString &message)
|
|
113
|
+
{
|
|
114
|
+
auto msg = message.toUtf8();
|
|
115
|
+
protect([&] {
|
|
116
|
+
rb_raise(rb_path2class(errorClassName), "%s", msg.data());
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
}
|