qml 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -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
|
data/ext/qml/common.h
ADDED
@@ -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,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
|