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,34 @@
1
+ module QML
2
+
3
+ # {Dispatchable} provides a handy way for asynchronous (queued) method calls
4
+ # which actually processed later in the event loop.
5
+ # @see QML.later
6
+ module Dispatchable
7
+
8
+ class Proxy < BasicObject
9
+ def initialize(obj)
10
+ @obj = obj
11
+ end
12
+
13
+ # Actually enqueues the method call using {QML.later}.
14
+ def method_missing(name, *args, &block)
15
+ ::QML.later do
16
+ @obj.__send__(name, *args, &block)
17
+ end
18
+ end
19
+ end
20
+
21
+ # Returns a proxy object for calling a method asynchronously within the event loop.
22
+ # @return [Proxy]
23
+ # @example
24
+ # def on_button_clicked
25
+ # Thread.new do
26
+ # result = do_task
27
+ # later.set_result_to_ui(result)
28
+ # end
29
+ # end
30
+ def later
31
+ Proxy.new(self)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,61 @@
1
+ require 'singleton'
2
+ require 'monitor'
3
+
4
+ module QML
5
+
6
+ class Dispatcher
7
+ include Singleton
8
+ include MonitorMixin
9
+
10
+ MAX_DURATION = 1/10.to_r
11
+
12
+ def initialize
13
+ @tasks = []
14
+ super
15
+ end
16
+
17
+ def add_task(&task)
18
+ synchronize do
19
+ @tasks << task
20
+ end
21
+ end
22
+
23
+ def empty?
24
+ synchronize do
25
+ @tasks.empty?
26
+ end
27
+ end
28
+
29
+ def run_tasks
30
+ synchronize do
31
+ start_time = Time.now
32
+ loop do
33
+ break if @tasks.empty?
34
+ break if start_time.to_r - Time.now.to_r > MAX_DURATION
35
+ task = @tasks.shift
36
+ task.call
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ # Runs a block asynchronously within the event loop.
43
+ #
44
+ # QML UI is not thread-safe and can only be accessed from the main thread.
45
+ # Use this method to set results of asynchronous tasks to UI.
46
+ # @example
47
+ # def on_button_clicked
48
+ # Thread.new do
49
+ # result = do_task
50
+ # QML.later do
51
+ # set_result_to_ui(result)
52
+ # end
53
+ # end
54
+ # end
55
+ # @see Dispatchable#later
56
+ # @see Dispatcher#add_task
57
+ def later(&block)
58
+ Dispatcher.instance.add_task(&block)
59
+ end
60
+ module_function :later
61
+ end
@@ -0,0 +1,54 @@
1
+ require 'qml/qt_classes'
2
+
3
+ module QML
4
+ # @!parse class Engine < QtObjectBase; end
5
+
6
+ # {Engine} provides a QML engine.
7
+ #
8
+ # @see http://qt-project.org/doc/qt-5/qqmlengine.html QQmlEngine (C++)
9
+ class Engine
10
+
11
+ # @return [Engine] the instance of {Engine}.
12
+ # @see QML.engine
13
+ def self.instance
14
+ Kernel.engine
15
+ end
16
+
17
+ # @note This method cannot be called because {Engine} is singleton.
18
+ def self.new
19
+ fail EngineError, "cannot create Engine instance manually"
20
+ end
21
+
22
+ def initialize
23
+ super()
24
+ @extension = Plugins.core.createEngineExtension(self)
25
+ end
26
+
27
+ # @return [Context] the root {Context} the {Engine}.
28
+ def context
29
+ @extension.rootContext
30
+ end
31
+
32
+ # Starts garbage collection on the {Engine}.
33
+ def collect_garbage
34
+ @extension.collect_garbage
35
+ end
36
+
37
+ # Adds an {ImageProvider} to the {Engine}.
38
+ # @param id [String]
39
+ # @param provider [ImageProvider]
40
+ # @return [ImageProvider]
41
+ # @see ImageProvider
42
+ def add_image_provider(id, provider)
43
+ @extension.add_image_provider(id, provider.qt_image_provider)
44
+ provider
45
+ end
46
+ end
47
+
48
+ # @return [Engine] the instance of {Engine}.
49
+ # @see Engine.instance
50
+ def engine
51
+ Kernel.engine
52
+ end
53
+ module_function :engine
54
+ end
@@ -0,0 +1,15 @@
1
+ module QML
2
+ module ErrorConverter
3
+
4
+ class << self
5
+ attr_reader :patterns
6
+
7
+ def add_pattern(cpp_classname, ruby_class)
8
+ @patterns ||= {}
9
+ @patterns[cpp_classname] = ruby_class
10
+ end
11
+ end
12
+
13
+ add_pattern 'RubyQml::QmlException', QMLError
14
+ end
15
+ end
@@ -0,0 +1,26 @@
1
+ module QML
2
+
3
+ class MethodError < StandardError; end
4
+ class SignalError < StandardError; end
5
+ class PropertyError < StandardError; end
6
+ class PluginError < StandardError; end
7
+ class QtObjectError < StandardError; end
8
+ class ConversionError < StandardError; end
9
+ class QMLError < StandardError; end
10
+ class AccessError < StandardError; end
11
+ class ApplicationError < StandardError; end
12
+ class EngineError < StandardError; end
13
+ class InvalidThreadError < StandardError; end
14
+ class UninitializedError < StandardError; end
15
+ class AlreadyInitializedError < StandardError; end
16
+
17
+ class CppError < StandardError
18
+ attr_reader :class_name, :raw_message
19
+
20
+ def initialize(classname, message)
21
+ super("<#{classname}> #{message}")
22
+ @class_name = classname
23
+ @raw_message = message
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ require 'qml/geometry/point'
2
+ require 'qml/geometry/size'
3
+ require 'qml/geometry/rectangle'
@@ -0,0 +1,5 @@
1
+ module QML
2
+ module Geometry
3
+ class Point < Struct.new(:x, :y); end
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module QML
2
+ module Geometry
3
+ class Rectangle < Struct.new(:x, :y, :width, :height); end
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module QML
2
+ module Geometry
3
+ class Size < Struct.new(:width, :height); end
4
+ end
5
+ end
@@ -0,0 +1,87 @@
1
+ require 'qml/access'
2
+
3
+ module QML
4
+
5
+ # {ImageProvider} is used to provide images manually to QML.
6
+ #
7
+ # QML requests images to ImageProviders when you use "image:" URL scheme
8
+ # (like "image://my_image_provider/foo.png") in source specification of Image elements.
9
+ #
10
+ # @example
11
+ # class MyImageProvider < QML::ImageProvider
12
+ # def request(req)
13
+ # Thread.new do
14
+ # image_data = load_image_data()
15
+ # req.finish(image_data)
16
+ # end
17
+ # end
18
+ # end
19
+ # QML.engine.add_image_provider(MyImageProvider.new)
20
+ #
21
+ # @see Engine#add_image_provider
22
+ # @see http://qt-project.org/doc/qt-5/qquickimageprovider.html QQuickImageProvider (C++)
23
+ # @see http://qt-project.org/doc/qt-5/qml-qtquick-image.html Image (QML)
24
+
25
+ class ImageProvider
26
+
27
+ # {Request} represents a image request from QML.
28
+ # @see ImageProvider#request
29
+ class Request
30
+ # The image ID.
31
+ # If you requested "image://my_image_provider/foo/bar.png", the ID would be "foo/bar.png".
32
+ attr_reader :id
33
+
34
+ # Finishes the image request.
35
+ # @param data [String] The image file data
36
+ def finish(data)
37
+ fail ::TypeError, "data must be a String" unless data.is_a? ::String
38
+ QML.later do
39
+ @promise.set_data(data)
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def initialize(id, promise)
46
+ @id = id;
47
+ @promise = promise
48
+ end
49
+ end
50
+
51
+ attr_reader :qt_image_provider
52
+
53
+ def initialize
54
+ @callback = Callback.new(self)
55
+ @qt_image_provider = Plugins.core.createImageProvider(@callback)
56
+ @qt_image_provider.prefer_managed false
57
+ end
58
+
59
+ # @abstract
60
+ # @param req [Request]
61
+ #
62
+ # Implement this method to handle image requests from QML.
63
+ #
64
+ # Call {Request#finish} to finish image loading.
65
+ # Image loading should be done asynchnorously because this method is called within the event loop.
66
+
67
+ def request(req)
68
+ fail ::NotImplementedError
69
+ end
70
+
71
+ private
72
+
73
+ class Callback
74
+ include QML::Access
75
+
76
+ def initialize(provider)
77
+ super()
78
+ @provider = provider
79
+ end
80
+
81
+ def request(id, promise)
82
+ promise.prefer_managed true
83
+ @provider.request(Request.new(id, promise))
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,20 @@
1
+ require 'qml/qml'
2
+ require 'qml/class_builder'
3
+
4
+ module QML
5
+ class MetaObject
6
+ alias_method :to_s, :name
7
+
8
+ def inspect
9
+ "<QML::MetaObject:#{self}>"
10
+ end
11
+
12
+ def build_class
13
+ @@classes ||= {}
14
+ klass = @@classes[name]
15
+ builder = ClassBuilder.new(self, klass)
16
+ builder.build
17
+ @@classes[name] = builder.klass
18
+ end
19
+ end
20
+ end
@@ -0,0 +1 @@
1
+ require 'qml/models/array_model'
@@ -0,0 +1,12 @@
1
+ module QML
2
+ module NameHelper
3
+ module_function
4
+
5
+ def to_underscore(sym)
6
+ sym.to_s.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').gsub(/([a-z\d])([A-Z])/,'\1_\2').downcase.to_sym
7
+ end
8
+ def to_upper_underscore(sym)
9
+ to_underscore(sym).upcase
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,15 @@
1
+ module QML
2
+
3
+ module Platform
4
+ module_function
5
+
6
+ def windows?
7
+ !!(/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM)
8
+ end
9
+
10
+ def mac?
11
+ !!(/darwin/ =~ RUBY_PLATFORM)
12
+ end
13
+ end
14
+
15
+ end
@@ -0,0 +1,46 @@
1
+ require 'pathname'
2
+ require 'qml/platform'
3
+
4
+ module QML
5
+ # {PluginLoader} loads Qt C++ plugins and enables you to use your Qt C++ codes from Ruby easily.
6
+ # @see http://qt-project.org/doc/qt-5/qpluginloader.html QPluginLoader (C++)
7
+ class PluginLoader
8
+
9
+ alias_method :initialize_orig, :initialize
10
+ private :initialize_orig
11
+
12
+ # @overload initialize(path)
13
+ # @param [String|Pathname] path the library path (may be platform-dependent).
14
+ # @example
15
+ # loader = QML::PluginLoader.new('path/to/libhoge.dylib')
16
+ # @overload initialize(dir, libname)
17
+ # @param [String|Pathname] dir the library directory.
18
+ # @param [String] libname the platform-independent library name.
19
+ # @example
20
+ # loader = QML::PluginLoader.new('path/to', 'hoge')
21
+ def initialize(path, libname = nil)
22
+ path = Pathname(path) + self.class.lib_filename(libname) if libname
23
+ initialize_orig(path.to_s)
24
+ end
25
+
26
+ # @!method instance
27
+ # Loads the plugin and returns the instance of the plugin.
28
+ # @return [QtObjectBase]
29
+
30
+ # @param [String] libname
31
+ # @return [String] platform-dependent library file name.
32
+ # @example
33
+ # # on Mac
34
+ # QML::PluginLoader.lib_filename("hoge") #=> "libhoge.dylib"
35
+ def self.lib_filename(libname)
36
+ case
37
+ when Platform::windows?
38
+ "#{libname}.dll"
39
+ when Platform::mac?
40
+ "lib#{libname}.dylib"
41
+ else
42
+ "lib#{libname}.so"
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,26 @@
1
+ require 'qml/plugin_loader'
2
+ require 'qml/root_path'
3
+ require 'pathname'
4
+
5
+ module QML
6
+ module Plugins
7
+
8
+ PATH = ROOT_PATH + 'ext/qml/plugins'
9
+
10
+ class << self
11
+ def core
12
+ @core ||= load('core')
13
+ end
14
+
15
+ def test_util
16
+ @test_util ||= load('testutil')
17
+ end
18
+
19
+ private
20
+
21
+ def load(name)
22
+ QML::PluginLoader.new(PATH + name, "rubyqml-#{name}").instance
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1 @@
1
+ require_relative '../../ext/qml/qml'
@@ -0,0 +1,6 @@
1
+ require 'qml/qt_classes'
2
+
3
+ module QML
4
+ # @!parse class Qt < QtObjectBase; end
5
+ class Qt; end
6
+ end