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
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
require 'qml/plugins'
|
|
2
|
+
|
|
3
|
+
module QML
|
|
4
|
+
Application = Kernel.application_meta_object.build_class
|
|
5
|
+
Engine = Kernel.engine_meta_object.build_class
|
|
6
|
+
Context = Plugins.core.metaObjects['QQmlContext'].build_class
|
|
7
|
+
Component = Plugins.core.metaObjects['QQmlComponent'].build_class
|
|
8
|
+
Qt = Plugins.core.metaObjects['Qt'].build_class
|
|
9
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
require 'qml/dispatchable'
|
|
2
|
+
|
|
3
|
+
module QML
|
|
4
|
+
|
|
5
|
+
# {QtObjectBase} is the base class for Qt object wrappers.
|
|
6
|
+
#
|
|
7
|
+
# In ruby-qml you can access the followings of Qt objects in Ruby.
|
|
8
|
+
#
|
|
9
|
+
# * Properties as {Reactive::Property}
|
|
10
|
+
# * Signals as {Reactive::Signal}
|
|
11
|
+
# * Q_INVOKABLE methods, slots and QML methods as Ruby methods
|
|
12
|
+
#
|
|
13
|
+
# Properties and signals support is provided by {Reactive::Object}.
|
|
14
|
+
#
|
|
15
|
+
# While their names are camelCase in Qt, ruby-qml aliases them as underscore_case.
|
|
16
|
+
#
|
|
17
|
+
# @example
|
|
18
|
+
# # QML::Application is actually a wrapper for QApplication
|
|
19
|
+
# app = QML.application
|
|
20
|
+
#
|
|
21
|
+
# # set property
|
|
22
|
+
# app.applicationName = "Test"
|
|
23
|
+
# app.application_name = "Test" # aliased version
|
|
24
|
+
#
|
|
25
|
+
# # connect to signal
|
|
26
|
+
# app.aboutToQuit.connect do # "about_to_quit" is also OK
|
|
27
|
+
# puts "quitting..."
|
|
28
|
+
# end
|
|
29
|
+
#
|
|
30
|
+
# # call slot
|
|
31
|
+
# app.quit
|
|
32
|
+
class QtObjectBase
|
|
33
|
+
include Dispatchable
|
|
34
|
+
include Reactive::Object
|
|
35
|
+
|
|
36
|
+
class << self
|
|
37
|
+
attr_accessor :meta_object
|
|
38
|
+
private :meta_object=
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
attr_accessor :pointer
|
|
42
|
+
private :pointer=
|
|
43
|
+
|
|
44
|
+
# @api private
|
|
45
|
+
def custom_data
|
|
46
|
+
@custom_data ||= {}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Evaluates a JavaScript expression on the QML context of the object.
|
|
50
|
+
# Fails with {QMLError} when the object is not created by QML and does not belong to any context.
|
|
51
|
+
# @param [String] str
|
|
52
|
+
# @return the result.
|
|
53
|
+
# @example
|
|
54
|
+
# component = QML::Component.new data: <<-EOS
|
|
55
|
+
# import QtQuick 2.0
|
|
56
|
+
# QtObject {
|
|
57
|
+
# property string foo: 'foo'
|
|
58
|
+
# property string bar: 'bar'
|
|
59
|
+
# }
|
|
60
|
+
# EOS
|
|
61
|
+
# obj = component.create
|
|
62
|
+
# obj.qml_eval("foo + bar") #=> "foobar"
|
|
63
|
+
def qml_eval(str)
|
|
64
|
+
context = Context.for_object(self)
|
|
65
|
+
fail QMLError, 'belongs to no context' unless context
|
|
66
|
+
context.eval(self, str)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def inspect
|
|
70
|
+
klass = self.class
|
|
71
|
+
property_inspect = klass.instance_properties.sort
|
|
72
|
+
.reject { |name| klass.instance_property(name).alias? }
|
|
73
|
+
.map do |name|
|
|
74
|
+
"#{name}=" +
|
|
75
|
+
begin
|
|
76
|
+
property(name).value.inspect
|
|
77
|
+
rescue ConversionError
|
|
78
|
+
"<unsupported type>"
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
.join(' ')
|
|
82
|
+
classname = klass.name || "[class for #{klass.meta_object.name}]"
|
|
83
|
+
"#<#{classname}:#{__id__} #{property_inspect}>"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
alias_method :to_s, :inspect
|
|
87
|
+
|
|
88
|
+
# @return whether the object is managed by Ruby and QML and garbage collected when no longer used.
|
|
89
|
+
def managed?
|
|
90
|
+
pointer.managed?
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Sets the management status of the object.
|
|
94
|
+
# @param [Boolean] managed
|
|
95
|
+
# @return [Boolean]
|
|
96
|
+
def managed=(managed)
|
|
97
|
+
pointer.managed = managed
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Sets the management status of the object in safer way.
|
|
101
|
+
# Objects that are created by QML will remain managed and objects that have parents will remain unmanaged.
|
|
102
|
+
# @param [Boolean] managed
|
|
103
|
+
# @return [Boolean]
|
|
104
|
+
def prefer_managed(managed)
|
|
105
|
+
pointer.prefer_managed managed
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
data/lib/qml/reactive.rb
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
require 'qml/reactive/signal'
|
|
2
|
+
require 'qml/reactive/signals/map_signal'
|
|
3
|
+
require 'qml/reactive/signals/select_signal'
|
|
4
|
+
require 'qml/reactive/signals/merge_signal'
|
|
5
|
+
require 'qml/reactive/signal_spy'
|
|
6
|
+
require 'qml/reactive/property'
|
|
7
|
+
require 'qml/reactive/object'
|
|
8
|
+
require 'qml/reactive/error'
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
module QML
|
|
2
|
+
module Reactive
|
|
3
|
+
module Bindable
|
|
4
|
+
|
|
5
|
+
def initialize(*args, &block)
|
|
6
|
+
super
|
|
7
|
+
@connections = []
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Sets a new value.
|
|
11
|
+
# The old property binding is discarded.
|
|
12
|
+
# @param new_value new value
|
|
13
|
+
def value=(new_value, unbind = true)
|
|
14
|
+
self.unbind if unbind
|
|
15
|
+
unless value == new_value
|
|
16
|
+
super(new_value)
|
|
17
|
+
changed.emit(new_value)
|
|
18
|
+
end
|
|
19
|
+
new_value
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
alias_method :set_value, :value=
|
|
23
|
+
|
|
24
|
+
# @return The property value
|
|
25
|
+
def value
|
|
26
|
+
touch
|
|
27
|
+
super
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @api private
|
|
31
|
+
module Resolver
|
|
32
|
+
@sources_stack = []
|
|
33
|
+
class << self
|
|
34
|
+
def eval_and_resolve(&block)
|
|
35
|
+
@sources_stack.push([])
|
|
36
|
+
ret = block.call
|
|
37
|
+
[ret, @sources_stack.pop]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def add(property)
|
|
41
|
+
current = @sources_stack.last
|
|
42
|
+
current && current << property
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Sets a property binding.
|
|
48
|
+
# The block is re-evaluated when any of other properties used in it is updated
|
|
49
|
+
# and the result is used as the new property value.
|
|
50
|
+
# @yield
|
|
51
|
+
# @example
|
|
52
|
+
# p1 = Property.new
|
|
53
|
+
# p2 = Property.new
|
|
54
|
+
# p1.bind { p2.value + 1 }
|
|
55
|
+
# p2.value = 10
|
|
56
|
+
# p1.value #=> 11
|
|
57
|
+
# @return The property value
|
|
58
|
+
def bind(&block)
|
|
59
|
+
unbind
|
|
60
|
+
value, sources = Resolver.eval_and_resolve(&block)
|
|
61
|
+
set_value(value, false)
|
|
62
|
+
@connections = sources.map do |source|
|
|
63
|
+
fail Error, 'Recursive binding' if source == self
|
|
64
|
+
source.changed.connect { set_value(block.call, false) }
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def unbind
|
|
69
|
+
@connections.each(&:disconnect)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def touch
|
|
75
|
+
Resolver.add(self)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'qml/reactive/signal'
|
|
2
|
+
|
|
3
|
+
module QML
|
|
4
|
+
module Reactive
|
|
5
|
+
class ChainedSignal < Signal
|
|
6
|
+
private
|
|
7
|
+
def connected(listener)
|
|
8
|
+
if connection_count == 1
|
|
9
|
+
@connections = Array(connect_to_sources)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def disconnected(listener)
|
|
14
|
+
if connection_count == 0
|
|
15
|
+
@connections.each(&:disconnect)
|
|
16
|
+
@connections = nil
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def connect_to_sources
|
|
21
|
+
fail NotImplementedError
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
require 'qml/reactive/unbound_signal'
|
|
2
|
+
require 'qml/reactive/unbound_property'
|
|
3
|
+
|
|
4
|
+
module QML
|
|
5
|
+
module Reactive
|
|
6
|
+
|
|
7
|
+
# {Object} is used to define signals and properties within class definition.
|
|
8
|
+
# Signals and properties will be inherited by and can be overridden in subclasses.
|
|
9
|
+
# @note Currently {Object} can be included only by classes, not modules.
|
|
10
|
+
# @see Object::ClassMethods
|
|
11
|
+
module Object
|
|
12
|
+
|
|
13
|
+
# When {Object} is included by a class, the class extends {ClassMethods} to add the common class methods.
|
|
14
|
+
def self.included(derived)
|
|
15
|
+
fail Error, "SignalDef must be included in a class" unless derived.is_a? ::Class
|
|
16
|
+
derived.extend(ClassMethods)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def initialize(*args, &block)
|
|
20
|
+
super
|
|
21
|
+
properties = self.class.instance_property_hash
|
|
22
|
+
.values
|
|
23
|
+
.map { |property| self.class.instance_property(property.original) }
|
|
24
|
+
.uniq
|
|
25
|
+
notifier_signals = properties.map(&:notifier_signal)
|
|
26
|
+
signals = self.class.instance_signal_hash
|
|
27
|
+
.values
|
|
28
|
+
.map { |signal| self.class.instance_signal(signal.original) }
|
|
29
|
+
.uniq - notifier_signals
|
|
30
|
+
|
|
31
|
+
properties.each do |property|
|
|
32
|
+
p = property.bind(self)
|
|
33
|
+
instance_variable_set(:"@_property_#{property.name}", p)
|
|
34
|
+
instance_variable_set(:"@_signal_#{property.notifier_signal.name}", p.changed)
|
|
35
|
+
end
|
|
36
|
+
signals.each do |signal|
|
|
37
|
+
instance_variable_set(:"@_signal_#{signal.name}", signal.bind(self))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
self.class.send(:initial_connections_hash).each do |name, blocks|
|
|
41
|
+
blocks.each do |block|
|
|
42
|
+
signal(name).connect do |*args|
|
|
43
|
+
instance_exec(*args, &block)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @!method signal(name)
|
|
50
|
+
# Returns a signal.
|
|
51
|
+
# @param name [Symbol] The signal name
|
|
52
|
+
# @return [QML::Reactive::Signal]
|
|
53
|
+
|
|
54
|
+
# @!method signals
|
|
55
|
+
# Returns all signal names.
|
|
56
|
+
# @return [Array<Symbol>]
|
|
57
|
+
|
|
58
|
+
# @!method property(name)
|
|
59
|
+
# Returns a property.
|
|
60
|
+
# @param name [Symbol] The property name
|
|
61
|
+
# @return [QML::Reactive::Property]
|
|
62
|
+
|
|
63
|
+
# @!method properties
|
|
64
|
+
# Returns all property names.
|
|
65
|
+
# @return [Array<Symbol>]
|
|
66
|
+
|
|
67
|
+
[%w{signal signals}, %w{property properties}].each do |singular, plural|
|
|
68
|
+
class_eval <<-EOS, __FILE__, __LINE__ + 1
|
|
69
|
+
def #{singular}(name)
|
|
70
|
+
name = self.class.instance_#{singular}(name).original
|
|
71
|
+
instance_variable_get(:"@_#{singular}_\#{name}") or
|
|
72
|
+
fail ::NameError, "undefined #{singular} '\#{name}' for class '\#{self.class}'"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def #{plural}
|
|
76
|
+
self.class.instance_#{plural}
|
|
77
|
+
end
|
|
78
|
+
EOS
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# @!parse extend ClassMethods
|
|
82
|
+
|
|
83
|
+
module ClassMethods
|
|
84
|
+
|
|
85
|
+
# @!method instance_signals(include_super = true)
|
|
86
|
+
# Returns all signal names for the class.
|
|
87
|
+
# @param include_super [true|false]
|
|
88
|
+
# @return [Array<Symbol>]
|
|
89
|
+
|
|
90
|
+
# @!method instance_signal(name)
|
|
91
|
+
# Returns an unbound signal.
|
|
92
|
+
# @param name [Symbol] The signal name
|
|
93
|
+
# @return [QML::Reactive::UnboundSignal]
|
|
94
|
+
|
|
95
|
+
# @!method instance_properties(include_super = true)
|
|
96
|
+
# Returns all property names for the class.
|
|
97
|
+
# @param include_super [true|false]
|
|
98
|
+
# @return [Array<Symbol>]
|
|
99
|
+
|
|
100
|
+
# @!method instance_property(name)
|
|
101
|
+
# Returns an unbound property.
|
|
102
|
+
# @param name [Symbol] The property name
|
|
103
|
+
# @return [QML::Reactive::UnboundProperty]
|
|
104
|
+
[%w{signal signals}, %w{property properties}].each do |singular, plural|
|
|
105
|
+
class_eval <<-EOS, __FILE__, __LINE__ + 1
|
|
106
|
+
def instance_#{singular}_hash(include_super = true)
|
|
107
|
+
if include_super && superclass.include?(Object)
|
|
108
|
+
superclass.instance_#{singular}_hash.merge instance_#{singular}_hash(false)
|
|
109
|
+
else
|
|
110
|
+
@instance_#{singular}_hash ||= {}
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def instance_#{plural}(include_super = true)
|
|
115
|
+
instance_#{singular}_hash(include_super).keys
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def instance_#{singular}(name)
|
|
119
|
+
instance_#{singular}_hash[name] or fail ::NameError, "undefined #{singular} '\#{name}' for class '\#{self}'"
|
|
120
|
+
end
|
|
121
|
+
EOS
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Defines a signal for the class.
|
|
125
|
+
# The signal will be variadic if args == nil.
|
|
126
|
+
# @param name [#to_sym] The signal name
|
|
127
|
+
# @param params [Array<#to_sym>, nil] The signal parameter names
|
|
128
|
+
# @return [Symbol] The signal name
|
|
129
|
+
# @example
|
|
130
|
+
# class Button
|
|
131
|
+
# include QML::Reactive::Object
|
|
132
|
+
# signal :pressed, [:pos]
|
|
133
|
+
# def press(pos)
|
|
134
|
+
# pressed.emit(pos)
|
|
135
|
+
# end
|
|
136
|
+
# end
|
|
137
|
+
#
|
|
138
|
+
# button = Button.new
|
|
139
|
+
# button.pressed.connect { |pos| puts "Pressed at #{pos}" }
|
|
140
|
+
# button.press([10, 20])
|
|
141
|
+
#
|
|
142
|
+
# class ColorButton < Button
|
|
143
|
+
# signal :pressed, [:pos, :color]
|
|
144
|
+
# end
|
|
145
|
+
#
|
|
146
|
+
# color_button = ColorButton.new
|
|
147
|
+
# color_button.pressed.connect { |pos, color| "#{color} button pressed at #{pos}" }
|
|
148
|
+
# color_button.press([10, 20], 'red')
|
|
149
|
+
def signal(name, params, factory: nil)
|
|
150
|
+
name.to_sym.tap do |name|
|
|
151
|
+
params = params.map(&:to_sym)
|
|
152
|
+
add_signal(UnboundSignal.new(name, params, false, self, factory))
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Defines a variadic signal.
|
|
157
|
+
# Variadic signals do not restrict the number of arguments.
|
|
158
|
+
# @see #signal
|
|
159
|
+
def variadic_signal(name, factory: nil)
|
|
160
|
+
name.to_sym.tap do |name|
|
|
161
|
+
add_signal(UnboundSignal.new(name, nil, true, self, factory))
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Aliases a signal.
|
|
166
|
+
# @return [Symbol] The new name
|
|
167
|
+
def alias_signal(name, original_name)
|
|
168
|
+
add_signal(instance_signal(original_name).alias(name))
|
|
169
|
+
name
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Defines a property for the class.
|
|
173
|
+
# @param name [#to_sym] The name of the property
|
|
174
|
+
# @param init_value The initial value (optional)
|
|
175
|
+
# @yield The initial property binding (optional)
|
|
176
|
+
# @return [Symbol] The name
|
|
177
|
+
# @example
|
|
178
|
+
# class Foo
|
|
179
|
+
# include QML::Reactive::Object
|
|
180
|
+
# property(:name) { 'hogehoge' }
|
|
181
|
+
# ...
|
|
182
|
+
# end
|
|
183
|
+
# Foo.new.name #=> 'hogehoge'
|
|
184
|
+
# Foo.new.name = 'foobar'
|
|
185
|
+
# Foo.new.name #=> 'foobar'
|
|
186
|
+
# Foo.new.name_changed.connect do |new_name|
|
|
187
|
+
# ...
|
|
188
|
+
#
|
|
189
|
+
# class Bar < Foo
|
|
190
|
+
# property(:name) { 'piyopiyo' }
|
|
191
|
+
# end
|
|
192
|
+
# Bar.new.name #=> 'piyopiyo'
|
|
193
|
+
def property(name, init_value = nil, factory: nil, &init_binding)
|
|
194
|
+
name = name.to_sym
|
|
195
|
+
add_property(UnboundProperty.new(name, init_value, init_binding, self, factory))
|
|
196
|
+
name
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Aliases a property.
|
|
200
|
+
# @return [Symbol] The new name
|
|
201
|
+
def alias_property(name, original_name)
|
|
202
|
+
add_property(instance_property(original_name).alias(name))
|
|
203
|
+
name
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Adds a signal handler.
|
|
207
|
+
# @param signal_name The name of the signal
|
|
208
|
+
# @yield The block that is connected to the signal during object initialization
|
|
209
|
+
def on(signal_name, &block)
|
|
210
|
+
# just for check
|
|
211
|
+
instance_signal(signal_name)
|
|
212
|
+
@initial_connections_hash ||= {}
|
|
213
|
+
@initial_connections_hash[signal_name] ||= []
|
|
214
|
+
@initial_connections_hash[signal_name] << block
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Adds a handler to a property change signal.
|
|
218
|
+
# @param property_name The name of the property
|
|
219
|
+
# @yield The block that is connected to the property change signal during object initialization
|
|
220
|
+
# @example
|
|
221
|
+
# class Foo
|
|
222
|
+
# property :bar, ''
|
|
223
|
+
# on_changed :bar do
|
|
224
|
+
# some_action
|
|
225
|
+
# end
|
|
226
|
+
# def some_action
|
|
227
|
+
# ...
|
|
228
|
+
# end
|
|
229
|
+
# end
|
|
230
|
+
# @see #on
|
|
231
|
+
def on_changed(property_name, &block)
|
|
232
|
+
on(:"#{property_name}_changed", &block)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
private
|
|
236
|
+
|
|
237
|
+
def add_signal(signal)
|
|
238
|
+
instance_signal_hash(false)[signal.name] = signal
|
|
239
|
+
|
|
240
|
+
class_eval <<-EOS, __FILE__, __LINE__ + 1
|
|
241
|
+
def #{signal.name}
|
|
242
|
+
@_signal_#{signal.original}
|
|
243
|
+
end
|
|
244
|
+
EOS
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def add_property(property)
|
|
248
|
+
instance_property_hash(false)[property.name] = property
|
|
249
|
+
|
|
250
|
+
class_eval <<-EOS, __FILE__, __LINE__ + 1
|
|
251
|
+
def #{property.name}(&block)
|
|
252
|
+
@_property_#{property.original}.value(&block)
|
|
253
|
+
end
|
|
254
|
+
def #{property.name}=(new_value)
|
|
255
|
+
@_property_#{property.original}.value = new_value
|
|
256
|
+
end
|
|
257
|
+
EOS
|
|
258
|
+
|
|
259
|
+
add_signal(property.notifier_signal)
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def initial_connections_hash(include_super: true)
|
|
263
|
+
if include_super && superclass.include?(Object)
|
|
264
|
+
superclass.send(:initial_connections_hash).dup.tap do |hash|
|
|
265
|
+
initial_connections_hash(include_super: false).each do |key, blocks|
|
|
266
|
+
hash[key] ||= []
|
|
267
|
+
hash[key] += blocks
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
else
|
|
271
|
+
@initial_connections_hash ||= {}
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|