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,19 @@
1
+ #pragma once
2
+ #include "foreignclass.h"
3
+ #include "rubyvalue.h"
4
+
5
+ namespace RubyQml {
6
+
7
+ class AccessWrapper;
8
+
9
+ class AccessClass : public ForeignClass
10
+ {
11
+ public:
12
+ AccessClass(RubyValue className, RubyValue methodInfos, RubyValue signalInfos, RubyValue propertyInfos);
13
+
14
+ QVariant callMethod(ForeignObject *obj, size_t id, const QVariantList &args) override;
15
+ void setProperty(ForeignObject *obj, size_t id, const QVariant &value) override;
16
+ QVariant getProperty(ForeignObject *obj, size_t id) override;
17
+ };
18
+
19
+ } // namespace RubyQml
@@ -0,0 +1,30 @@
1
+ #include "accessobject.h"
2
+ #include "util.h"
3
+ #include "rubyclass.h"
4
+ #include <QSet>
5
+
6
+ namespace RubyQml {
7
+
8
+ AccessWrapper::AccessWrapper(const SP<ForeignMetaObject> &metaobj, RubyValue value) :
9
+ ForeignObject(metaobj),
10
+ mWrapped(value)
11
+ {
12
+ if (!value.isKindOf(RubyModule::fromPath("QML::Access"))) {
13
+ std::logic_error("wrapping non QML::Access object");
14
+ }
15
+ value.send("access_wrappers").send("push", RubyValue::fromQObject(this, false));
16
+ }
17
+
18
+ AccessWrapper::~AccessWrapper()
19
+ {
20
+ if (mWrapped.hasValue()) {
21
+ mWrapped.value().send("access_wrappers").send("delete", RubyValue::fromQObject(this, false));
22
+ }
23
+ }
24
+
25
+ void AccessWrapper::gc_mark()
26
+ {
27
+ rb_gc_mark(mWrapped.value());
28
+ }
29
+
30
+ } // namespace RubyQml
@@ -0,0 +1,22 @@
1
+ #pragma once
2
+ #include "weakvaluereference.h"
3
+ #include "foreignobject.h"
4
+ #include "markable.h"
5
+
6
+ namespace RubyQml {
7
+
8
+ class AccessWrapper : public ForeignObject, public Markable
9
+ {
10
+ public:
11
+ AccessWrapper(const SP<ForeignMetaObject> &metaobj, RubyValue wrappedValue);
12
+ ~AccessWrapper();
13
+
14
+ RubyValue wrappedValue() { return mWrapped.value(); }
15
+ void gc_mark() override;
16
+
17
+ private:
18
+ WeakValueReference mWrapped;
19
+ };
20
+
21
+ } // namespace RubyQml
22
+
@@ -0,0 +1,54 @@
1
+ #include "application.h"
2
+ #include "util.h"
3
+
4
+ namespace RubyQml {
5
+ namespace Application {
6
+
7
+ namespace {
8
+
9
+ int argc;
10
+ QList<QByteArray> argData;
11
+ char **argv;
12
+
13
+ QApplication *application_;
14
+ QQmlEngine *engine_;
15
+
16
+ }
17
+
18
+ void failIfUninitialized()
19
+ {
20
+ if (!initialized()) {
21
+ fail("QML::UninitializedError", "ruby-qml not yet initialized");
22
+ }
23
+ }
24
+
25
+ QApplication *application()
26
+ {
27
+ failIfUninitialized();
28
+ return application_;
29
+ }
30
+
31
+ QQmlEngine *engine()
32
+ {
33
+ failIfUninitialized();
34
+ return engine_;
35
+ }
36
+
37
+ bool initialized()
38
+ {
39
+ return application_;
40
+ }
41
+
42
+ void init(const QList<QByteArray> &args)
43
+ {
44
+ argc = args.size();
45
+ argData = args;
46
+ argv = new char*[argc];
47
+ std::transform(argData.begin(), argData.end(), argv, [](QByteArray &ba) { return ba.data(); });
48
+
49
+ application_ = new QApplication(argc, argv);
50
+ engine_ = new QQmlEngine();
51
+ }
52
+
53
+ }
54
+ } // namespace RubyQml
@@ -0,0 +1,17 @@
1
+ #pragma once
2
+
3
+ #include <QList>
4
+ #include <QApplication>
5
+ #include <QQmlEngine>
6
+
7
+ namespace RubyQml {
8
+ namespace Application {
9
+
10
+ QApplication *application();
11
+ QQmlEngine *engine();
12
+ bool initialized();
13
+
14
+ void init(const QList<QByteArray> &args);
15
+
16
+ }
17
+ } // namespace RubyQml
@@ -0,0 +1,18 @@
1
+ #pragma once
2
+ #include <memory>
3
+
4
+ namespace RubyQml {
5
+
6
+ template <typename T>
7
+ using SP = std::shared_ptr<T>;
8
+
9
+ template <typename T>
10
+ using WP = std::weak_ptr<T>;
11
+
12
+ template <typename T, typename ... Args>
13
+ inline SP<T> makeSP(Args && ... args)
14
+ {
15
+ return std::make_shared<T>(std::forward<Args>(args)...);
16
+ }
17
+
18
+ } // namespace RubyQml
@@ -0,0 +1,77 @@
1
+ #include "ext_accesssupport.h"
2
+ #include "ext_pointer.h"
3
+ #include "rubyclass.h"
4
+ #include "accessclass.h"
5
+ #include "accessobject.h"
6
+ #include "foreignmetaobject.h"
7
+ #include "qmltyperegisterer.h"
8
+
9
+ namespace RubyQml {
10
+ namespace Ext {
11
+
12
+ AccessWrapperFactory::AccessWrapperFactory(RubyValue self) :
13
+ self(self)
14
+ {
15
+ }
16
+
17
+ AccessWrapperFactory::~AccessWrapperFactory()
18
+ {
19
+ }
20
+
21
+ RubyValue AccessWrapperFactory::ext_initialize(RubyValue rubyClass, RubyValue className, RubyValue methodInfos, RubyValue signalInfos, RubyValue propertyInfos)
22
+ {
23
+ mRubyClass = rubyClass;
24
+ mAccessClass = makeSP<AccessClass>(className, methodInfos, signalInfos, propertyInfos);
25
+ mMetaObject = makeSP<ForeignMetaObject>(mAccessClass);
26
+ return self;
27
+ }
28
+
29
+ RubyValue AccessWrapperFactory::ext_emitSignal(RubyValue obj, RubyValue name, RubyValue args)
30
+ {
31
+ auto accessObj = wrapperRubyClass<Pointer>().unwrap(obj.send("access_object"))->fetchQObject();
32
+ auto nameId = name.toID();
33
+ auto argVariants = args.to<QVariantList>();
34
+ withoutGvl([&] {
35
+ mMetaObject->emitSignal(dynamic_cast<ForeignObject *>(accessObj), nameId, argVariants);
36
+ });
37
+ return Qnil;
38
+ }
39
+
40
+ RubyValue AccessWrapperFactory::ext_registerToQml(RubyValue path, RubyValue majorVersion, RubyValue minorVersion, RubyValue name)
41
+ {
42
+ using namespace std::placeholders;
43
+ if (!mTypeRegisterer) {
44
+ mTypeRegisterer = makeSP<QmlTypeRegisterer>(mMetaObject, std::bind(&AccessWrapperFactory::newInstanceInto, this, _1));
45
+ mTypeRegisterer->registerType(path.to<QByteArray>(), majorVersion.to<int>(), minorVersion.to<int>(), name.to<QByteArray>());
46
+ }
47
+ return self;
48
+ }
49
+
50
+ RubyValue AccessWrapperFactory::ext_create(RubyValue access)
51
+ {
52
+ return RubyValue::fromQObject(create(access), false);
53
+ }
54
+
55
+ AccessWrapper *AccessWrapperFactory::create(RubyValue access)
56
+ {
57
+ return new AccessWrapper(mMetaObject, access);
58
+ }
59
+
60
+ void AccessWrapperFactory::newInstanceInto(void *where)
61
+ {
62
+ withGvl([&] {
63
+ new(where) AccessWrapper(mMetaObject, mRubyClass.send("new"));
64
+ });
65
+ }
66
+
67
+ void AccessWrapperFactory::defineClass()
68
+ {
69
+ WrapperRubyClass<AccessWrapperFactory> klass(RubyModule::fromPath("QML"), "AccessWrapperFactory");
70
+ klass.defineMethod(MethodAccess::Protected, "initialize", RUBYQML_MEMBER_FUNCTION_INFO(&AccessWrapperFactory::ext_initialize));
71
+ klass.defineMethod("emit_signal", RUBYQML_MEMBER_FUNCTION_INFO(&AccessWrapperFactory::ext_emitSignal));
72
+ klass.defineMethod("register_to_qml", RUBYQML_MEMBER_FUNCTION_INFO(&AccessWrapperFactory::ext_registerToQml));
73
+ klass.defineMethod("create", RUBYQML_MEMBER_FUNCTION_INFO(&AccessWrapperFactory::ext_create));
74
+ }
75
+
76
+ } // namespace Ext
77
+ } // namespace RubyQml
@@ -0,0 +1,42 @@
1
+ #pragma once
2
+ #include "common.h"
3
+ #include "rubyvalue.h"
4
+
5
+ namespace RubyQml {
6
+
7
+ class AccessClass;
8
+ class ForeignMetaObject;
9
+ class QmlTypeRegisterer;
10
+ class AccessWrapper;
11
+
12
+ namespace Ext {
13
+
14
+ class AccessWrapperFactory
15
+ {
16
+ public:
17
+ AccessWrapperFactory(RubyValue self);
18
+ ~AccessWrapperFactory();
19
+
20
+ RubyValue ext_initialize(RubyValue rubyClass, RubyValue className, RubyValue methodInfos, RubyValue signalInfos, RubyValue propertyInfos);
21
+ RubyValue ext_emitSignal(RubyValue obj, RubyValue name, RubyValue args);
22
+ RubyValue ext_registerToQml(RubyValue path, RubyValue majorVersion, RubyValue minorVersion, RubyValue name);
23
+ RubyValue ext_create(RubyValue access);
24
+
25
+ AccessWrapper *create(RubyValue access);
26
+
27
+ void gc_mark() {}
28
+ static void defineClass();
29
+
30
+ private:
31
+
32
+ void newInstanceInto(void *where);
33
+
34
+ const RubyValue self;
35
+ RubyValue mRubyClass;
36
+ SP<AccessClass> mAccessClass;
37
+ SP<ForeignMetaObject> mMetaObject;
38
+ SP<QmlTypeRegisterer> mTypeRegisterer;
39
+ };
40
+
41
+ } // namespace Ext
42
+ } // namespace RubyQml
@@ -0,0 +1,39 @@
1
+ #include "ext_gcmarker.h"
2
+ #include "rubyclass.h"
3
+ #include <QtCore/QSet>
4
+
5
+ namespace RubyQml {
6
+ namespace Ext {
7
+
8
+ GCMarker::GCMarker(RubyValue self)
9
+ {
10
+ Q_UNUSED(self)
11
+ }
12
+
13
+ RubyValue GCMarker::fromMarkFunction(const std::function<void ()> &func)
14
+ {
15
+ auto klass = wrapperRubyClass<GCMarker>();
16
+ auto marker = klass.newInstance();
17
+ klass.unwrap(marker)->setMarkFunction(func);
18
+ return marker;
19
+ }
20
+
21
+ void GCMarker::setMarkFunction(const std::function<void ()> &func)
22
+ {
23
+ mMarkFunc = func;
24
+ }
25
+
26
+ void GCMarker::gc_mark()
27
+ {
28
+ if (mMarkFunc)
29
+ mMarkFunc();
30
+ }
31
+
32
+ void GCMarker::defineClass()
33
+ {
34
+ WrapperRubyClass<GCMarker> klass(RubyModule::fromPath("QML"), "GCProtection");
35
+ Q_UNUSED(klass);
36
+ }
37
+
38
+ } // namespace Ext
39
+ } // namespace RubyQml
@@ -0,0 +1,27 @@
1
+ #pragma once
2
+
3
+ #include "rubyvalue.h"
4
+ #include <functional>
5
+
6
+ namespace RubyQml {
7
+
8
+ namespace Ext {
9
+
10
+ class GCMarker
11
+ {
12
+ public:
13
+ GCMarker(RubyValue self);
14
+ static RubyValue fromMarkFunction(const std::function<void ()> &func);
15
+
16
+ void setMarkFunction(const std::function<void()> &func);
17
+ void gc_mark();
18
+
19
+ static void defineClass();
20
+
21
+ private:
22
+ std::function<void ()> mMarkFunc;
23
+ };
24
+
25
+ } // namespace Ext
26
+
27
+ } // namespace RubyQml
@@ -0,0 +1,62 @@
1
+ #include "ext_kernel.h"
2
+ #include "application.h"
3
+ #include "rubyvalue.h"
4
+ #include "rubyclass.h"
5
+
6
+ namespace RubyQml {
7
+ namespace Ext {
8
+ namespace Kernel {
9
+
10
+ namespace {
11
+
12
+ RubyValue application(RubyValue self)
13
+ {
14
+ Q_UNUSED(self);
15
+ return RubyValue::from(Application::application());
16
+ }
17
+
18
+ RubyValue engine(RubyValue self)
19
+ {
20
+ Q_UNUSED(self);
21
+ return RubyValue::from(Application::engine());
22
+ }
23
+
24
+ RubyValue applicationMetaObject()
25
+ {
26
+ return RubyValue::from(&QApplication::staticMetaObject);
27
+ }
28
+
29
+ RubyValue engineMetaObject()
30
+ {
31
+ return RubyValue::from(&QQmlEngine::staticMetaObject);
32
+ }
33
+
34
+ RubyValue init(RubyValue self, RubyValue argv)
35
+ {
36
+ Q_UNUSED(self);
37
+ Application::init(argv.to<QList<QByteArray>>());
38
+ return Qnil;
39
+ }
40
+
41
+ RubyValue initialized(RubyValue self)
42
+ {
43
+ Q_UNUSED(self);
44
+ return RubyValue::from(Application::initialized());
45
+ }
46
+
47
+ }
48
+
49
+ void defineModule()
50
+ {
51
+ RubyModule kernel(RubyModule::fromPath("QML"), "Kernel");
52
+ kernel.defineModuleFunction("application", RUBYQML_FUNCTION_INFO(&application));
53
+ kernel.defineModuleFunction("engine", RUBYQML_FUNCTION_INFO(&engine));
54
+ kernel.defineModuleFunction("application_meta_object", RUBYQML_FUNCTION_INFO(&applicationMetaObject));
55
+ kernel.defineModuleFunction("engine_meta_object", RUBYQML_FUNCTION_INFO(&engineMetaObject));
56
+ kernel.defineModuleFunction("init", RUBYQML_FUNCTION_INFO(&init));
57
+ kernel.defineModuleFunction("initialized?", RUBYQML_FUNCTION_INFO(&initialized));
58
+ }
59
+
60
+ } // namespace Kernel
61
+ } // namespace Ext
62
+ } // namespace RubyQml
@@ -0,0 +1,11 @@
1
+ #pragma once
2
+
3
+ namespace RubyQml {
4
+ namespace Ext {
5
+ namespace Kernel {
6
+
7
+ void defineModule();
8
+
9
+ } // namespace Kernel
10
+ } // namespace Ext
11
+ } // namespace RubyQml
@@ -0,0 +1,410 @@
1
+ #include "ext_metaobject.h"
2
+ #include "util.h"
3
+ #include "ext_pointer.h"
4
+ #include "rubyclass.h"
5
+ #include "signalforwarder.h"
6
+ #include <QtCore/QMetaObject>
7
+ #include <QtCore/QMetaMethod>
8
+ #include <QtCore/QMetaProperty>
9
+ #include <QtCore/QMetaEnum>
10
+ #include <QtCore/QVariant>
11
+ #include <array>
12
+ #include <QtCore/QDebug>
13
+ #include <QtQml/QQmlEngine>
14
+
15
+ namespace RubyQml {
16
+ namespace Ext {
17
+
18
+ namespace {
19
+
20
+ RubyValue idListToArray(const QList<ID> &xs)
21
+ {
22
+ return protect([&] {
23
+ auto ary = rb_ary_new();
24
+ for (ID id : xs) {
25
+ rb_ary_push(ary, ID2SYM(id));
26
+ }
27
+ return ary;
28
+ });
29
+ }
30
+
31
+ }
32
+
33
+ MetaObject::MetaObject(RubyValue self) :
34
+ self(self)
35
+ {
36
+ setMetaObject(&QObject::staticMetaObject);
37
+ }
38
+
39
+ RubyValue MetaObject::className() const
40
+ {
41
+ return rb_str_new_cstr(mMetaObject->className());
42
+ }
43
+
44
+ RubyValue MetaObject::methodNames() const
45
+ {
46
+ return idListToArray(mMethodHash.keys());
47
+ }
48
+
49
+ RubyValue MetaObject::isPublic(RubyValue name) const
50
+ {
51
+ auto methods = findMethods(name);
52
+ return RubyValue::from(mMetaObject->method(methods.first()).access() == QMetaMethod::Public);
53
+ }
54
+ RubyValue MetaObject::isProtected(RubyValue name) const
55
+ {
56
+ auto methods = findMethods(name);
57
+ return RubyValue::from(mMetaObject->method(methods.first()).access() == QMetaMethod::Protected);
58
+ }
59
+ RubyValue MetaObject::isPrivate(RubyValue name) const
60
+ {
61
+ auto methods = findMethods(name);
62
+ return RubyValue::from(mMetaObject->method(methods.first()).access() == QMetaMethod::Private);
63
+ }
64
+ RubyValue MetaObject::isSignal(RubyValue name) const
65
+ {
66
+ auto methods = findMethods(name);
67
+ return RubyValue::from(mMetaObject->method(methods.first()).methodType() == QMetaMethod::Signal);
68
+ }
69
+
70
+ class MethodInvoker
71
+ {
72
+ public:
73
+ MethodInvoker(RubyValue args, const QMetaMethod &method) :
74
+ mArgs(args), mMethod(method) {}
75
+
76
+ bool isArgsCompatible() const
77
+ {
78
+ int count = RARRAY_LEN(VALUE(mArgs));
79
+ if (mMethod.parameterCount() != count) {
80
+ return false;
81
+ }
82
+ for (int i = 0; i < count; ++i) {
83
+ auto metaType = mMethod.parameterType(i);
84
+ RubyValue arg = RARRAY_AREF(VALUE(mArgs), i);
85
+ if (!arg.isConvertibleTo(metaType)) {
86
+ return false;
87
+ }
88
+ }
89
+ return true;
90
+ }
91
+
92
+ RubyValue invoke(QObject *obj)
93
+ {
94
+ std::array<QVariant, 10> argVariants;
95
+ std::array<QGenericArgument, 10> args;
96
+ for (int i = 0; i < mMethod.parameterCount(); ++i) {
97
+ auto metaType = mMethod.parameterType(i);
98
+ argVariants[i] = RubyValue(RARRAY_AREF(VALUE(mArgs), i)).toVariant(metaType);
99
+ args[i] = QGenericArgument(QMetaType::typeName(metaType), argVariants[i].data());
100
+ }
101
+
102
+ int returnType = mMethod.returnType();
103
+ if (returnType == QMetaType::UnknownType) {
104
+ fail("QML::MethodError", "unknown return metatype");
105
+ }
106
+ bool voidReturning = (returnType == QMetaType::Void);
107
+ QVariant returnValue;
108
+ if (!voidReturning) {
109
+ returnValue = QVariant(returnType, QMetaType::create(returnType));
110
+ }
111
+
112
+ bool result;
113
+ withoutGvl([&] {
114
+ result = mMethod.invoke(
115
+ obj,
116
+ Qt::DirectConnection,
117
+ QGenericReturnArgument(QMetaType::typeName(returnType), returnValue.data()),
118
+ args[0],args[1],args[2],args[3],args[4],
119
+ args[5],args[6],args[7],args[8],args[9]);
120
+ });
121
+
122
+ if (!result) {
123
+ QString error;
124
+ QDebug(&error) << "failed to call method" << mMethod.methodSignature();
125
+ fail("QML::MethodError", error);
126
+ }
127
+ if (voidReturning) {
128
+ return Qnil;
129
+ } else {
130
+ auto ret = RubyValue::from(returnValue);
131
+ static auto objectBaseClass = RubyClass::fromPath("QML::QtObjectBase");
132
+ if (ret.isKindOf(objectBaseClass)) {
133
+ auto pointer = wrapperRubyClass<Pointer>().unwrap(ret.send("pointer"));
134
+ pointer->preferManaged(true);
135
+ }
136
+ return ret;
137
+ }
138
+ }
139
+ private:
140
+ RubyValue mArgs;
141
+ QMetaMethod mMethod;
142
+ };
143
+
144
+ RubyValue MetaObject::invokeMethod(RubyValue object, RubyValue methodName, RubyValue args) const
145
+ {
146
+ checkThread();
147
+
148
+ auto methodIndexes = findMethods(methodName);
149
+
150
+ protect([&] {
151
+ args = rb_check_array_type(args);
152
+ });
153
+ auto obj = wrapperRubyClass<Pointer>().unwrap(object)->fetchQObject();
154
+ for (int i : methodIndexes) {
155
+ MethodInvoker invoker(args, mMetaObject->method(i));
156
+ if (invoker.isArgsCompatible()) {
157
+ return invoker.invoke(obj);
158
+ }
159
+ }
160
+ protect([&] {
161
+ auto to_class = rb_funcall(ID2SYM(rb_intern("class")), rb_intern("to_proc"), 0);
162
+ auto classes = rb_funcall_with_block(args, rb_intern("map"), 0, nullptr, to_class);
163
+ auto classes_str = rb_funcall(classes, rb_intern("to_s"), 0);
164
+
165
+ rb_raise(rb_path2class("QML::MethodError"),
166
+ "method mismatch (%s with params %s in %s)",
167
+ mMetaObject->method(methodIndexes.first()).name().data(),
168
+ StringValueCStr(classes_str),
169
+ mMetaObject->className());
170
+ });
171
+ return Qnil;
172
+ }
173
+
174
+ RubyValue MetaObject::connectSignal(RubyValue object, RubyValue signalName, RubyValue proc) const
175
+ {
176
+ auto id = signalName.toID();
177
+ auto obj = wrapperRubyClass<Pointer>().unwrap(object)->fetchQObject();
178
+
179
+ proc = proc.send("to_proc");
180
+
181
+ auto methodIndexes = mMethodHash.values(id);
182
+ std::reverse(methodIndexes.begin(), methodIndexes.end());
183
+
184
+ for (int i : methodIndexes) {
185
+ auto method = mMetaObject->method(i);
186
+ if (method.methodType() != QMetaMethod::Signal) {
187
+ continue;
188
+ }
189
+ new SignalForwarder(obj, method, proc);
190
+ return Qnil;
191
+ }
192
+ protect([&] {
193
+ rb_raise(rb_path2class("QML::MethodError"),
194
+ "signal not found (%s in %s)",
195
+ rb_id2name(id), mMetaObject->className());
196
+ });
197
+ return Qnil;
198
+ }
199
+
200
+ RubyValue MetaObject::propertyNames() const
201
+ {
202
+ return idListToArray(mPropertyHash.keys());
203
+ }
204
+
205
+ RubyValue MetaObject::getProperty(RubyValue object, RubyValue name) const
206
+ {
207
+ checkThread();
208
+
209
+ auto metaProperty = mMetaObject->property(findProperty(name));
210
+
211
+ auto qobj = wrapperRubyClass<Pointer>().unwrap(object)->fetchQObject();
212
+ QVariant result;
213
+ withoutGvl([&] {
214
+ result = metaProperty.read(qobj);
215
+ });
216
+ return RubyValue::from(result);
217
+ }
218
+
219
+ RubyValue MetaObject::setProperty(RubyValue object, RubyValue name, RubyValue newValue) const
220
+ {
221
+ checkThread();
222
+
223
+ auto metaProperty = mMetaObject->property(findProperty(name));
224
+ if (!newValue.isConvertibleTo(metaProperty.userType())) {
225
+ protect([&] {
226
+ rb_raise(rb_path2class("QML::PropertyError"),
227
+ "type mismatch (%s for %s)",
228
+ rb_obj_classname(newValue), metaProperty.typeName());
229
+ });
230
+ }
231
+
232
+ auto qobj = wrapperRubyClass<Pointer>().unwrap(object)->fetchQObject();
233
+ auto variant = newValue.to<QVariant>();
234
+ QVariant result;
235
+ withoutGvl([&] {
236
+ metaProperty.write(qobj, variant);
237
+ result = metaProperty.read(qobj);
238
+ });
239
+ return RubyValue::from(result);
240
+ }
241
+
242
+ RubyValue MetaObject::notifySignal(RubyValue name) const
243
+ {
244
+ auto metaProperty = mMetaObject->property(findProperty(name));
245
+ auto signal = metaProperty.notifySignal();
246
+ if (signal.isValid()) {
247
+ return ID2SYM(rb_intern(metaProperty.notifySignal().name()));
248
+ }
249
+ else {
250
+ return Qnil;
251
+ }
252
+ }
253
+
254
+ void MetaObject::checkThread() const
255
+ {
256
+ if (rb_thread_current() != rb_thread_main()) {
257
+ fail("QML::InvalidThreadError", "Qt object accessed from non-main thread");
258
+ }
259
+ }
260
+
261
+ QList<int> MetaObject::findMethods(RubyValue name) const
262
+ {
263
+ auto id = name.toID();
264
+ auto methodIndexes = mMethodHash.values(id);
265
+ if (methodIndexes.size() == 0) {
266
+ protect([&] {
267
+ rb_raise(rb_path2class("QML::MethodError"),
268
+ "method not found (%s in %s)",
269
+ rb_id2name(id),
270
+ mMetaObject->className());
271
+ });
272
+ }
273
+ return methodIndexes;
274
+ }
275
+
276
+ int MetaObject::findProperty(RubyValue name) const
277
+ {
278
+ auto id = name.toID();
279
+ if (!mPropertyHash.contains(id)) {
280
+ protect([&] {
281
+ rb_raise(rb_path2class("QML::PropertyError"),
282
+ "property not found (%s in %s)",
283
+ rb_id2name(id), mMetaObject->className());
284
+ });
285
+ }
286
+ return mPropertyHash[id];
287
+ }
288
+
289
+ RubyValue MetaObject::enumerators() const
290
+ {
291
+ QHash<QByteArray, QHash<QByteArray, int>> enums;
292
+
293
+ for (int i = mMetaObject->enumeratorOffset(); i < mMetaObject->enumeratorCount(); ++i) {
294
+ auto metaEnum = mMetaObject->enumerator(i);
295
+ if (metaEnum.isFlag()) {
296
+ continue;
297
+ }
298
+
299
+ QHash<QByteArray, int> enumHash;
300
+ for (int j = 0; j < metaEnum.keyCount(); ++j) {
301
+ enumHash[metaEnum.key(j)] = metaEnum.value(j);
302
+ }
303
+ enums[metaEnum.name()] = enumHash;
304
+ }
305
+
306
+ return RubyValue::from(enums);
307
+ }
308
+
309
+ RubyValue MetaObject::superClass() const
310
+ {
311
+ auto superclass = mMetaObject->superClass();
312
+ if (!superclass) {
313
+ return Qnil;
314
+ }
315
+ return fromMetaObject(superclass);
316
+ }
317
+
318
+ RubyValue MetaObject::isEqual(RubyValue other) const
319
+ {
320
+ return RubyValue::from(mMetaObject == wrapperRubyClass<MetaObject>().unwrap(other)->mMetaObject);
321
+ }
322
+
323
+ RubyValue MetaObject::hash() const
324
+ {
325
+ return RubyValue::from(reinterpret_cast<size_t>(mMetaObject)).send("hash");
326
+ }
327
+
328
+ void MetaObject::setMetaObject(const QMetaObject *metaObject)
329
+ {
330
+ int methodCount = metaObject->methodCount() - metaObject->methodOffset();
331
+
332
+ QMultiHash<ID, int> methodHash;
333
+ QHash<ID, int> propertyHash;
334
+
335
+ for (int i = 0; i < methodCount; ++i) {
336
+
337
+ auto index = i + metaObject->methodOffset();
338
+ auto method = metaObject->method(index);
339
+
340
+ if (method.methodType() == QMetaMethod::Constructor) {
341
+ continue;
342
+ }
343
+
344
+ methodHash.insert(rb_intern(method.name()), index);
345
+ }
346
+
347
+ int propertyCount = metaObject->propertyCount() - metaObject->propertyOffset();
348
+
349
+ for (int i = 0; i < propertyCount; ++i) {
350
+ auto index = i + metaObject->propertyOffset();
351
+ auto property = metaObject->property(index);
352
+ propertyHash[rb_intern(property.name())] = index;
353
+ }
354
+
355
+ int enumCount = metaObject->enumeratorCount() - metaObject->enumeratorOffset();
356
+ for (int i = 0; i < enumCount; ++i) {
357
+ auto index = i + metaObject->enumeratorOffset();
358
+ auto metaEnum = metaObject->enumerator(index);
359
+ auto typeName = QByteArray(metaObject->className()) + "::" + QByteArray(metaEnum.name());
360
+ auto metaType = QMetaType::type(typeName);
361
+ if (metaType != QMetaType::UnknownType) {
362
+ RubyValue::addEnumeratorMetaType(metaType);
363
+ }
364
+ }
365
+
366
+ mMetaObject = metaObject;
367
+ mMethodHash = methodHash;
368
+ mPropertyHash = propertyHash;
369
+ }
370
+
371
+ RubyValue MetaObject::fromMetaObject(const QMetaObject *metaObject)
372
+ {
373
+ auto klass = wrapperRubyClass<MetaObject>();
374
+ auto value = klass.newInstance();
375
+ klass.unwrap(value)->setMetaObject(metaObject);
376
+ return value;
377
+ }
378
+
379
+ void MetaObject::defineClass()
380
+ {
381
+ WrapperRubyClass<MetaObject> klass(RubyModule::fromPath("QML"), "MetaObject");
382
+
383
+ klass.defineMethod("name", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::className));
384
+
385
+ klass.defineMethod("method_names", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::methodNames));
386
+ klass.defineMethod("public?", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::isPublic));
387
+ klass.defineMethod("protected?", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::isProtected));
388
+ klass.defineMethod("private?", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::isPrivate));
389
+ klass.defineMethod("signal?", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::isSignal));
390
+
391
+ klass.defineMethod("invoke_method", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::invokeMethod));
392
+ klass.defineMethod("connect_signal", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::connectSignal));
393
+
394
+ klass.defineMethod("property_names", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::propertyNames));
395
+ klass.defineMethod("get_property", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::getProperty));
396
+ klass.defineMethod("set_property", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::setProperty));
397
+ klass.defineMethod("notify_signal", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::notifySignal));
398
+
399
+ klass.defineMethod("enumerators", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::enumerators));
400
+
401
+ klass.defineMethod("super_class", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::superClass));
402
+
403
+ klass.defineMethod("==", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::isEqual));
404
+ klass.defineMethod("hash", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::hash));
405
+
406
+ klass.aliasMethod("eql?", "==");
407
+ }
408
+
409
+ } // namespace Ext
410
+ } // namespace RubyQml