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/util.h
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <QMetaType>
|
|
4
|
+
#include <functional>
|
|
5
|
+
#include <type_traits>
|
|
6
|
+
#include <array>
|
|
7
|
+
#include <ruby.h>
|
|
8
|
+
|
|
9
|
+
#ifndef RARRAY_AREF
|
|
10
|
+
#define RARRAY_AREF(a, i) ((RARRAY_PTR(a)[i]))
|
|
11
|
+
#endif
|
|
12
|
+
|
|
13
|
+
namespace RubyQml {
|
|
14
|
+
|
|
15
|
+
template <size_t... Indices>
|
|
16
|
+
struct IntegerSequence {};
|
|
17
|
+
|
|
18
|
+
template <size_t N, typename Seq = IntegerSequence<>, bool Last = N == 0>
|
|
19
|
+
struct MakeIntegerSequenceImpl
|
|
20
|
+
{
|
|
21
|
+
using type = Seq;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
template <size_t N, size_t... Indices>
|
|
25
|
+
struct MakeIntegerSequenceImpl<N, IntegerSequence<Indices...>, false> :
|
|
26
|
+
MakeIntegerSequenceImpl<N - 1, IntegerSequence<N - 1, Indices...>>
|
|
27
|
+
{};
|
|
28
|
+
|
|
29
|
+
template <size_t N>
|
|
30
|
+
using MakeIntegerSequence = typename MakeIntegerSequenceImpl<N>::type;
|
|
31
|
+
|
|
32
|
+
template <typename F, typename... Args, size_t... Indices>
|
|
33
|
+
typename std::result_of<F(Args...)>::type
|
|
34
|
+
applyWithTupleImpl(const F &func, const std::tuple<Args...> &args, IntegerSequence<Indices...>)
|
|
35
|
+
{
|
|
36
|
+
return func(std::get<Indices>(args)...);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
template <typename F, typename... Args>
|
|
40
|
+
typename std::result_of<F(Args...)>::type
|
|
41
|
+
applyWithTuple(const F &func, const std::tuple<Args...> &args)
|
|
42
|
+
{
|
|
43
|
+
return applyWithTupleImpl(func, args, MakeIntegerSequence<sizeof...(Args)>());
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
class RubyValue;
|
|
47
|
+
|
|
48
|
+
class RubyException
|
|
49
|
+
{
|
|
50
|
+
public:
|
|
51
|
+
RubyException(int state) : mState(state) {}
|
|
52
|
+
int state() const { return mState; }
|
|
53
|
+
private:
|
|
54
|
+
int mState = 0;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
// Convert Ruby exceptions into C++ exceptions (RubyException)
|
|
59
|
+
void protect(const std::function<void()> &doAction);
|
|
60
|
+
|
|
61
|
+
template <typename F>
|
|
62
|
+
typename std::enable_if<
|
|
63
|
+
!std::is_same<typename std::result_of<F()>::type, void>::value,
|
|
64
|
+
typename std::result_of<F()>::type>::type
|
|
65
|
+
protect(const F &doAction)
|
|
66
|
+
{
|
|
67
|
+
typename std::result_of<F()>::type ret;
|
|
68
|
+
protect([&] {
|
|
69
|
+
ret = doAction();
|
|
70
|
+
});
|
|
71
|
+
return ret;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Regenerate Ruby exceptions that are converted into RubyException
|
|
75
|
+
// and convert std::exception exceptions into Ruby errors.
|
|
76
|
+
// Other C++ exceptions are not allowed to be thrown out of this function.
|
|
77
|
+
void unprotect(const std::function<void()> &doAction) noexcept;
|
|
78
|
+
|
|
79
|
+
void rescue(const std::function<void ()> &doAction, const std::function<void (RubyValue)> &handleException);
|
|
80
|
+
|
|
81
|
+
// call function with GVL unlocked
|
|
82
|
+
void withoutGvl(const std::function<void()> &doAction);
|
|
83
|
+
|
|
84
|
+
// call function with GVL locked
|
|
85
|
+
void withGvl(const std::function<void()> &doAction);
|
|
86
|
+
|
|
87
|
+
void fail(const char *errorClassName, const QString &message);
|
|
88
|
+
|
|
89
|
+
template <typename ... TArgs>
|
|
90
|
+
void callSuper(TArgs ... args)
|
|
91
|
+
{
|
|
92
|
+
constexpr int argc = sizeof...(args);
|
|
93
|
+
std::array<VALUE, argc> argv = {{ VALUE(args)... }};
|
|
94
|
+
protect([&] {
|
|
95
|
+
rb_call_super(argc, argv.data());
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
} // namespace RubyQml
|
|
100
|
+
|
|
101
|
+
Q_DECLARE_METATYPE(const QMetaObject*)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#include "valuereference.h"
|
|
2
|
+
#include <QSet>
|
|
3
|
+
|
|
4
|
+
namespace RubyQml {
|
|
5
|
+
|
|
6
|
+
using ReferenceCounts = QHash<RubyValue, int>;
|
|
7
|
+
Q_GLOBAL_STATIC(ReferenceCounts, referenceCounts)
|
|
8
|
+
|
|
9
|
+
struct ValueReference::Owner
|
|
10
|
+
{
|
|
11
|
+
Owner(RubyValue value) :
|
|
12
|
+
value(value)
|
|
13
|
+
{
|
|
14
|
+
auto &refCounts = *referenceCounts;
|
|
15
|
+
if (!refCounts.contains(value)) {
|
|
16
|
+
refCounts[value] = 0;
|
|
17
|
+
}
|
|
18
|
+
refCounts[value] += 1;
|
|
19
|
+
}
|
|
20
|
+
~Owner()
|
|
21
|
+
{
|
|
22
|
+
auto &refCounts = *referenceCounts;
|
|
23
|
+
refCounts[value] -= 1;
|
|
24
|
+
if (refCounts[value] == 0) {
|
|
25
|
+
refCounts.remove(value);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
RubyValue value;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
ValueReference::ValueReference(RubyValue value) :
|
|
32
|
+
mOwner(makeSP<Owner>(value))
|
|
33
|
+
{
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
RubyValue ValueReference::value() const
|
|
37
|
+
{
|
|
38
|
+
return mOwner->value;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
void ValueReference::markAllReferences()
|
|
42
|
+
{
|
|
43
|
+
auto begin = referenceCounts->begin();
|
|
44
|
+
auto end = referenceCounts->end();
|
|
45
|
+
for (auto i = begin; i != end; ++i) {
|
|
46
|
+
rb_gc_mark(i.key());
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
} // namespace RubyQml
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#include "rubyvalue.h"
|
|
3
|
+
#include "common.h"
|
|
4
|
+
|
|
5
|
+
namespace RubyQml {
|
|
6
|
+
|
|
7
|
+
class ValueReference
|
|
8
|
+
{
|
|
9
|
+
public:
|
|
10
|
+
ValueReference() = default;
|
|
11
|
+
ValueReference(RubyValue value);
|
|
12
|
+
|
|
13
|
+
RubyValue value() const;
|
|
14
|
+
|
|
15
|
+
static void markAllReferences();
|
|
16
|
+
|
|
17
|
+
private:
|
|
18
|
+
struct Owner;
|
|
19
|
+
SP<Owner> mOwner;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
} // namespace RubyQml
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#include "weakvaluereference.h"
|
|
2
|
+
#include "rubyclass.h"
|
|
3
|
+
|
|
4
|
+
namespace RubyQml {
|
|
5
|
+
|
|
6
|
+
WeakValueReference::WeakValueReference(RubyValue value) :
|
|
7
|
+
mHasValue(true),
|
|
8
|
+
mValue(value)
|
|
9
|
+
{
|
|
10
|
+
static auto objspace = RubyModule::fromPath("ObjectSpace");
|
|
11
|
+
protect([&] {
|
|
12
|
+
auto proc = rb_proc_new((VALUE (*)(...))&finalize, VALUE(this));
|
|
13
|
+
VALUE args[] = { value };
|
|
14
|
+
rb_funcall_with_block(objspace, RUBYQML_INTERN("define_finalizer"), 1, args, proc);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
VALUE WeakValueReference::finalize(VALUE arg, VALUE data)
|
|
19
|
+
{
|
|
20
|
+
Q_UNUSED(arg);
|
|
21
|
+
auto reference = (WeakValueReference *)data;
|
|
22
|
+
reference->mHasValue = false;
|
|
23
|
+
reference->mValue = Qnil;
|
|
24
|
+
return Qnil;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
} // namespace RubyQml
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#include "rubyvalue.h"
|
|
3
|
+
|
|
4
|
+
namespace RubyQml {
|
|
5
|
+
|
|
6
|
+
class WeakValueReference
|
|
7
|
+
{
|
|
8
|
+
public:
|
|
9
|
+
WeakValueReference(RubyValue value);
|
|
10
|
+
|
|
11
|
+
bool hasValue() const { return mHasValue; }
|
|
12
|
+
RubyValue value() const { return mValue; }
|
|
13
|
+
private:
|
|
14
|
+
static VALUE finalize(VALUE arg, VALUE data);
|
|
15
|
+
bool mHasValue;
|
|
16
|
+
RubyValue mValue;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
} // namespace RubyQml
|
data/lib/qml.rb
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require 'qml/version'
|
|
2
|
+
require 'qml/qml'
|
|
3
|
+
|
|
4
|
+
require 'qml/geometry'
|
|
5
|
+
require 'qml/reactive'
|
|
6
|
+
|
|
7
|
+
require 'qml/meta_object'
|
|
8
|
+
require 'qml/plugin_loader'
|
|
9
|
+
require 'qml/component'
|
|
10
|
+
require 'qml/engine'
|
|
11
|
+
require 'qml/context'
|
|
12
|
+
require 'qml/application'
|
|
13
|
+
require 'qml/image_provider'
|
|
14
|
+
require 'qml/qt'
|
|
15
|
+
require 'qml/access'
|
|
16
|
+
require 'qml/root_path'
|
|
17
|
+
require 'qml/dispatcher'
|
|
18
|
+
require 'qml/dispatchable'
|
|
19
|
+
|
|
20
|
+
require 'qml/data'
|
|
21
|
+
require 'qml/test_util'
|
|
22
|
+
|
|
23
|
+
module QML
|
|
24
|
+
|
|
25
|
+
def initialized?
|
|
26
|
+
Kernel.initialized?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Initializes ruby-qml.
|
|
30
|
+
# @param [Boolean] offscreen set this to true to run application offscreen (without GUI)
|
|
31
|
+
def init(offscreen: false)
|
|
32
|
+
fail AlreadyInitializedError, "ruby-qml already initialized" if initialized?
|
|
33
|
+
argv = [$PROGRAM_NAME]
|
|
34
|
+
argv += %w{-platform offscreen} if offscreen
|
|
35
|
+
Kernel.init(argv)
|
|
36
|
+
application.events_processed.each do
|
|
37
|
+
Dispatcher.instance.run_tasks
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
module_function :initialized?, :init
|
|
41
|
+
end
|
data/lib/qml/access.rb
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
require 'ostruct'
|
|
2
|
+
require 'qml/qml'
|
|
3
|
+
require 'qml/dispatchable'
|
|
4
|
+
require 'qml/wrappable'
|
|
5
|
+
|
|
6
|
+
module QML
|
|
7
|
+
|
|
8
|
+
# {Access} enables classes to be exposed to QML.
|
|
9
|
+
#
|
|
10
|
+
module Access
|
|
11
|
+
include Dispatchable
|
|
12
|
+
include Wrappable
|
|
13
|
+
# @!parse include Reactive::Object
|
|
14
|
+
# @!parse include SignalInitialization
|
|
15
|
+
# @!parse extend ClassMethods
|
|
16
|
+
|
|
17
|
+
def self.included(derived)
|
|
18
|
+
derived.class_eval do
|
|
19
|
+
include Reactive::Object
|
|
20
|
+
include SignalInitialization
|
|
21
|
+
extend ClassMethods
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @api private
|
|
26
|
+
def self.unregistered_classes
|
|
27
|
+
@unregistered_classes ||= []
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @api private
|
|
31
|
+
def self.register_classes
|
|
32
|
+
unregistered_classes.each(&:register_to_qml_real)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# allowed name patterns for exposed method names
|
|
36
|
+
ALLOWED_PATTERN = /^[a-zA-Z_]\w*$/
|
|
37
|
+
|
|
38
|
+
module ClassMethods
|
|
39
|
+
|
|
40
|
+
# Registers the class as a QML type.
|
|
41
|
+
# @param under [String|nil] the namespece which encapsulates the exported QML type. If not specified, automatically inferred from the module nesting of the class.
|
|
42
|
+
# @param version [String|nil] the version of the type. Defaults to VERSION constant of the encapsulating module / class of the class.
|
|
43
|
+
# @param name [String|nil] the name of the type. Defaults to the name of the class.
|
|
44
|
+
def register_to_qml(under: nil, version: nil, name: nil)
|
|
45
|
+
if !under || !version || !name
|
|
46
|
+
path = self.name.split('::')
|
|
47
|
+
end
|
|
48
|
+
if !under && !version
|
|
49
|
+
fail AccessError, "cannot guess namespace of toplevel class '#{self.name}'" if path.size == 1
|
|
50
|
+
encapsulatings = path[0, path.size - 1]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
under ||= encapsulatings.join('.')
|
|
54
|
+
version ||= eval("::#{encapsulatings.join('::')}").const_get(:VERSION)
|
|
55
|
+
versions = version.split('.').map(&method(:Integer))
|
|
56
|
+
fail AccessError, 'insufficient version (major and minor versions required)' unless versions.size >= 2
|
|
57
|
+
name ||= path.last
|
|
58
|
+
|
|
59
|
+
@qml_registeration = {
|
|
60
|
+
under: under,
|
|
61
|
+
version_major: versions[0],
|
|
62
|
+
version_minor: versions[1],
|
|
63
|
+
name: name
|
|
64
|
+
}
|
|
65
|
+
Access.unregistered_classes << self
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# @api private
|
|
69
|
+
def register_to_qml_real
|
|
70
|
+
access_wrapper_factory.register_to_qml(
|
|
71
|
+
@qml_registeration[:under],
|
|
72
|
+
@qml_registeration[:version_major],
|
|
73
|
+
@qml_registeration[:version_minor],
|
|
74
|
+
@qml_registeration[:name])
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# @api private
|
|
78
|
+
def access_wrapper_factory
|
|
79
|
+
@access_wrapper_factory ||= create_access_wrapper_factory
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
def create_access_wrapper_factory
|
|
85
|
+
classname = "RubyQml::Access::#{name}"
|
|
86
|
+
|
|
87
|
+
signals = instance_signals.grep(ALLOWED_PATTERN)
|
|
88
|
+
properties = instance_properties.grep(ALLOWED_PATTERN)
|
|
89
|
+
|
|
90
|
+
signal_infos = signals
|
|
91
|
+
.map { |name| instance_signal(name) }
|
|
92
|
+
.reject(&:variadic?)
|
|
93
|
+
.map { |signal| OpenStruct.new(name: signal.name, params: signal.parameters.map(&:last)) }
|
|
94
|
+
|
|
95
|
+
property_infos = properties
|
|
96
|
+
.map { |name| OpenStruct.new(name: name, getter: name, setter: :"#{name}=", notifier: :"#{name}_changed") }
|
|
97
|
+
|
|
98
|
+
methods = ancestors.take_while { |k| k.include?(Access) }
|
|
99
|
+
.map { |k| k.instance_methods(false) }.inject(&:|)
|
|
100
|
+
.grep(ALLOWED_PATTERN)
|
|
101
|
+
ignored_methods = signals | property_infos.flat_map { |p| [p.getter, p.setter, p.notifier] }
|
|
102
|
+
method_infos = (methods - ignored_methods)
|
|
103
|
+
.map { |name| instance_method(name) }
|
|
104
|
+
.select { |method| method.parameters.all? { |param| param[0] == :req } }
|
|
105
|
+
.map { |method| OpenStruct.new(name: method.name, params: method.parameters.map(&:last)) }
|
|
106
|
+
|
|
107
|
+
AccessWrapperFactory.new(self, classname, method_infos, signal_infos, property_infos)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
module SignalInitialization
|
|
112
|
+
def initialize(*args, &block)
|
|
113
|
+
super
|
|
114
|
+
signal_names = signals + properties.map { |name| :"#{name}_changed" }
|
|
115
|
+
signal_names.each do |name|
|
|
116
|
+
__send__(name).connect do |*args|
|
|
117
|
+
@access_wrappers.each do |obj|
|
|
118
|
+
obj.class.meta_object.invoke_method(obj.pointer, name, args)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# @api private
|
|
126
|
+
attr_reader :access_wrappers
|
|
127
|
+
|
|
128
|
+
def initialize(*args, &block)
|
|
129
|
+
super
|
|
130
|
+
@access_wrappers = []
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def create_wrapper
|
|
134
|
+
self.class.access_wrapper_factory.create(self)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
require 'qml/qt_classes'
|
|
2
|
+
|
|
3
|
+
module QML
|
|
4
|
+
# @!parse class Application < QtObjectBase; end
|
|
5
|
+
|
|
6
|
+
# {Application} represents a Qt application instance.
|
|
7
|
+
# It provides the event loop and manages Application-level configurations.
|
|
8
|
+
#
|
|
9
|
+
# @see http://qt-project.org/doc/qt-5/qapplication.html QApplication (C++)
|
|
10
|
+
class Application
|
|
11
|
+
|
|
12
|
+
# @return [Application] The application instance.
|
|
13
|
+
def self.instance
|
|
14
|
+
Kernel.application
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.notify_error(error)
|
|
18
|
+
instance.notify_error(error)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @note This method cannot be called because {Application} is singleton.
|
|
22
|
+
def self.new
|
|
23
|
+
fail ApplicationError, "cannot create Application instance manually"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @!method events_processed
|
|
27
|
+
# This signal is emitted every time events are processed in the event loop.
|
|
28
|
+
# @return [Reactive::Signal]
|
|
29
|
+
signal :events_processed, []
|
|
30
|
+
|
|
31
|
+
def initialize
|
|
32
|
+
super()
|
|
33
|
+
@extension = Plugins.core.createApplicationExtension(self)
|
|
34
|
+
@extension.events_processed.each do
|
|
35
|
+
events_processed.call
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @return [Engine] The engine of the application.
|
|
40
|
+
def engine
|
|
41
|
+
Kernel.engine
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @return [Context] The root context of the engine.
|
|
45
|
+
# @see #engine
|
|
46
|
+
def context
|
|
47
|
+
engine.context
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @return [Component] The root component of the application that represents the loaded QML file.
|
|
51
|
+
# @see #load
|
|
52
|
+
# @see #load_data
|
|
53
|
+
# @see #load_path
|
|
54
|
+
def root_component
|
|
55
|
+
@root_component or fail ApplicationError, "QML data or file has not been loaded"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Loads a QML file. The loaded component can be accessed by {#root_component}
|
|
59
|
+
# @see Component
|
|
60
|
+
def load(data: nil, path: nil)
|
|
61
|
+
@root_component = Component.new(data: data, path: path)
|
|
62
|
+
@root = @root_component.create
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Loads a QML file from string data.
|
|
66
|
+
# @see #load
|
|
67
|
+
def load_data(data)
|
|
68
|
+
load(data: data)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Loads a QML file from a file path.
|
|
72
|
+
# @see #load
|
|
73
|
+
def load_path(path)
|
|
74
|
+
load(path: path)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# @return The root object created by the root component.
|
|
78
|
+
def root
|
|
79
|
+
@root or fail "QML data or file has not been loaded"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Starts the event loop of the application.
|
|
83
|
+
# This method never returns until the application quits.
|
|
84
|
+
def exec
|
|
85
|
+
@extension.exec
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Processes queued events in the event loop manually.
|
|
89
|
+
# This method is useful when you are combining an external event loop like EventMachine.
|
|
90
|
+
def process_events
|
|
91
|
+
@extension.process_events
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def force_deferred_deletes
|
|
95
|
+
@extension.force_deferred_deletes
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Runs garbage collection of Ruby and QML and deletes unused objects.
|
|
99
|
+
def collect_garbage
|
|
100
|
+
::GC.start
|
|
101
|
+
engine.collect_garbage
|
|
102
|
+
force_deferred_deletes
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Called when an Ruby error is occured in executing Qt code.
|
|
106
|
+
# @param error The error (or the exception)
|
|
107
|
+
def notify_error(error)
|
|
108
|
+
warn "-- An error occured when running Ruby code from Qt --"
|
|
109
|
+
warn "#{error.class.name}: #{error.message}"
|
|
110
|
+
warn "Backtrace: \n\t#{error.backtrace.join("\n\t")}"
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# @overload application
|
|
115
|
+
# Returns the instance of {Application}.
|
|
116
|
+
# @return [Application]
|
|
117
|
+
# @overload application
|
|
118
|
+
# Call {init} if ruby-qml is not initialized, then yields the application instance and then call {Application#exec}.
|
|
119
|
+
# @return [Application]
|
|
120
|
+
# @example
|
|
121
|
+
# QML.application do |app|
|
|
122
|
+
# app.context[:foo] = 'foo'
|
|
123
|
+
# app.load_path Pathname(__FILE__) + '../main.qml'
|
|
124
|
+
# end
|
|
125
|
+
# @see Application.instance
|
|
126
|
+
def application
|
|
127
|
+
if block_given?
|
|
128
|
+
QML.init unless QML.initialized?
|
|
129
|
+
Application.instance.tap do |app|
|
|
130
|
+
yield app
|
|
131
|
+
app.exec
|
|
132
|
+
end
|
|
133
|
+
else
|
|
134
|
+
Application.instance
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
module_function :application
|
|
139
|
+
end
|