qt 0.1.6 → 0.1.7
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 +4 -4
- data/ext/qt_ruby_bridge/runtime_signals.cpp +27 -1
- data/lib/qt/event_runtime.rb +64 -12
- data/lib/qt/event_runtime_dispatch.rb +10 -3
- data/lib/qt/generated_setter_aliases_runtime.rb +37 -0
- data/lib/qt/generated_singleton_forwarders_runtime.rb +24 -0
- data/lib/qt/object_list_codec.rb +23 -0
- data/lib/qt/object_wrapper.rb +101 -6
- data/lib/qt/version.rb +1 -1
- data/lib/qt.rb +6 -1
- data/scripts/generate_bridge/auto_methods.rb +1 -0
- data/scripts/generate_bridge/cpp_method_return_emitter.rb +12 -0
- data/scripts/generate_bridge.rb +70 -12
- metadata +4 -2
- data/lib/qt/children_tracking.rb +0 -15
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: be7294fb17e8d0cd4eeb66cc86fe6ee91940a95d5a6f3afaf9dfa44fb54445ba
|
|
4
|
+
data.tar.gz: 20683a100daf8ca9299a2227ae2605acbde2393e2444d020625c058cbc76993c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 151a6423964153f8e5f54cba0cc7c54d1ff6b4a7fbd10b003d2a19f6263831493d18ed6fbaeabf88d5791d3e3326e356ba50e0d5332e212c75341bf6ea3c2f50
|
|
7
|
+
data.tar.gz: 9e7542fc4bc481b7c3110f4b55aa4b33dd327babbe4aaad3dc4078ce2bd404a97a90022f1e49d97bd3ae05cb805e4db8fd0aef13e368974505dc361d29edfdd9
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#include "qt_ruby_runtime.hpp"
|
|
2
2
|
|
|
3
3
|
#include <QByteArray>
|
|
4
|
+
#include <QCoreApplication>
|
|
4
5
|
#include <QDateTimeEdit>
|
|
5
6
|
#include <QMetaMethod>
|
|
6
7
|
#include <QObject>
|
|
@@ -29,6 +30,11 @@ std::unordered_map<QObject*, SignalHandlersByIndex>& signal_handlers() {
|
|
|
29
30
|
return handlers;
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
QObject* signal_callback_context() {
|
|
34
|
+
static QObject fallback_context;
|
|
35
|
+
return QCoreApplication::instance() ? static_cast<QObject*>(QCoreApplication::instance()) : &fallback_context;
|
|
36
|
+
}
|
|
37
|
+
|
|
32
38
|
int resolve_signal_index(QObject* obj, const char* signal_name) {
|
|
33
39
|
if (!obj || !signal_name) {
|
|
34
40
|
return -1;
|
|
@@ -122,7 +128,25 @@ int QtRubyRuntime::qobject_connect_signal(void* object_handle, const char* signa
|
|
|
122
128
|
}
|
|
123
129
|
|
|
124
130
|
const QMetaMethod signal_method = obj->metaObject()->method(signal_index);
|
|
125
|
-
|
|
131
|
+
if (signal_method.name() == "destroyed") {
|
|
132
|
+
QMetaObject::Connection direct_connection =
|
|
133
|
+
QObject::connect(obj, &QObject::destroyed, signal_callback_context(), [obj, signal_index](QObject*) {
|
|
134
|
+
if (!signal_callback_ref()) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
signal_callback_ref()(obj, signal_index, signal_payload_for(obj, signal_index));
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
if (!direct_connection) {
|
|
141
|
+
return -4;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
auto& by_index = signal_handlers()[obj];
|
|
145
|
+
by_index[signal_index].push_back(SignalHandler{signal_index, direct_connection, {}, nullptr});
|
|
146
|
+
return signal_index;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
auto* mapper = new QSignalMapper(QCoreApplication::instance());
|
|
126
150
|
mapper->setMapping(obj, signal_index);
|
|
127
151
|
|
|
128
152
|
const int map_slot_index = mapper->metaObject()->indexOfSlot("map()");
|
|
@@ -146,6 +170,8 @@ int QtRubyRuntime::qobject_connect_signal(void* object_handle, const char* signa
|
|
|
146
170
|
signal_callback_ref()(obj, mapped_signal_index, signal_payload_for(obj, mapped_signal_index));
|
|
147
171
|
});
|
|
148
172
|
|
|
173
|
+
QObject::connect(obj, &QObject::destroyed, mapper, [mapper]() { mapper->deleteLater(); });
|
|
174
|
+
|
|
149
175
|
if (!mapped_connection) {
|
|
150
176
|
QObject::disconnect(signal_connection);
|
|
151
177
|
mapper->deleteLater();
|
data/lib/qt/event_runtime.rb
CHANGED
|
@@ -30,22 +30,30 @@ module Qt
|
|
|
30
30
|
ensure_signal_callback!
|
|
31
31
|
|
|
32
32
|
handle = widget_handle(widget) || raise(ArgumentError, 'widget handle is required')
|
|
33
|
-
per_signal = prepare_signal_registration(handle, signal_name)
|
|
33
|
+
per_signal = prepare_signal_registration(@signal_handlers ||= {}, handle, signal_name)
|
|
34
34
|
per_signal[:blocks] << block
|
|
35
35
|
true
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
def
|
|
38
|
+
def on_internal_signal(widget, signal_name, &block)
|
|
39
|
+
raise ArgumentError, 'pass block to on_internal_signal' unless block
|
|
40
|
+
|
|
41
|
+
ensure_native_bridge_ready!
|
|
42
|
+
ensure_signal_callback!
|
|
43
|
+
|
|
44
|
+
handle = widget_handle(widget) || raise(ArgumentError, 'widget handle is required')
|
|
45
|
+
per_signal = prepare_signal_registration(@internal_signal_handlers ||= {}, handle, signal_name)
|
|
46
|
+
per_signal[:blocks] << block
|
|
47
|
+
true
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def prepare_signal_registration(signal_store, handle, signal_name)
|
|
39
51
|
signal_key = signal_name.to_s
|
|
40
52
|
raise ArgumentError, 'signal name is required' if signal_key.empty?
|
|
41
53
|
|
|
42
|
-
|
|
43
|
-
per_signal = ((@signal_handlers[handle.address] ||= {})[signal_key] ||= { index: nil, blocks: [] })
|
|
54
|
+
per_signal = ((signal_store[handle.address] ||= {})[signal_key] ||= { index: nil, blocks: [] })
|
|
44
55
|
if per_signal[:index].nil?
|
|
45
|
-
index =
|
|
46
|
-
raise ArgumentError, "failed to connect signal #{signal_key.inspect} (code=#{index})" if index.negative?
|
|
47
|
-
|
|
48
|
-
per_signal[:index] = index
|
|
56
|
+
per_signal[:index] = acquire_signal_registration(handle, signal_key)
|
|
49
57
|
end
|
|
50
58
|
per_signal
|
|
51
59
|
end
|
|
@@ -59,9 +67,14 @@ module Qt
|
|
|
59
67
|
return false if per_widget.nil?
|
|
60
68
|
|
|
61
69
|
signal_key = signal_name&.to_s
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
70
|
+
if signal_key
|
|
71
|
+
per_widget.delete(signal_key)
|
|
72
|
+
release_signal_registration(handle, signal_key)
|
|
73
|
+
else
|
|
74
|
+
per_widget.keys.each { |registered_signal| release_signal_registration(handle, registered_signal) }
|
|
75
|
+
per_widget.clear
|
|
76
|
+
end
|
|
77
|
+
@signal_handlers.delete(handle.address) if per_widget.empty?
|
|
65
78
|
true
|
|
66
79
|
end
|
|
67
80
|
|
|
@@ -103,7 +116,9 @@ module Qt
|
|
|
103
116
|
|
|
104
117
|
@signal_callback = FFI::Function.new(:void, %i[pointer int string]) do |object_handle, signal_index, payload|
|
|
105
118
|
normalized_payload = payload.nil? ? nil : Qt::StringCodec.from_qt_text(payload)
|
|
106
|
-
EventRuntimeDispatch.dispatch_signal(
|
|
119
|
+
EventRuntimeDispatch.dispatch_signal(
|
|
120
|
+
@internal_signal_handlers, @signal_handlers, object_handle, signal_index, normalized_payload
|
|
121
|
+
)
|
|
107
122
|
end
|
|
108
123
|
|
|
109
124
|
Qt::Native.set_signal_callback(@signal_callback)
|
|
@@ -141,5 +156,42 @@ module Qt
|
|
|
141
156
|
@event_handlers.delete(handle.address)
|
|
142
157
|
end
|
|
143
158
|
end
|
|
159
|
+
|
|
160
|
+
def acquire_signal_registration(handle, signal_key)
|
|
161
|
+
@signal_registrations ||= {}
|
|
162
|
+
per_widget = (@signal_registrations[handle.address] ||= {})
|
|
163
|
+
registration = (per_widget[signal_key] ||= { index: nil, refcount: 0 })
|
|
164
|
+
if registration[:index].nil?
|
|
165
|
+
index = Qt::Native.qobject_connect_signal(handle, signal_key)
|
|
166
|
+
raise ArgumentError, "failed to connect signal #{signal_key.inspect} (code=#{index})" if index.negative?
|
|
167
|
+
|
|
168
|
+
registration[:index] = index
|
|
169
|
+
end
|
|
170
|
+
registration[:refcount] += 1
|
|
171
|
+
registration[:index]
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def release_signal_registration(handle, signal_key)
|
|
175
|
+
return if @signal_registrations.nil?
|
|
176
|
+
|
|
177
|
+
per_widget = @signal_registrations[handle.address]
|
|
178
|
+
return if per_widget.nil?
|
|
179
|
+
|
|
180
|
+
registration = per_widget[signal_key]
|
|
181
|
+
return if registration.nil?
|
|
182
|
+
|
|
183
|
+
registration[:refcount] -= 1
|
|
184
|
+
return if registration[:refcount].positive?
|
|
185
|
+
|
|
186
|
+
Qt::Native.qobject_disconnect_signal(handle, signal_key)
|
|
187
|
+
per_widget.delete(signal_key)
|
|
188
|
+
@signal_registrations.delete(handle.address) if per_widget.empty?
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def clear_signal_registrations_for_address(address)
|
|
192
|
+
@signal_handlers&.delete(address)
|
|
193
|
+
@internal_signal_handlers&.delete(address)
|
|
194
|
+
@signal_registrations&.delete(address)
|
|
195
|
+
end
|
|
144
196
|
end
|
|
145
197
|
end
|
|
@@ -26,8 +26,15 @@ module Qt
|
|
|
26
26
|
EVENT_RESULT_CONTINUE
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
def dispatch_signal(signal_handlers, object_handle, signal_index, payload)
|
|
30
|
-
return unless object_handle
|
|
29
|
+
def dispatch_signal(internal_signal_handlers, signal_handlers, object_handle, signal_index, payload)
|
|
30
|
+
return unless object_handle
|
|
31
|
+
|
|
32
|
+
dispatch_signal_store(signal_handlers, object_handle, signal_index, payload)
|
|
33
|
+
dispatch_signal_store(internal_signal_handlers, object_handle, signal_index, payload)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def dispatch_signal_store(signal_handlers, object_handle, signal_index, payload)
|
|
37
|
+
return unless signal_handlers
|
|
31
38
|
|
|
32
39
|
per_widget = signal_handlers[object_handle.address]
|
|
33
40
|
return unless per_widget
|
|
@@ -36,7 +43,7 @@ module Qt
|
|
|
36
43
|
next unless entry[:index] == signal_index
|
|
37
44
|
|
|
38
45
|
typed_payload = Qt::DateTimeCodec.decode_for_signal(signal_name, payload)
|
|
39
|
-
entry[:blocks].each { |handler| handler.call(typed_payload) }
|
|
46
|
+
entry[:blocks].dup.each { |handler| handler.call(typed_payload) }
|
|
40
47
|
end
|
|
41
48
|
end
|
|
42
49
|
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Qt
|
|
4
|
+
module GeneratedSetterAliasesRuntime
|
|
5
|
+
module_function
|
|
6
|
+
|
|
7
|
+
def apply!(qt_module)
|
|
8
|
+
qt_module.constants(false).each do |const_name|
|
|
9
|
+
klass = qt_module.const_get(const_name, false)
|
|
10
|
+
next unless klass.is_a?(Class)
|
|
11
|
+
|
|
12
|
+
apply_instance_aliases!(klass)
|
|
13
|
+
apply_singleton_aliases!(klass)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def apply_instance_aliases!(klass)
|
|
18
|
+
apply_aliases_to_target!(klass, klass, :QT_API_SETTER_ALIASES)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def apply_singleton_aliases!(klass)
|
|
22
|
+
apply_aliases_to_target!(klass.singleton_class, klass, :QT_API_SINGLETON_SETTER_ALIASES)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def apply_aliases_to_target!(target, owner, constant_name)
|
|
26
|
+
return unless owner.const_defined?(constant_name, false)
|
|
27
|
+
|
|
28
|
+
owner.const_get(constant_name, false).each do |alias_name, setter_name|
|
|
29
|
+
next if target.method_defined?(alias_name) || target.private_method_defined?(alias_name)
|
|
30
|
+
|
|
31
|
+
target.send(:define_method, alias_name) do |value|
|
|
32
|
+
public_send(setter_name, value)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Qt
|
|
4
|
+
module GeneratedSingletonForwardersRuntime
|
|
5
|
+
module_function
|
|
6
|
+
|
|
7
|
+
def apply!(qt_module)
|
|
8
|
+
qt_module.constants(false).each do |const_name|
|
|
9
|
+
klass = qt_module.const_get(const_name, false)
|
|
10
|
+
next unless klass.is_a?(Class)
|
|
11
|
+
next unless klass.const_defined?(:QT_API_SINGLETON_FORWARDERS, false)
|
|
12
|
+
|
|
13
|
+
klass.const_get(:QT_API_SINGLETON_FORWARDERS, false).each do |method_name|
|
|
14
|
+
next if klass.instance_methods(false).include?(method_name.to_sym)
|
|
15
|
+
next if klass.private_instance_methods(false).include?(method_name.to_sym)
|
|
16
|
+
|
|
17
|
+
klass.send(:define_method, method_name) do |*args, &block|
|
|
18
|
+
self.class.public_send(method_name, *args, &block)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
5
|
+
module Qt
|
|
6
|
+
# Decodes QObjectList bridge payloads into canonical Ruby wrappers.
|
|
7
|
+
module ObjectListCodec
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
def decode(payload, expected_qt_class = 'QObject')
|
|
11
|
+
raw = Qt::StringCodec.from_qt_text(payload.to_s)
|
|
12
|
+
return [] if raw.empty?
|
|
13
|
+
|
|
14
|
+
JSON.parse(raw).filter_map do |address|
|
|
15
|
+
next if address.nil? || address.to_s.empty?
|
|
16
|
+
|
|
17
|
+
Qt::ObjectWrapper.wrap(FFI::Pointer.new(Integer(address, 10)), expected_qt_class)
|
|
18
|
+
end
|
|
19
|
+
rescue JSON::ParserError, ArgumentError
|
|
20
|
+
[]
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/qt/object_wrapper.rb
CHANGED
|
@@ -5,14 +5,24 @@ module Qt
|
|
|
5
5
|
module ObjectWrapper
|
|
6
6
|
module_function
|
|
7
7
|
|
|
8
|
+
module ConstructorCacheHook
|
|
9
|
+
def initialize(*args, &block)
|
|
10
|
+
super
|
|
11
|
+
Qt::ObjectWrapper.register_wrapper(self)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
8
15
|
def wrap(pointer, expected_qt_class = nil)
|
|
9
16
|
return nil if null_pointer?(pointer)
|
|
10
17
|
return pointer if pointer.respond_to?(:handle)
|
|
11
18
|
|
|
19
|
+
cached = cached_wrapper_for(pointer)
|
|
20
|
+
return cached if cached
|
|
21
|
+
|
|
12
22
|
klass = resolve_wrapper_class(pointer, expected_qt_class) || fallback_wrapper_class(expected_qt_class)
|
|
13
23
|
return pointer unless klass
|
|
14
24
|
|
|
15
|
-
instantiate_wrapper(klass, pointer)
|
|
25
|
+
register_wrapper(instantiate_wrapper(klass, pointer))
|
|
16
26
|
end
|
|
17
27
|
|
|
18
28
|
def null_pointer?(pointer)
|
|
@@ -26,9 +36,10 @@ module Qt
|
|
|
26
36
|
end
|
|
27
37
|
|
|
28
38
|
def candidate_wrapper_classes(expected_qt_class)
|
|
39
|
+
normalized_qt_class = normalize_expected_qt_class(expected_qt_class)
|
|
29
40
|
@candidate_wrapper_classes ||= {}
|
|
30
|
-
@candidate_wrapper_classes[
|
|
31
|
-
base = fallback_wrapper_class(
|
|
41
|
+
@candidate_wrapper_classes[normalized_qt_class] ||= begin
|
|
42
|
+
base = fallback_wrapper_class(normalized_qt_class)
|
|
32
43
|
wrappers = qobject_wrapper_classes
|
|
33
44
|
wrappers = wrappers.select { |klass| klass <= base } if base
|
|
34
45
|
wrappers.sort_by { |klass| -inheritance_depth(klass) }
|
|
@@ -47,23 +58,95 @@ module Qt
|
|
|
47
58
|
end
|
|
48
59
|
|
|
49
60
|
def fallback_wrapper_class(expected_qt_class)
|
|
50
|
-
|
|
61
|
+
normalized_qt_class = normalize_expected_qt_class(expected_qt_class)
|
|
62
|
+
return nil unless normalized_qt_class
|
|
63
|
+
return nil unless Qt.const_defined?(normalized_qt_class, false)
|
|
51
64
|
|
|
52
|
-
klass = Qt.const_get(
|
|
65
|
+
klass = Qt.const_get(normalized_qt_class, false)
|
|
53
66
|
return nil unless klass.is_a?(Class)
|
|
54
67
|
return nil unless klass.const_defined?(:QT_CLASS, false)
|
|
55
68
|
return nil unless klass <= Qt::QObject
|
|
56
69
|
|
|
57
70
|
klass
|
|
71
|
+
rescue NameError
|
|
72
|
+
nil
|
|
58
73
|
end
|
|
59
74
|
|
|
60
75
|
def instantiate_wrapper(klass, pointer)
|
|
61
76
|
wrapped = klass.allocate
|
|
62
77
|
wrapped.instance_variable_set(:@handle, pointer)
|
|
63
|
-
wrapped.init_children_tracking! if wrapped.respond_to?(:init_children_tracking!, true)
|
|
64
78
|
wrapped
|
|
65
79
|
end
|
|
66
80
|
|
|
81
|
+
def cached_wrapper_for(pointer)
|
|
82
|
+
wrapper_cache[pointer.address]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def register_wrapper(wrapper)
|
|
86
|
+
return wrapper unless wrapper.respond_to?(:handle)
|
|
87
|
+
|
|
88
|
+
pointer = wrapper.handle
|
|
89
|
+
return wrapper if null_pointer?(pointer)
|
|
90
|
+
|
|
91
|
+
cached = cached_wrapper_for(pointer)
|
|
92
|
+
return cached if cached
|
|
93
|
+
|
|
94
|
+
cache_wrapper(wrapper)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def cache_wrapper(wrapper)
|
|
98
|
+
pointer = wrapper.handle
|
|
99
|
+
wrapper_cache[pointer.address] = wrapper
|
|
100
|
+
ensure_destroy_hook(wrapper, pointer)
|
|
101
|
+
wrapper
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def invalidate_cached_wrapper(pointer_or_address, expected_wrapper = nil)
|
|
105
|
+
address = pointer_or_address.is_a?(Integer) ? pointer_or_address : pointer_or_address.address
|
|
106
|
+
cached = wrapper_cache[address]
|
|
107
|
+
return unless cached
|
|
108
|
+
return if expected_wrapper && !cached.equal?(expected_wrapper)
|
|
109
|
+
|
|
110
|
+
wrapper_cache.delete(address)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def reset_cache!
|
|
114
|
+
@wrapper_cache = {}
|
|
115
|
+
@destroy_hook_addresses = {}
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def install_constructor_cache_hooks!
|
|
119
|
+
qobject_wrapper_classes.each do |klass|
|
|
120
|
+
next if klass.instance_variable_defined?(:@__qt_object_wrapper_constructor_hook_installed)
|
|
121
|
+
|
|
122
|
+
klass.prepend(ConstructorCacheHook)
|
|
123
|
+
klass.instance_variable_set(:@__qt_object_wrapper_constructor_hook_installed, true)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def wrapper_cache
|
|
128
|
+
@wrapper_cache ||= {}
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def destroy_hook_addresses
|
|
132
|
+
@destroy_hook_addresses ||= {}
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def ensure_destroy_hook(wrapper, pointer)
|
|
136
|
+
address = pointer.address
|
|
137
|
+
return if destroy_hook_addresses[address]
|
|
138
|
+
|
|
139
|
+
destroy_hook_addresses[address] = true
|
|
140
|
+
Qt::EventRuntime.on_internal_signal(pointer, 'destroyed()') do |_payload|
|
|
141
|
+
destroy_hook_addresses.delete(address)
|
|
142
|
+
Qt::EventRuntime.clear_signal_registrations_for_address(address)
|
|
143
|
+
invalidate_cached_wrapper(address, wrapper)
|
|
144
|
+
end
|
|
145
|
+
rescue StandardError
|
|
146
|
+
destroy_hook_addresses.delete(address)
|
|
147
|
+
raise
|
|
148
|
+
end
|
|
149
|
+
|
|
67
150
|
def inheritance_depth(klass)
|
|
68
151
|
depth = 0
|
|
69
152
|
current = klass
|
|
@@ -73,5 +156,17 @@ module Qt
|
|
|
73
156
|
end
|
|
74
157
|
depth
|
|
75
158
|
end
|
|
159
|
+
|
|
160
|
+
def normalize_expected_qt_class(expected_qt_class)
|
|
161
|
+
return nil if expected_qt_class.nil?
|
|
162
|
+
|
|
163
|
+
value = expected_qt_class.to_s.strip
|
|
164
|
+
return nil if value.empty?
|
|
165
|
+
|
|
166
|
+
value = value.delete_prefix('Qt::')
|
|
167
|
+
return nil unless value.match?(/\A[A-Z]\w*\z/)
|
|
168
|
+
|
|
169
|
+
value
|
|
170
|
+
end
|
|
76
171
|
end
|
|
77
172
|
end
|
data/lib/qt/version.rb
CHANGED
data/lib/qt.rb
CHANGED
|
@@ -22,18 +22,23 @@ require_relative 'qt/constants'
|
|
|
22
22
|
require_relative 'qt/string_codec'
|
|
23
23
|
require_relative 'qt/date_time_codec'
|
|
24
24
|
require_relative 'qt/key_sequence_codec'
|
|
25
|
+
require_relative 'qt/object_list_codec'
|
|
25
26
|
require_relative 'qt/variant_codec'
|
|
26
27
|
require_relative 'qt/inspectable'
|
|
27
|
-
require_relative 'qt/children_tracking'
|
|
28
28
|
require_relative 'qt/object_wrapper'
|
|
29
29
|
require_relative 'qt/application_lifecycle'
|
|
30
30
|
require_relative 'qt/bridge'
|
|
31
31
|
require_relative 'qt/native'
|
|
32
|
+
require_relative 'qt/generated_setter_aliases_runtime'
|
|
33
|
+
require_relative 'qt/generated_singleton_forwarders_runtime'
|
|
32
34
|
require_relative 'qt/event_runtime_dispatch'
|
|
33
35
|
require_relative 'qt/event_runtime_qobject_methods'
|
|
34
36
|
require_relative 'qt/event_runtime'
|
|
35
37
|
require GENERATED_EVENT_PAYLOADS
|
|
36
38
|
require GENERATED_WIDGETS
|
|
39
|
+
Qt::GeneratedSetterAliasesRuntime.apply!(Qt)
|
|
40
|
+
Qt::GeneratedSingletonForwardersRuntime.apply!(Qt)
|
|
41
|
+
Qt::ObjectWrapper.install_constructor_cache_hooks!
|
|
37
42
|
require_relative 'qt/shortcut_compat'
|
|
38
43
|
Qt::GeneratedConstantsRuntime.apply_generated_scoped_constants!(Qt)
|
|
39
44
|
|
|
@@ -81,6 +81,7 @@ def map_scalar_cpp_return_type(type)
|
|
|
81
81
|
return { ffi_return: :int } if type == 'int'
|
|
82
82
|
return { ffi_return: :bool } if type == 'bool'
|
|
83
83
|
return { ffi_return: :string, return_cast: :qstring_to_utf8 } if type == 'QString'
|
|
84
|
+
return { ffi_return: :string, return_cast: :qobject_list_to_wrapped_array, object_list_class: 'QObject' } if type == 'QObjectList'
|
|
84
85
|
return { ffi_return: :string, return_cast: :qdatetime_to_utf8 } if type == 'QDateTime'
|
|
85
86
|
return { ffi_return: :string, return_cast: :qdate_to_utf8 } if type == 'QDate'
|
|
86
87
|
return { ffi_return: :string, return_cast: :qtime_to_utf8 } if type == 'QTime'
|
|
@@ -11,6 +11,7 @@ class CppMethodReturnEmitter
|
|
|
11
11
|
def emit
|
|
12
12
|
return emit_void if method[:ffi_return] == :void
|
|
13
13
|
return emit_qstring if qstring_return?
|
|
14
|
+
return emit_qobject_list if qobject_list_return?
|
|
14
15
|
return emit_qdatetime if qdatetime_return?
|
|
15
16
|
return emit_qdate if qdate_return?
|
|
16
17
|
return emit_qtime if qtime_return?
|
|
@@ -32,6 +33,10 @@ class CppMethodReturnEmitter
|
|
|
32
33
|
method[:ffi_return] == :string && method[:return_cast] == :qvariant_to_utf8
|
|
33
34
|
end
|
|
34
35
|
|
|
36
|
+
def qobject_list_return?
|
|
37
|
+
method[:ffi_return] == :string && method[:return_cast] == :qobject_list_to_wrapped_array
|
|
38
|
+
end
|
|
39
|
+
|
|
35
40
|
def qdatetime_return?
|
|
36
41
|
method[:ffi_return] == :string && method[:return_cast] == :qdatetime_to_utf8
|
|
37
42
|
end
|
|
@@ -62,6 +67,13 @@ class CppMethodReturnEmitter
|
|
|
62
67
|
lines << ' return utf8.constData();'
|
|
63
68
|
end
|
|
64
69
|
|
|
70
|
+
def emit_qobject_list
|
|
71
|
+
lines << " const QObjectList value = #{invocation};"
|
|
72
|
+
lines << ' thread_local QByteArray utf8;'
|
|
73
|
+
lines << ' utf8 = qobject_list_to_bridge_string(value).toUtf8();'
|
|
74
|
+
lines << ' return utf8.constData();'
|
|
75
|
+
end
|
|
76
|
+
|
|
65
77
|
def emit_qdatetime
|
|
66
78
|
lines << " const QDateTime value = #{invocation};"
|
|
67
79
|
lines << ' thread_local QByteArray utf8;'
|
data/scripts/generate_bridge.rb
CHANGED
|
@@ -549,6 +549,14 @@ def cpp_bridge_prelude
|
|
|
549
549
|
const QByteArray fallback = value.toString().toUtf8().toBase64();
|
|
550
550
|
return QStringLiteral("qtv:str:") + QString::fromUtf8(fallback);
|
|
551
551
|
}
|
|
552
|
+
|
|
553
|
+
QString qobject_list_to_bridge_string(const QObjectList& value) {
|
|
554
|
+
QJsonArray array;
|
|
555
|
+
for (QObject* object : value) {
|
|
556
|
+
array.append(QString::number(reinterpret_cast<quintptr>(object)));
|
|
557
|
+
}
|
|
558
|
+
return QString::fromUtf8(QJsonDocument(array).toJson(QJsonDocument::Compact));
|
|
559
|
+
}
|
|
552
560
|
} // namespace
|
|
553
561
|
CPP
|
|
554
562
|
end
|
|
@@ -590,14 +598,19 @@ def ruby_api_metadata(methods)
|
|
|
590
598
|
ruby_method_names = methods.flat_map do |method|
|
|
591
599
|
ruby_name = ruby_safe_method_name(method[:ruby_name])
|
|
592
600
|
snake = to_snake(ruby_name)
|
|
593
|
-
snake == ruby_name ? [ruby_name] : [ruby_name, snake]
|
|
601
|
+
names = snake == ruby_name ? [ruby_name] : [ruby_name, snake]
|
|
602
|
+
names.concat(setter_alias_method_names(method))
|
|
603
|
+
names
|
|
594
604
|
end.uniq
|
|
595
605
|
properties = methods.filter_map { |method| method[:property] }.uniq
|
|
596
606
|
|
|
597
607
|
{
|
|
598
608
|
qt_method_names: qt_method_names,
|
|
599
609
|
ruby_method_names: ruby_method_names,
|
|
600
|
-
properties: properties
|
|
610
|
+
properties: properties,
|
|
611
|
+
setter_aliases: {},
|
|
612
|
+
singleton_setter_aliases: {},
|
|
613
|
+
singleton_forwarders: []
|
|
601
614
|
}
|
|
602
615
|
end
|
|
603
616
|
|
|
@@ -606,6 +619,9 @@ def append_ruby_class_api_constants(lines, qt_class:, metadata:, indent:)
|
|
|
606
619
|
lines << "#{indent}QT_API_QT_METHODS = #{metadata[:qt_method_names].inspect}.freeze"
|
|
607
620
|
lines << "#{indent}QT_API_RUBY_METHODS = #{metadata[:ruby_method_names].map(&:to_sym).inspect}.freeze"
|
|
608
621
|
lines << "#{indent}QT_API_PROPERTIES = #{metadata[:properties].map(&:to_sym).inspect}.freeze"
|
|
622
|
+
lines << "#{indent}QT_API_SETTER_ALIASES = #{metadata[:setter_aliases].inspect}.freeze"
|
|
623
|
+
lines << "#{indent}QT_API_SINGLETON_SETTER_ALIASES = #{metadata[:singleton_setter_aliases].inspect}.freeze"
|
|
624
|
+
lines << "#{indent}QT_API_SINGLETON_FORWARDERS = #{metadata[:singleton_forwarders].inspect}.freeze"
|
|
609
625
|
end
|
|
610
626
|
|
|
611
627
|
def ruby_method_arguments(method, arg_map, required_arg_count)
|
|
@@ -675,6 +691,9 @@ end
|
|
|
675
691
|
|
|
676
692
|
def ruby_native_method_body(method, rewritten_native_call)
|
|
677
693
|
return "Qt::StringCodec.from_qt_text(#{rewritten_native_call})" if method[:return_cast] == :qstring_to_utf8
|
|
694
|
+
if method[:return_cast] == :qobject_list_to_wrapped_array
|
|
695
|
+
return "Qt::ObjectListCodec.decode(#{rewritten_native_call}, '#{method[:object_list_class]}')"
|
|
696
|
+
end
|
|
678
697
|
return "Qt::VariantCodec.decode(#{rewritten_native_call})" if method[:return_cast] == :qvariant_to_utf8
|
|
679
698
|
return "Qt::DateTimeCodec.decode_qdatetime(#{rewritten_native_call})" if method[:return_cast] == :qdatetime_to_utf8
|
|
680
699
|
return "Qt::DateTimeCodec.decode_qdate(#{rewritten_native_call})" if method[:return_cast] == :qdate_to_utf8
|
|
@@ -694,6 +713,39 @@ def append_ruby_property_writer(lines, method:, indent:)
|
|
|
694
713
|
lines << "#{indent}alias_method :#{snake_property}=, :#{method[:property]}=" if snake_property != method[:property]
|
|
695
714
|
end
|
|
696
715
|
|
|
716
|
+
def setter_alias_base_name(method)
|
|
717
|
+
return nil unless method[:args].length == 1
|
|
718
|
+
return nil if method[:property]
|
|
719
|
+
|
|
720
|
+
property_name_from_setter(method[:qt_name] || method[:ruby_name].to_s)
|
|
721
|
+
end
|
|
722
|
+
|
|
723
|
+
def setter_alias_method_names(method)
|
|
724
|
+
setter_alias_specs(method).keys
|
|
725
|
+
end
|
|
726
|
+
|
|
727
|
+
def setter_alias_specs(method)
|
|
728
|
+
base_name = setter_alias_base_name(method)
|
|
729
|
+
return {} unless base_name
|
|
730
|
+
|
|
731
|
+
ruby_target = ruby_public_method_name(base_name)
|
|
732
|
+
snake_target = to_snake(ruby_target)
|
|
733
|
+
setter_method_name = ruby_safe_method_name(method[:ruby_name])
|
|
734
|
+
aliases = { "#{ruby_target}=" => setter_method_name }
|
|
735
|
+
aliases["#{snake_target}="] = setter_method_name if snake_target != ruby_target
|
|
736
|
+
aliases
|
|
737
|
+
end
|
|
738
|
+
|
|
739
|
+
def qapplication_singleton_forwarder_names(methods, singleton_setter_aliases)
|
|
740
|
+
method_names = methods.flat_map do |method|
|
|
741
|
+
ruby_name = ruby_safe_method_name(method[:ruby_name])
|
|
742
|
+
snake_alias = to_snake(ruby_name)
|
|
743
|
+
snake_alias == ruby_name ? [ruby_name] : [ruby_name, snake_alias]
|
|
744
|
+
end
|
|
745
|
+
|
|
746
|
+
(method_names + singleton_setter_aliases.keys).uniq
|
|
747
|
+
end
|
|
748
|
+
|
|
697
749
|
def append_widget_initializer(lines, spec:, widget_root:, indent:)
|
|
698
750
|
if spec[:constructor][:mode] == :string_path
|
|
699
751
|
append_string_path_initializer(lines, spec, indent)
|
|
@@ -712,7 +764,6 @@ end
|
|
|
712
764
|
def append_parent_widget_initializer(lines, spec, widget_root, indent)
|
|
713
765
|
lines << "#{indent}def initialize(parent = nil)"
|
|
714
766
|
lines << "#{indent} @handle = Native.#{spec[:prefix]}_new(parent&.handle)"
|
|
715
|
-
lines << "#{indent} init_children_tracking!" if widget_root
|
|
716
767
|
append_parent_registration_logic(lines, spec, indent)
|
|
717
768
|
end
|
|
718
769
|
|
|
@@ -733,20 +784,15 @@ def append_keysequence_parent_initializer(lines, spec, widget_root, indent)
|
|
|
733
784
|
lines << "#{indent} key = nil"
|
|
734
785
|
lines << "#{indent} end"
|
|
735
786
|
lines << "#{indent} @handle = Native.#{spec[:prefix]}_new(Qt::KeySequenceCodec.encode(key), parent&.handle)"
|
|
736
|
-
lines << "#{indent} init_children_tracking!" if widget_root
|
|
737
787
|
append_parent_registration_logic(lines, spec, indent)
|
|
738
788
|
end
|
|
739
789
|
|
|
740
790
|
def append_parent_registration_logic(lines, spec, indent)
|
|
741
791
|
if spec[:ruby_class] == 'QWidget'
|
|
742
|
-
lines << "#{indent}
|
|
743
|
-
lines << "#{indent} parent.add_child(self)"
|
|
744
|
-
lines << "#{indent} else"
|
|
792
|
+
lines << "#{indent} unless parent"
|
|
745
793
|
lines << "#{indent} app = QApplication.current"
|
|
746
794
|
lines << "#{indent} app&.register_window(self)"
|
|
747
795
|
lines << "#{indent} end"
|
|
748
|
-
elsif spec[:constructor][:register_in_parent]
|
|
749
|
-
lines << "#{indent} parent.add_child(self) if parent&.respond_to?(:add_child)"
|
|
750
796
|
end
|
|
751
797
|
end
|
|
752
798
|
|
|
@@ -773,6 +819,13 @@ end
|
|
|
773
819
|
|
|
774
820
|
def generate_ruby_qapplication(lines, spec)
|
|
775
821
|
metadata = ruby_api_metadata(spec[:methods])
|
|
822
|
+
metadata[:singleton_setter_aliases] = Array(spec[:class_methods]).each_with_object({}) do |method, aliases|
|
|
823
|
+
aliases.merge!(setter_alias_specs(method))
|
|
824
|
+
end
|
|
825
|
+
metadata[:singleton_forwarders] = qapplication_singleton_forwarder_names(
|
|
826
|
+
Array(spec[:class_methods]),
|
|
827
|
+
metadata[:singleton_setter_aliases]
|
|
828
|
+
)
|
|
776
829
|
|
|
777
830
|
append_ruby_qapplication_prelude(lines, spec, metadata)
|
|
778
831
|
append_ruby_qapplication_singleton_accessors(lines)
|
|
@@ -826,6 +879,9 @@ end
|
|
|
826
879
|
|
|
827
880
|
def qapplication_class_method_body(method, native_call)
|
|
828
881
|
return " Qt::StringCodec.from_qt_text(#{native_call})" if method[:return_cast] == :qstring_to_utf8
|
|
882
|
+
if method[:return_cast] == :qobject_list_to_wrapped_array
|
|
883
|
+
return " Qt::ObjectListCodec.decode(#{native_call}, '#{method[:object_list_class]}')"
|
|
884
|
+
end
|
|
829
885
|
return " Qt::DateTimeCodec.decode_qdatetime(#{native_call})" if method[:return_cast] == :qdatetime_to_utf8
|
|
830
886
|
return " Qt::DateTimeCodec.decode_qdate(#{native_call})" if method[:return_cast] == :qdate_to_utf8
|
|
831
887
|
return " Qt::DateTimeCodec.decode_qtime(#{native_call})" if method[:return_cast] == :qtime_to_utf8
|
|
@@ -842,9 +898,7 @@ def generate_ruby_widget_class_header(lines, spec, metadata:, super_ruby:, class
|
|
|
842
898
|
append_ruby_class_api_constants(lines, qt_class: spec[:qt_class], metadata: metadata, indent: ' ')
|
|
843
899
|
lines << ''
|
|
844
900
|
lines << ' attr_reader :handle'
|
|
845
|
-
lines << ' attr_reader :children' if widget_root
|
|
846
901
|
lines << ' include Inspectable'
|
|
847
|
-
lines << ' include ChildrenTracking' if widget_root
|
|
848
902
|
lines << ' include EventRuntime::QObjectMethods' if qobject_based
|
|
849
903
|
lines << ''
|
|
850
904
|
end
|
|
@@ -883,7 +937,11 @@ end
|
|
|
883
937
|
def ruby_api_metadata_for_spec(spec, specs_by_qt, super_qt_by_qt)
|
|
884
938
|
inherited_methods = inherited_methods_for_spec(spec, specs_by_qt, super_qt_by_qt)
|
|
885
939
|
all_methods = (inherited_methods + spec[:methods]).uniq { |method| method[:qt_name] }
|
|
886
|
-
ruby_api_metadata(all_methods)
|
|
940
|
+
metadata = ruby_api_metadata(all_methods)
|
|
941
|
+
metadata[:setter_aliases] = all_methods.each_with_object({}) do |method, aliases|
|
|
942
|
+
aliases.merge!(setter_alias_specs(method))
|
|
943
|
+
end
|
|
944
|
+
metadata
|
|
887
945
|
end
|
|
888
946
|
|
|
889
947
|
def ruby_super_class_for_spec(spec, super_qt_by_qt, qt_to_ruby)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: qt
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Maksim Veynberg
|
|
@@ -50,7 +50,6 @@ files:
|
|
|
50
50
|
- lib/qt.rb
|
|
51
51
|
- lib/qt/application_lifecycle.rb
|
|
52
52
|
- lib/qt/bridge.rb
|
|
53
|
-
- lib/qt/children_tracking.rb
|
|
54
53
|
- lib/qt/constants.rb
|
|
55
54
|
- lib/qt/date_time_codec.rb
|
|
56
55
|
- lib/qt/errors.rb
|
|
@@ -58,9 +57,12 @@ files:
|
|
|
58
57
|
- lib/qt/event_runtime_dispatch.rb
|
|
59
58
|
- lib/qt/event_runtime_qobject_methods.rb
|
|
60
59
|
- lib/qt/generated_constants_runtime.rb
|
|
60
|
+
- lib/qt/generated_setter_aliases_runtime.rb
|
|
61
|
+
- lib/qt/generated_singleton_forwarders_runtime.rb
|
|
61
62
|
- lib/qt/inspectable.rb
|
|
62
63
|
- lib/qt/key_sequence_codec.rb
|
|
63
64
|
- lib/qt/native.rb
|
|
65
|
+
- lib/qt/object_list_codec.rb
|
|
64
66
|
- lib/qt/object_wrapper.rb
|
|
65
67
|
- lib/qt/shortcut_compat.rb
|
|
66
68
|
- lib/qt/string_codec.rb
|
data/lib/qt/children_tracking.rb
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Qt
|
|
4
|
-
# Child object tracking to mirror Qt parent/child ownership in Ruby.
|
|
5
|
-
module ChildrenTracking
|
|
6
|
-
def init_children_tracking!
|
|
7
|
-
@children = []
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def add_child(child)
|
|
11
|
-
@children ||= []
|
|
12
|
-
@children << child
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|