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