qml 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +57 -4
  3. data/changes.md +8 -0
  4. data/examples/fizzbuzz/fizzbuzz.rb +1 -1
  5. data/examples/imageprovider/imageprovider.rb +1 -1
  6. data/examples/todo_array/todo_array.rb +1 -1
  7. data/examples/todo_sequel/todo_sequel.rb +1 -1
  8. data/examples/twitter/twitter.rb +1 -1
  9. data/ext/qml/accessclass.cpp +5 -9
  10. data/ext/qml/conversionerror.h +14 -0
  11. data/ext/qml/ext_metaobject.cpp +30 -41
  12. data/ext/qml/init.cpp +5 -7
  13. data/ext/qml/rubyclass.cpp +6 -18
  14. data/ext/qml/rubyclass.h +22 -26
  15. data/ext/qml/rubyvalue.cpp +155 -221
  16. data/ext/qml/rubyvalue.h +30 -63
  17. data/ext/qml/signalforwarder.cpp +1 -3
  18. data/ext/qml/util.cpp +2 -25
  19. data/ext/qml/util.h +1 -21
  20. data/ext/qml/weakvaluereference.cpp +4 -5
  21. data/lib/qml/access.rb +9 -17
  22. data/lib/qml/application.rb +18 -21
  23. data/lib/qml/context.rb +1 -1
  24. data/lib/qml/dispatcher.rb +0 -1
  25. data/lib/qml/error_converter.rb +1 -0
  26. data/lib/qml/meta_object.rb +3 -3
  27. data/lib/qml/qt_object_base.rb +130 -5
  28. data/lib/qml/reactive/object.rb +34 -25
  29. data/lib/qml/reactive/signal.rb +6 -10
  30. data/lib/qml/reactive/unbound_property.rb +1 -1
  31. data/lib/qml/reactive/unbound_signal.rb +2 -2
  32. data/lib/qml/version.rb +1 -1
  33. data/spec/qml/reactive/object_spec.rb +14 -38
  34. data/spec/qml/reactive/signal_spec.rb +1 -1
  35. data/spec/qml/reactive/unbound_property_spec.rb +40 -0
  36. data/spec/qml/reactive/unbound_signal_spec.rb +70 -0
  37. data/spec/{shared_examples → shared}/qml/data/list_model.rb +0 -0
  38. data/spec/shared/qml/reactive/object.rb +39 -0
  39. data/spec/spec_helper.rb +1 -1
  40. metadata +12 -6
  41. data/lib/qml/class_builder.rb +0 -129
@@ -42,11 +42,7 @@ public:
42
42
  template <typename ... TArgs>
43
43
  RubyValue send(ID method, TArgs ... args) const
44
44
  {
45
- constexpr int argc = sizeof...(args);
46
- std::array<VALUE, argc> argv = {{ VALUE(args)... }};
47
- return protect([&] {
48
- return rb_funcall2(mValue, method, argc, argv.data());
49
- });
45
+ return rb_funcall(mValue, method, sizeof...(args), args...);
50
46
  }
51
47
 
52
48
  template <typename ... TArgs>
@@ -59,7 +55,11 @@ public:
59
55
  bool operator!=(const RubyValue &other) const { return !operator==(other); }
60
56
  explicit operator bool() const { return RTEST(mValue); }
61
57
 
62
- bool isKindOf(const RubyModule &module) const;
58
+ bool isKindOf(const RubyValue &module) const
59
+ {
60
+ return RTEST(rb_obj_is_kind_of(mValue, module));
61
+ }
62
+
63
63
  bool isConvertibleTo(int metaType) const;
64
64
  int defaultMetaType() const;
65
65
 
@@ -99,16 +99,8 @@ struct Conversion<bool>
99
99
  template <typename T>
100
100
  struct Conversion<T, typename std::enable_if<std::is_signed<T>::value && std::is_integral<T>::value>::type>
101
101
  {
102
- static T from(RubyValue x)
103
- {
104
- return protect([&] {
105
- return NUM2LL(x);
106
- });
107
- }
108
- static RubyValue to(T x)
109
- {
110
- return LL2NUM(x);
111
- }
102
+ static T from(RubyValue x) { return NUM2LL(x); }
103
+ static RubyValue to(T x) { return LL2NUM(x); }
112
104
  };
113
105
 
114
106
  // unsigned integers
@@ -116,16 +108,8 @@ struct Conversion<T, typename std::enable_if<std::is_signed<T>::value && std::is
116
108
  template <typename T>
117
109
  struct Conversion<T, typename std::enable_if<std::is_unsigned<T>::value && std::is_integral<T>::value>::type>
118
110
  {
119
- static T from(RubyValue x)
120
- {
121
- return protect([&] {
122
- return NUM2ULL(x);
123
- });
124
- }
125
- static RubyValue to(T x)
126
- {
127
- return ULL2NUM(x);
128
- }
111
+ static T from(RubyValue x) { return NUM2ULL(x); }
112
+ static RubyValue to(T x) { return ULL2NUM(x); }
129
113
  };
130
114
 
131
115
  // floating point values
@@ -135,14 +119,12 @@ struct Conversion<T, typename std::enable_if<std::is_floating_point<T>::value>::
135
119
  {
136
120
  static T from(RubyValue x)
137
121
  {
138
- return protect([&] {
139
- auto type = rb_type(x);
140
- if (type == T_FIXNUM || type == T_BIGNUM) {
141
- return double(NUM2LL(x));
142
- } else {
143
- return RFLOAT_VALUE(VALUE(x));
144
- }
145
- });
122
+ auto type = rb_type(x);
123
+ if (type == T_FIXNUM || type == T_BIGNUM) {
124
+ return double(NUM2LL(x));
125
+ } else {
126
+ return RFLOAT_VALUE(VALUE(x));
127
+ }
146
128
  }
147
129
  static RubyValue to(T x)
148
130
  {
@@ -161,9 +143,7 @@ struct Conversion<T<V>, typename std::enable_if<IsQListLike<T<V>>::value>::type>
161
143
  {
162
144
  static T<V> from(RubyValue x)
163
145
  {
164
- protect([&] {
165
- x = rb_convert_type(x, T_ARRAY, "Array", "to_ary");
166
- });
146
+ x = rb_convert_type(x, T_ARRAY, "Array", "to_ary");
167
147
  int length = RARRAY_LEN(VALUE(x));
168
148
  T<V> list;
169
149
  list.reserve(length);
@@ -174,14 +154,10 @@ struct Conversion<T<V>, typename std::enable_if<IsQListLike<T<V>>::value>::type>
174
154
  }
175
155
  static RubyValue to(const T<V> &list)
176
156
  {
177
- RubyValue ary = protect([&] {
178
- return rb_ary_new();
179
- });
157
+ auto ary = rb_ary_new();
180
158
  for (const auto &elem : list) {
181
159
  auto x = RubyValue::from(elem);
182
- protect([&] {
183
- rb_ary_push(ary, x);
184
- });
160
+ rb_ary_push(ary, x);
185
161
  }
186
162
  return ary;
187
163
  }
@@ -198,34 +174,25 @@ struct Conversion<T<K, V>, typename std::enable_if<IsQHashLike<T<K, V>>::value>:
198
174
  {
199
175
  static T<K, V> from(RubyValue x)
200
176
  {
201
- protect([&] {
202
- x = rb_convert_type(x, T_HASH, "Hash", "to_hash");
203
- });
177
+ x = rb_convert_type(x, T_HASH, "Hash", "to_hash");
178
+
204
179
  T<K, V> hash;
205
- protect([&] {
206
- auto each = [](VALUE key, VALUE value, VALUE arg) -> int {
207
- auto &hash = *reinterpret_cast<T<K, V> *>(arg);
208
- unprotect([&] {
209
- hash[RubyValue(key).to<K>()] = RubyValue(value).to<V>();
210
- });
211
- return ST_CONTINUE;
212
- };
213
- auto eachPtr = (int (*)(VALUE, VALUE, VALUE))each;
214
- rb_hash_foreach(x, (int (*)(...))eachPtr, (VALUE)(&hash));
215
- });
180
+ auto each = [](VALUE key, VALUE value, VALUE arg) -> int {
181
+ auto &hash = *reinterpret_cast<T<K, V> *>(arg);
182
+ hash[RubyValue(key).to<K>()] = RubyValue(value).to<V>();
183
+ return ST_CONTINUE;
184
+ };
185
+ auto eachPtr = (int (*)(VALUE, VALUE, VALUE))each;
186
+ rb_hash_foreach(x, (int (*)(...))eachPtr, (VALUE)(&hash));
216
187
  return hash;
217
188
  }
218
189
  static RubyValue to(const T<K, V> &hash)
219
190
  {
220
- RubyValue rubyHash = protect([&] {
221
- return rb_hash_new();
222
- });
191
+ auto rubyHash = rb_hash_new();
223
192
  for (auto i = hash.begin(); i != hash.end(); ++i) {
224
193
  auto k = RubyValue::from(i.key());
225
194
  auto v = RubyValue::from(i.value());
226
- protect([&] {
227
- rb_hash_aset(rubyHash, k, v);
228
- });
195
+ rb_hash_aset(rubyHash, k, v);
229
196
  }
230
197
  return rubyHash;
231
198
  }
@@ -56,9 +56,7 @@ void SignalForwarder::callProc(const QVariantList &list)
56
56
  if (mProcRef.hasValue()) {
57
57
  withGvl([&] {
58
58
  auto args = RubyValue::from(list);
59
- protect([&] {
60
- rb_funcall2(mProcRef.value(), rb_intern("call"), RARRAY_LEN(VALUE(args)), RARRAY_PTR(VALUE(args)));
61
- });
59
+ rb_apply(mProcRef.value(), RUBYQML_INTERN("call"), args);
62
60
  });
63
61
  }
64
62
  }
@@ -16,32 +16,14 @@ void *rb_thread_call_without_gvl(void *(*func)(void *), void *data1, rb_unblock_
16
16
 
17
17
  namespace RubyQml {
18
18
 
19
- void protect(const std::function<void ()> &doAction)
19
+ void convertCppErrors(const std::function<void ()> &doAction) noexcept
20
20
  {
21
- auto callback = [](VALUE data) {
22
- auto &doAction= *reinterpret_cast<const std::function<void ()> *>(data);
23
- doAction();
24
- return Qnil;
25
- };
26
- int state;
27
- rb_protect(callback, reinterpret_cast<VALUE>(&doAction), &state);
28
- if (state) {
29
- throw RubyException(state);
30
- }
31
- }
32
-
33
- void unprotect(const std::function<void ()> &doAction) noexcept
34
- {
35
- int state = 0;
36
21
  bool cppErrorOccured= false;
37
22
  VALUE cppErrorClassName = Qnil;
38
23
  VALUE cppErrorMessage = Qnil;
39
24
  try {
40
25
  doAction();
41
26
  }
42
- catch (const RubyException &ex) {
43
- state = ex.state();
44
- }
45
27
  catch (const std::exception &ex) {
46
28
  cppErrorOccured = true;
47
29
  int status;
@@ -50,9 +32,6 @@ void unprotect(const std::function<void ()> &doAction) noexcept
50
32
  free(classname);
51
33
  cppErrorMessage = rb_str_new_cstr(ex.what());
52
34
  }
53
- if (state) {
54
- rb_jump_tag(state);
55
- }
56
35
  if (cppErrorOccured) {
57
36
  auto patterns = rb_funcall(rb_path2class("QML::ErrorConverter"), rb_intern("patterns"), 0);
58
37
  auto rubyClass = rb_hash_aref(patterns, cppErrorClassName);
@@ -127,9 +106,7 @@ void withGvl(const std::function<void ()> &doAction)
127
106
  void fail(const char *errorClassName, const QString &message)
128
107
  {
129
108
  auto msg = message.toUtf8();
130
- protect([&] {
131
- rb_raise(rb_path2class(errorClassName), "%s", msg.data());
132
- });
109
+ rb_raise(rb_path2class(errorClassName), "%s", msg.data());
133
110
  }
134
111
 
135
112
  }
@@ -54,27 +54,7 @@ private:
54
54
  int mState = 0;
55
55
  };
56
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;
57
+ void convertCppErrors(const std::function<void()> &doAction) noexcept;
78
58
 
79
59
  void rescue(const std::function<void ()> &doAction, const std::function<void (RubyValue)> &handleException);
80
60
  void rescueNotify(const std::function<void ()> &doAction);
@@ -32,11 +32,10 @@ WeakValueReference::WeakValueReference(RubyValue value) :
32
32
  d->mHasValue = true;
33
33
  d->mValue = value;
34
34
  static auto objspace = RubyModule::fromPath("ObjectSpace");
35
- protect([&] {
36
- auto proc = rb_proc_new((VALUE (*)(...))&Data::finalize, Ext_AnyWrapper::create(QVariant::fromValue(d)));
37
- VALUE args[] = { value };
38
- rb_funcall_with_block(objspace, RUBYQML_INTERN("define_finalizer"), 1, args, proc);
39
- });
35
+
36
+ auto proc = rb_proc_new((VALUE (*)(...))&Data::finalize, Ext_AnyWrapper::create(QVariant::fromValue(d)));
37
+ VALUE args[] = { value };
38
+ rb_funcall_with_block(objspace, RUBYQML_INTERN("define_finalizer"), 1, args, proc);
40
39
  }
41
40
 
42
41
  bool WeakValueReference::hasValue() const
@@ -11,13 +11,11 @@ module QML
11
11
  include Dispatchable
12
12
  include Wrappable
13
13
  # @!parse include Reactive::Object
14
- # @!parse include SignalInitialization
15
14
  # @!parse extend ClassMethods
16
15
 
17
16
  def self.included(derived)
18
17
  derived.class_eval do
19
18
  include Reactive::Object
20
- include SignalInitialization
21
19
  extend ClassMethods
22
20
  end
23
21
  end
@@ -113,26 +111,20 @@ module QML
113
111
  end
114
112
  end
115
113
 
116
- module SignalInitialization
117
- def initialize(*args, &block)
118
- super
119
- signal_names = signals + properties.map { |name| :"#{name}_changed" }
120
- signal_names.each do |name|
121
- __send__(name).connect do |*args|
122
- @access_wrappers.each do |obj|
123
- obj.class.meta_object.invoke_method(obj.pointer, name, args)
124
- end
125
- end
126
- end
127
- end
128
- end
129
-
130
114
  # @api private
131
115
  attr_reader :access_wrappers
132
116
 
133
117
  def initialize(*args, &block)
134
- super
118
+ signal_names = signals + properties.map { |name| :"#{name}_changed" }
119
+ signal_names.each do |name|
120
+ __send__(name).connect do |*args|
121
+ @access_wrappers.each do |obj|
122
+ obj.class.meta_object.invoke_method(obj.pointer, name, args)
123
+ end
124
+ end
125
+ end
135
126
  @access_wrappers = []
127
+ super
136
128
  end
137
129
 
138
130
  def create_wrapper
@@ -106,29 +106,26 @@ module QML
106
106
  end
107
107
  end
108
108
 
109
- # @overload application
110
- # Returns the instance of {Application}.
111
- # @return [Application]
112
- # @overload application
113
- # Call {init} if ruby-qml is not initialized, then yields the application instance and then call {Application#exec}.
114
- # @return [Application]
115
- # @example
116
- # QML.application do |app|
117
- # app.context[:foo] = 'foo'
118
- # app.load_path Pathname(__FILE__) + '../main.qml'
119
- # end
120
- # @see Application.instance
109
+ # Returns the instance of {Application}.
110
+ # @return [Application]
121
111
  def application
122
- if block_given?
123
- QML.init unless QML.initialized?
124
- Application.instance.tap do |app|
125
- yield app
126
- app.exec
127
- end
128
- else
129
- Application.instance
112
+ Application.instance
113
+ end
114
+
115
+ # Call {init} if ruby-qml is not initialized, then yields the application instance and then call {Application#exec}.
116
+ # @return [Application]
117
+ # @example
118
+ # QML.run do |app|
119
+ # app.context[:foo] = 'foo'
120
+ # app.load_path Pathname(__FILE__) + '../main.qml'
121
+ # end
122
+ def run
123
+ QML.init unless QML.initialized?
124
+ Application.instance.tap do |app|
125
+ yield app
126
+ app.exec
130
127
  end
131
128
  end
132
129
 
133
- module_function :application
130
+ module_function :application, :run
134
131
  end
@@ -10,7 +10,7 @@ module QML
10
10
  # @see http://qt-project.org/doc/qt-5/qqmlcontext.html QQmlContext(C++)
11
11
  #
12
12
  # @example
13
- # QML.application do |app|
13
+ # QML.run do |app|
14
14
  # app.context[:foo] = 'foo'
15
15
  # app.context[:bar] = 'bar'
16
16
  # ...
@@ -43,7 +43,6 @@ module QML
43
43
 
44
44
  on_init do
45
45
  Kernel.event_loop_hook_timer.timeout.connect do
46
- puts "======== run tasks"
47
46
  Dispatcher.instance.run_tasks
48
47
  end
49
48
  end
@@ -11,5 +11,6 @@ module QML
11
11
  end
12
12
 
13
13
  add_pattern 'RubyQml::QmlException', QMLError
14
+ add_pattern 'RubyQml::ConversionError', ConversionError
14
15
  end
15
16
  end
@@ -1,5 +1,5 @@
1
1
  require 'qml/qml'
2
- require 'qml/class_builder'
2
+ require 'qml/qt_object_base'
3
3
 
4
4
  module QML
5
5
  class MetaObject
@@ -12,9 +12,9 @@ module QML
12
12
  def build_class
13
13
  @@classes ||= {}
14
14
  klass = @@classes[name]
15
- builder = ClassBuilder.new(self, klass)
15
+ builder = QtObjectBase::SubclassBuilder.new(self, klass)
16
16
  builder.build
17
- @@classes[name] = builder.klass
17
+ @@classes[name] = builder.subclass
18
18
  end
19
19
  end
20
20
  end
@@ -1,7 +1,50 @@
1
1
  require 'qml/dispatchable'
2
+ require 'qml/name_helper'
2
3
 
3
4
  module QML
4
5
 
6
+ class QtPropertyBase
7
+ attr_reader :changed
8
+
9
+ def initialize(metaobj, objptr, name)
10
+ super()
11
+ @metaobj = metaobj
12
+ @objptr = objptr
13
+ @name = name
14
+ @changed = QtSignal.new(metaobj, objptr, @metaobj.notify_signal(@name))
15
+ end
16
+
17
+ def value=(newval)
18
+ @metaobj.set_property(@objptr, @name, newval)
19
+ end
20
+
21
+ def value
22
+ @metaobj.get_property(@objptr, @name)
23
+ end
24
+ end
25
+
26
+ class QtProperty < QtPropertyBase
27
+ include Reactive::Bindable
28
+ end
29
+
30
+ class QtSignal < Reactive::Signal
31
+ def initialize(metaobj, objptr, name)
32
+ super(nil)
33
+ @objptr = objptr
34
+ @metaobj = metaobj
35
+ @name = name
36
+ @initialized = false
37
+ end
38
+
39
+ private
40
+
41
+ def connected(block)
42
+ return if @initialized
43
+ @metaobj.connect_signal(@objptr, @name, method(:emit))
44
+ @initialized = true
45
+ end
46
+ end
47
+
5
48
  # {QtObjectBase} is the base class for Qt object wrappers.
6
49
  #
7
50
  # In ruby-qml you can access the followings of Qt objects in Ruby.
@@ -33,6 +76,93 @@ module QML
33
76
  include Dispatchable
34
77
  include Reactive::Object
35
78
 
79
+ # @api private
80
+ class SubclassBuilder
81
+
82
+ attr_reader :subclass
83
+
84
+ def initialize(metaobj, klass)
85
+ @metaobj = metaobj
86
+ @subclass = klass
87
+ end
88
+
89
+ def build
90
+ create unless @subclass
91
+ return if @subclass.meta_object == @metaobj
92
+
93
+ @metaobj.method_names.reject { |name| @metaobj.signal?(name) }.each do |name|
94
+ define_method(name)
95
+ end
96
+ @metaobj.method_names.select { |name| @metaobj.signal?(name) }.each do |name|
97
+ define_signal(name)
98
+ end
99
+ @metaobj.property_names.each do |name|
100
+ define_property(name)
101
+ end
102
+ @metaobj.enumerators.each do |k, v|
103
+ define_enum(k, v)
104
+ end
105
+ @subclass.__send__ :meta_object=, @metaobj
106
+
107
+ self
108
+ end
109
+
110
+ private
111
+
112
+ def create
113
+ super_metaobj = @metaobj.super_class
114
+ @subclass = Class.new(super_metaobj ? super_metaobj.build_class : QtObjectBase)
115
+ end
116
+
117
+ def define_method(name)
118
+ metaobj = @metaobj
119
+ return if metaobj.private?(name)
120
+ underscore = NameHelper.to_underscore(name)
121
+
122
+ @subclass.class_eval do
123
+ define_method name do |*args|
124
+ metaobj.invoke_method(@pointer, name, args)
125
+ end
126
+ private name if metaobj.protected?(name)
127
+ alias_method underscore, name unless underscore == name
128
+ end
129
+ end
130
+
131
+ def define_signal(name)
132
+ metaobj = @metaobj
133
+ underscore = NameHelper.to_underscore(name)
134
+ @subclass.class_eval do
135
+ variadic_signal name, signal: -> { QtSignal.new(metaobj, @pointer, name) }
136
+ alias_signal underscore, name unless underscore == name
137
+ end
138
+ end
139
+
140
+ def define_property(name)
141
+ metaobj = @metaobj
142
+ underscore = NameHelper.to_underscore(name)
143
+ @subclass.class_eval do
144
+ property name, nil, property: -> { QtProperty.new(metaobj, @pointer, name) }
145
+ alias_property underscore, name unless underscore == name
146
+ end
147
+ end
148
+
149
+ def define_enum(name, hash)
150
+ define_const(name, hash.values.sort)
151
+ hash.each do |k, v|
152
+ define_const(k, v)
153
+ end
154
+ end
155
+
156
+ def define_const(name, value)
157
+ name = (name[0].capitalize + name[1..-1]).to_sym
158
+ underscore = NameHelper.to_upper_underscore(name)
159
+ @subclass.class_eval do
160
+ const_set name, value
161
+ const_set underscore, value unless underscore == name
162
+ end
163
+ end
164
+ end
165
+
36
166
  class << self
37
167
  attr_accessor :meta_object
38
168
  private :meta_object=
@@ -41,11 +171,6 @@ module QML
41
171
  attr_accessor :pointer
42
172
  private :pointer=
43
173
 
44
- # @api private
45
- def custom_data
46
- @custom_data ||= {}
47
- end
48
-
49
174
  # Evaluates a JavaScript expression on the QML context of the object.
50
175
  # Fails with {QMLError} when the object is not created by QML and does not belong to any context.
51
176
  # @param [String] str