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
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
|