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