qt 0.1.7 → 0.1.9
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/README.md +6 -0
- data/ext/qt_ruby_bridge/extconf.rb +28 -12
- data/lib/qt/object_wrapper.rb +37 -2
- data/lib/qt/version.rb +1 -1
- data/scripts/generate_bridge/auto_methods.rb +2 -1
- data/scripts/generate_bridge/core_utils.rb +5 -1
- data/scripts/generate_bridge/ffi_api.rb +2 -0
- data/scripts/generate_bridge/spec_discovery.rb +146 -20
- data/scripts/generate_bridge.rb +67 -6
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 50d48e8571c76d7413f893a6189b7670629bd3a80123a131bf9cc00ffe6d818e
|
|
4
|
+
data.tar.gz: bcb0081e9ebabc04175f7f041fdf8442293d08191e5ee6e2b26164d32c730942
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 82bde56dd8d499bfc122923554109e6f7f92dfea399ed6fd2f821737c29cfb487b0025908409273aeb13df2ca9ed57a36eb524142a968c24a84eba43af4836b3
|
|
7
|
+
data.tar.gz: 0444cabcb5c8ce71559758e92168f0f357de967edaaf15671f5f867f7e115dc48615e49de6d4d9a3fa55c7d098da46460b810ec0ac07ff625922c724daeac575
|
data/README.md
CHANGED
|
@@ -301,6 +301,12 @@ Native event-runtime debug logs:
|
|
|
301
301
|
QT_RUBY_EVENT_DEBUG=1 ruby your_app.rb
|
|
302
302
|
```
|
|
303
303
|
|
|
304
|
+
Object-wrapper fallback debug logs:
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
QT_RUBY_OBJECT_WRAPPER_DEBUG=1 ruby your_app.rb
|
|
308
|
+
```
|
|
309
|
+
|
|
304
310
|
Optional tuning:
|
|
305
311
|
|
|
306
312
|
```bash
|
|
@@ -6,6 +6,11 @@ require 'fileutils'
|
|
|
6
6
|
PKG_CONFIG = RbConfig::CONFIG['PKG_CONFIG'] || 'pkg-config'
|
|
7
7
|
QT_PACKAGES = %w[Qt6Core Qt6Gui Qt6Widgets].freeze
|
|
8
8
|
MINIMUM_QT_VERSION = Gem::Version.new('6.4.2')
|
|
9
|
+
ROOT = File.expand_path('../..', __dir__)
|
|
10
|
+
GENERATOR = File.join(ROOT, 'scripts/generate_bridge.rb')
|
|
11
|
+
GENERATED_CPP = File.join(ROOT, 'build/generated/qt_ruby_bridge.cpp')
|
|
12
|
+
GENERATED_EVENT_PAYLOADS = File.join(ROOT, 'build/generated/event_payloads.inc')
|
|
13
|
+
LOCAL_CPP = File.expand_path('qt_ruby_bridge.cpp')
|
|
9
14
|
|
|
10
15
|
def pkg_config(*)
|
|
11
16
|
system(PKG_CONFIG, *, out: File::NULL, err: File::NULL)
|
|
@@ -15,15 +20,27 @@ def pkg_config_capture(*args)
|
|
|
15
20
|
`#{[PKG_CONFIG, *args].join(' ')}`.strip
|
|
16
21
|
end
|
|
17
22
|
|
|
18
|
-
|
|
23
|
+
def generated_bridge_inputs_available?
|
|
24
|
+
(File.exist?(LOCAL_CPP) || File.exist?(GENERATED_CPP)) && File.exist?(GENERATED_EVENT_PAYLOADS)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def generator_env
|
|
28
|
+
scope = ENV.fetch('QT_RUBY_SCOPE', nil)
|
|
29
|
+
scope && !scope.empty? ? { 'QT_RUBY_SCOPE' => scope } : {}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def ensure_generated_bridge_inputs!
|
|
33
|
+
return false if generated_bridge_inputs_available?
|
|
19
34
|
|
|
20
|
-
|
|
21
|
-
abort
|
|
35
|
+
abort "Generator script not found: #{GENERATOR}" unless File.exist?(GENERATOR)
|
|
36
|
+
abort 'Failed to generate Qt bridge files.' unless system(generator_env, RbConfig.ruby, GENERATOR)
|
|
37
|
+
|
|
38
|
+
true
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
abort 'pkg-config is required to build qt-ruby bridge.' unless find_executable(PKG_CONFIG)
|
|
22
42
|
|
|
23
|
-
|
|
24
|
-
scope = ENV.fetch('QT_RUBY_SCOPE', nil)
|
|
25
|
-
generator_env['QT_RUBY_SCOPE'] = scope if scope && !scope.empty?
|
|
26
|
-
abort 'Failed to generate Qt bridge files.' unless system(generator_env, RbConfig.ruby, generator)
|
|
43
|
+
generated_by_extconf = ensure_generated_bridge_inputs!
|
|
27
44
|
|
|
28
45
|
missing = QT_PACKAGES.reject { |pkg| pkg_config('--exists', pkg) }
|
|
29
46
|
abort "Missing Qt packages: #{missing.join(', ')}" unless missing.empty?
|
|
@@ -34,10 +51,10 @@ abort "Qt version #{qt_version} is too old. Require >= #{MINIMUM_QT_VERSION}." i
|
|
|
34
51
|
|
|
35
52
|
cflags = pkg_config_capture('--cflags', *QT_PACKAGES)
|
|
36
53
|
libs = pkg_config_capture('--libs', *QT_PACKAGES)
|
|
37
|
-
generated_cpp = if File.exist?(
|
|
38
|
-
|
|
54
|
+
generated_cpp = if !generated_by_extconf && File.exist?(LOCAL_CPP)
|
|
55
|
+
LOCAL_CPP
|
|
39
56
|
else
|
|
40
|
-
|
|
57
|
+
GENERATED_CPP
|
|
41
58
|
end
|
|
42
59
|
runtime_hpp = File.expand_path('../../ext/qt_ruby_bridge/qt_ruby_runtime.hpp', __dir__)
|
|
43
60
|
runtime_cpp_files = %w[
|
|
@@ -52,8 +69,7 @@ abort "Runtime header not found: #{runtime_hpp}" unless File.exist?(runtime_hpp)
|
|
|
52
69
|
missing_runtime = runtime_cpp_files.reject { |path| File.exist?(path) }
|
|
53
70
|
abort "Runtime source not found: #{missing_runtime.join(', ')}" unless missing_runtime.empty?
|
|
54
71
|
|
|
55
|
-
|
|
56
|
-
FileUtils.cp(generated_cpp, local_cpp) unless File.exist?(local_cpp) && File.identical?(generated_cpp, local_cpp)
|
|
72
|
+
FileUtils.cp(generated_cpp, LOCAL_CPP) unless File.exist?(LOCAL_CPP) && File.identical?(generated_cpp, LOCAL_CPP)
|
|
57
73
|
runtime_cpp_files.each do |runtime_cpp|
|
|
58
74
|
local_runtime_cpp = File.expand_path(File.basename(runtime_cpp))
|
|
59
75
|
unless File.exist?(local_runtime_cpp) && File.identical?(runtime_cpp, local_runtime_cpp)
|
data/lib/qt/object_wrapper.rb
CHANGED
|
@@ -20,7 +20,10 @@ module Qt
|
|
|
20
20
|
return cached if cached
|
|
21
21
|
|
|
22
22
|
klass = resolve_wrapper_class(pointer, expected_qt_class) || fallback_wrapper_class(expected_qt_class)
|
|
23
|
-
|
|
23
|
+
unless klass
|
|
24
|
+
warn_unwrapped_pointer(pointer, expected_qt_class)
|
|
25
|
+
return pointer
|
|
26
|
+
end
|
|
24
27
|
|
|
25
28
|
register_wrapper(instantiate_wrapper(klass, pointer))
|
|
26
29
|
end
|
|
@@ -65,7 +68,6 @@ module Qt
|
|
|
65
68
|
klass = Qt.const_get(normalized_qt_class, false)
|
|
66
69
|
return nil unless klass.is_a?(Class)
|
|
67
70
|
return nil unless klass.const_defined?(:QT_CLASS, false)
|
|
68
|
-
return nil unless klass <= Qt::QObject
|
|
69
71
|
|
|
70
72
|
klass
|
|
71
73
|
rescue NameError
|
|
@@ -87,6 +89,7 @@ module Qt
|
|
|
87
89
|
|
|
88
90
|
pointer = wrapper.handle
|
|
89
91
|
return wrapper if null_pointer?(pointer)
|
|
92
|
+
return wrapper unless qobject_wrapper?(wrapper.class)
|
|
90
93
|
|
|
91
94
|
cached = cached_wrapper_for(pointer)
|
|
92
95
|
return cached if cached
|
|
@@ -113,6 +116,7 @@ module Qt
|
|
|
113
116
|
def reset_cache!
|
|
114
117
|
@wrapper_cache = {}
|
|
115
118
|
@destroy_hook_addresses = {}
|
|
119
|
+
@unwrapped_pointer_warning_keys = {}
|
|
116
120
|
end
|
|
117
121
|
|
|
118
122
|
def install_constructor_cache_hooks!
|
|
@@ -132,6 +136,33 @@ module Qt
|
|
|
132
136
|
@destroy_hook_addresses ||= {}
|
|
133
137
|
end
|
|
134
138
|
|
|
139
|
+
def object_wrapper_debug_enabled?
|
|
140
|
+
ENV['QT_RUBY_OBJECT_WRAPPER_DEBUG'] == '1'
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def warn_unwrapped_pointer(pointer, expected_qt_class)
|
|
144
|
+
return unless object_wrapper_debug_enabled?
|
|
145
|
+
|
|
146
|
+
label = warning_qt_class_label(expected_qt_class)
|
|
147
|
+
return if unwrapped_pointer_warning_keys[label]
|
|
148
|
+
|
|
149
|
+
unwrapped_pointer_warning_keys[label] = true
|
|
150
|
+
warn "[qt-ruby-wrapper] returning raw pointer for #{label}; no generated wrapper class matched " \
|
|
151
|
+
"address=0x#{pointer.address.to_s(16)}"
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def warning_qt_class_label(expected_qt_class)
|
|
155
|
+
normalized = normalize_expected_qt_class(expected_qt_class)
|
|
156
|
+
return normalized if normalized
|
|
157
|
+
|
|
158
|
+
raw = expected_qt_class.to_s.strip
|
|
159
|
+
raw.empty? ? '(unknown Qt class)' : raw
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def unwrapped_pointer_warning_keys
|
|
163
|
+
@unwrapped_pointer_warning_keys ||= {}
|
|
164
|
+
end
|
|
165
|
+
|
|
135
166
|
def ensure_destroy_hook(wrapper, pointer)
|
|
136
167
|
address = pointer.address
|
|
137
168
|
return if destroy_hook_addresses[address]
|
|
@@ -157,6 +188,10 @@ module Qt
|
|
|
157
188
|
depth
|
|
158
189
|
end
|
|
159
190
|
|
|
191
|
+
def qobject_wrapper?(klass)
|
|
192
|
+
klass.is_a?(Class) && klass <= Qt::QObject
|
|
193
|
+
end
|
|
194
|
+
|
|
160
195
|
def normalize_expected_qt_class(expected_qt_class)
|
|
161
196
|
return nil if expected_qt_class.nil?
|
|
162
197
|
|
data/lib/qt/version.rb
CHANGED
|
@@ -52,6 +52,7 @@ def map_cpp_arg_type(type_name, qt_class: nil, int_cast_types: nil)
|
|
|
52
52
|
return { ffi: :string, cast: :qdate_from_utf8 } if type == 'QDate'
|
|
53
53
|
return { ffi: :string, cast: :qtime_from_utf8 } if type == 'QTime'
|
|
54
54
|
return { ffi: :string, cast: :qkeysequence_from_utf8 } if type == 'QKeySequence'
|
|
55
|
+
return { ffi: :int, cast: :qcursor_shape } if type == 'QCursor'
|
|
55
56
|
return { ffi: :pointer, cast: :qicon_ref } if type == 'QIcon'
|
|
56
57
|
return { ffi: :string, cast: :qany_string_view } if type == 'QAnyStringView'
|
|
57
58
|
return { ffi: :string, cast: :qvariant_from_utf8 } if type == 'QVariant'
|
|
@@ -95,7 +96,7 @@ def map_pointer_cpp_return_type(type, ast: nil)
|
|
|
95
96
|
|
|
96
97
|
info = { ffi_return: :pointer }
|
|
97
98
|
base_type = type.sub(/\s*\*\z/, '').strip
|
|
98
|
-
if ast &&
|
|
99
|
+
if ast && ast_class_index(ast)[:methods_by_class].key?(base_type)
|
|
99
100
|
info[:pointer_class] = base_type
|
|
100
101
|
end
|
|
101
102
|
|
|
@@ -109,6 +109,10 @@ def ctor_function_name(spec)
|
|
|
109
109
|
"qt_ruby_#{spec[:prefix]}_new"
|
|
110
110
|
end
|
|
111
111
|
|
|
112
|
-
def
|
|
112
|
+
def default_method_function_name(spec, method)
|
|
113
113
|
"qt_ruby_#{spec[:prefix]}_#{to_snake(method[:qt_name])}"
|
|
114
114
|
end
|
|
115
|
+
|
|
116
|
+
def method_function_name(spec, method)
|
|
117
|
+
method[:native_name] || default_method_function_name(spec, method)
|
|
118
|
+
end
|
|
@@ -19,6 +19,8 @@ def all_ffi_functions(specs, free_function_specs:)
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def append_constructor_ffi_function(fns, spec)
|
|
22
|
+
return if spec[:constructor][:mode] == :wrap_only
|
|
23
|
+
|
|
22
24
|
ctor_args = constructor_ffi_args(spec)
|
|
23
25
|
fns << { name: ctor_function_name(spec), ffi_return: :pointer, args: ctor_args }
|
|
24
26
|
end
|
|
@@ -151,6 +151,90 @@ def discover_related_value_classes(ast, seed_classes, all_classes, template_clas
|
|
|
151
151
|
discovered.to_a
|
|
152
152
|
end
|
|
153
153
|
|
|
154
|
+
def discover_related_qobject_wrapper_classes(ast, seed_classes, all_classes, template_classes, scope)
|
|
155
|
+
discovered = seed_classes.to_set
|
|
156
|
+
queue = seed_classes.dup
|
|
157
|
+
related = []
|
|
158
|
+
|
|
159
|
+
until queue.empty?
|
|
160
|
+
klass = queue.shift
|
|
161
|
+
related_qobject_pointer_type_names(ast, klass).each do |candidate|
|
|
162
|
+
next if discovered.include?(candidate)
|
|
163
|
+
next unless related_qobject_wrapper_candidate?(ast, candidate, all_classes, template_classes, scope)
|
|
164
|
+
|
|
165
|
+
discovered << candidate
|
|
166
|
+
related << candidate
|
|
167
|
+
queue << candidate
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
related
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def related_qobject_wrapper_candidate?(ast, qt_class, all_classes, template_classes, scope)
|
|
175
|
+
return false unless all_classes.include?(qt_class)
|
|
176
|
+
return false if template_classes.include?(qt_class)
|
|
177
|
+
return false if qt_class.end_with?('Private')
|
|
178
|
+
return false if qt_class == 'QApplication'
|
|
179
|
+
return false unless class_inherits?(ast, qt_class, 'QObject')
|
|
180
|
+
|
|
181
|
+
related_qobject_wrapper_matches_scope?(ast, qt_class, scope)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def related_qobject_wrapper_matches_scope?(ast, qt_class, scope)
|
|
185
|
+
case scope
|
|
186
|
+
when 'widgets'
|
|
187
|
+
class_inherits?(ast, qt_class, 'QWidget') || class_inherits?(ast, qt_class, 'QLayout')
|
|
188
|
+
when 'qobject'
|
|
189
|
+
!class_inherits?(ast, qt_class, 'QWidget') && !class_inherits?(ast, qt_class, 'QLayout')
|
|
190
|
+
when 'all'
|
|
191
|
+
true
|
|
192
|
+
else
|
|
193
|
+
raise "Unsupported QT_RUBY_SCOPE=#{scope.inspect}. Supported: #{SUPPORTED_SCOPES.join(', ')}"
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def related_qobject_pointer_type_names(ast, qt_class)
|
|
198
|
+
int_cast_types = ast_int_cast_type_set(ast)
|
|
199
|
+
method_decls_for_related_wrapper_scan(ast, qt_class).each_with_object(Set.new) do |decl, out|
|
|
200
|
+
next unless auto_method_decl_candidate?(decl)
|
|
201
|
+
next unless auto_method_supported_for_related_wrapper_scan?(ast, decl, qt_class, int_cast_types)
|
|
202
|
+
|
|
203
|
+
parsed = parse_method_signature(decl)
|
|
204
|
+
append_qobject_pointer_types_from_decl(out, parsed)
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def method_decls_for_related_wrapper_scan(ast, qt_class)
|
|
209
|
+
collect_method_names_with_bases(ast, qt_class).flat_map do |method_name|
|
|
210
|
+
collect_method_decls_with_bases(ast, qt_class, method_name)
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def auto_method_supported_for_related_wrapper_scan?(ast, decl, qt_class, int_cast_types)
|
|
215
|
+
entry = { qt_name: decl['name'] }
|
|
216
|
+
!build_auto_method_from_decl(decl, entry, qt_class: qt_class, int_cast_types: int_cast_types, ast: ast).nil?
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def append_qobject_pointer_types_from_decl(out, parsed)
|
|
220
|
+
pointer_type = qobject_pointer_type_name(parsed[:return_type])
|
|
221
|
+
out << pointer_type if pointer_type
|
|
222
|
+
parsed[:params].each do |param|
|
|
223
|
+
pointer_type = qobject_pointer_type_name(param[:type])
|
|
224
|
+
out << pointer_type if pointer_type
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def qobject_pointer_type_name(raw_type)
|
|
229
|
+
normalized = normalized_cpp_type_name(raw_type).to_s
|
|
230
|
+
return nil unless normalized.end_with?('*')
|
|
231
|
+
|
|
232
|
+
type_name = normalized.delete_suffix('*')
|
|
233
|
+
return nil unless type_name.match?(/\AQ[A-Z]\w*\z/)
|
|
234
|
+
|
|
235
|
+
type_name
|
|
236
|
+
end
|
|
237
|
+
|
|
154
238
|
def related_value_class_candidate?(ast, qt_class, all_classes, template_classes)
|
|
155
239
|
return false if SCALAR_BRIDGED_QT_TYPES.include?(qt_class)
|
|
156
240
|
return false unless all_classes.include?(qt_class)
|
|
@@ -160,7 +244,7 @@ def related_value_class_candidate?(ast, qt_class, all_classes, template_classes)
|
|
|
160
244
|
return false if abstract_class?(ast, qt_class)
|
|
161
245
|
return false if class_inherits?(ast, qt_class, 'QObject')
|
|
162
246
|
|
|
163
|
-
|
|
247
|
+
true
|
|
164
248
|
end
|
|
165
249
|
|
|
166
250
|
def related_qt_type_names(ast, qt_class)
|
|
@@ -178,11 +262,12 @@ end
|
|
|
178
262
|
|
|
179
263
|
def append_related_qt_types_from_decl(out, parsed, method_name)
|
|
180
264
|
candidates = []
|
|
181
|
-
candidates <<
|
|
182
|
-
parsed[:params].each { |param| candidates <<
|
|
183
|
-
candidates.each do |candidate|
|
|
265
|
+
candidates << [related_qt_type_name(parsed[:return_type]), parsed[:return_type].to_s] if parsed[:return_type]
|
|
266
|
+
parsed[:params].each { |param| candidates << [related_qt_type_name(param[:type]), param[:type].to_s] }
|
|
267
|
+
candidates.each do |candidate, raw_type|
|
|
184
268
|
next if candidate.empty? || !candidate.start_with?('Q')
|
|
185
|
-
next unless related_type_name_matches_method?(candidate, method_name)
|
|
269
|
+
next unless related_type_name_matches_method?(candidate, method_name) ||
|
|
270
|
+
(qt_pointer_type?(raw_type) && candidate.end_with?('Item'))
|
|
186
271
|
|
|
187
272
|
out << candidate
|
|
188
273
|
end
|
|
@@ -195,6 +280,14 @@ def related_type_name_matches_method?(qt_type, method_name)
|
|
|
195
280
|
method_name.to_s.downcase.include?(token)
|
|
196
281
|
end
|
|
197
282
|
|
|
283
|
+
def qt_pointer_type?(raw_type)
|
|
284
|
+
raw_type.to_s.strip.end_with?('*')
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def related_qt_type_name(raw_type)
|
|
288
|
+
normalized_cpp_type_name(raw_type).to_s.delete_suffix('*')
|
|
289
|
+
end
|
|
290
|
+
|
|
198
291
|
def discover_target_qt_classes(ast, scope)
|
|
199
292
|
all_classes = q_class_names(ast)
|
|
200
293
|
template_classes = template_qt_classes(ast)
|
|
@@ -206,9 +299,13 @@ def discover_target_qt_classes(ast, scope)
|
|
|
206
299
|
targets = timed("discover_target_qt_classes/#{scope}/related") do
|
|
207
300
|
discover_related_value_classes(ast, base_targets, all_classes, template_classes).sort
|
|
208
301
|
end
|
|
302
|
+
wrapper_targets = timed("discover_target_qt_classes/#{scope}/related_qobject_wrappers") do
|
|
303
|
+
discover_related_qobject_wrapper_classes(ast, targets, all_classes, template_classes, scope).sort
|
|
304
|
+
end
|
|
305
|
+
targets = (targets + wrapper_targets).uniq.sort
|
|
209
306
|
debug_log(
|
|
210
307
|
"discover_target_qt_classes scope=#{scope} total_q=#{all_classes.length} " \
|
|
211
|
-
"base=#{base_targets.length} targets=#{targets.length}"
|
|
308
|
+
"base=#{base_targets.length} wrappers=#{wrapper_targets.length} targets=#{targets.length}"
|
|
212
309
|
)
|
|
213
310
|
targets
|
|
214
311
|
end
|
|
@@ -220,9 +317,24 @@ def widget_target_qt_class?(ast, qt_class)
|
|
|
220
317
|
|
|
221
318
|
class_inherits?(ast, qt_class, 'QWidget') ||
|
|
222
319
|
class_inherits?(ast, qt_class, 'QLayout') ||
|
|
320
|
+
widget_item_target_qt_class?(ast, qt_class) ||
|
|
223
321
|
qt_class == 'QTableWidgetItem'
|
|
224
322
|
end
|
|
225
323
|
|
|
324
|
+
def widget_item_target_qt_class?(ast, qt_class)
|
|
325
|
+
return false unless qt_class.end_with?('Item')
|
|
326
|
+
return false if class_inherits?(ast, qt_class, 'QObject')
|
|
327
|
+
|
|
328
|
+
collect_constructor_decls(ast, qt_class).any? do |decl|
|
|
329
|
+
parsed = parse_method_signature(decl)
|
|
330
|
+
next false unless parsed
|
|
331
|
+
|
|
332
|
+
parsed[:params].any? do |param|
|
|
333
|
+
normalized_cpp_type_name(param[:type]).to_s.match?(/\AQ\w*Widget\*\z/)
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
226
338
|
def qobject_target_qt_class?(ast, qt_class)
|
|
227
339
|
return false if qt_class.end_with?('Private')
|
|
228
340
|
return false if qt_class == 'QApplication'
|
|
@@ -240,7 +352,7 @@ def constructor_usable_for_codegen?(ast, qt_class)
|
|
|
240
352
|
ctor_decls = collect_constructor_decls(ast, qt_class)
|
|
241
353
|
ctor_decls.any? do |decl|
|
|
242
354
|
constructor_keysequence_parent_type(decl) ||
|
|
243
|
-
|
|
355
|
+
constructor_supports_parent_only?(decl) ||
|
|
244
356
|
constructor_supports_no_args?(decl) ||
|
|
245
357
|
constructor_supports_string_path?(decl)
|
|
246
358
|
end
|
|
@@ -248,26 +360,40 @@ end
|
|
|
248
360
|
|
|
249
361
|
def build_base_spec_for_qt_class(ast, qt_class)
|
|
250
362
|
ctor_decls = collect_constructor_decls(ast, qt_class)
|
|
363
|
+
parent_ctor = if abstract_class?(ast, qt_class)
|
|
364
|
+
{ parent: false, mode: :wrap_only }
|
|
365
|
+
else
|
|
366
|
+
constructor_spec_for_qt_class(ast, qt_class, ctor_decls)
|
|
367
|
+
end
|
|
368
|
+
base_spec_hash(qt_class, parent_ctor)
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
def constructor_spec_for_qt_class(ast, qt_class, ctor_decls)
|
|
251
372
|
keysequence_parent_type = ctor_decls.filter_map { |decl| constructor_keysequence_parent_type(decl) }.first
|
|
252
373
|
parent_type = ctor_decls.filter_map { |decl| parent_constructor_first_type(decl) }.first
|
|
253
|
-
string_path_cast = ctor_decls
|
|
374
|
+
string_path_cast = constructor_string_path_cast(ctor_decls)
|
|
375
|
+
widget_child = qt_class != 'QWidget' && class_inherits?(ast, qt_class, 'QWidget')
|
|
376
|
+
|
|
377
|
+
if keysequence_parent_type
|
|
378
|
+
{ parent: true, parent_type: keysequence_parent_type, mode: :keysequence_parent, register_in_parent: widget_child }
|
|
379
|
+
elsif parent_type
|
|
380
|
+
parent_constructor_for_type(parent_type, widget_child)
|
|
381
|
+
elsif string_path_cast
|
|
382
|
+
string_path_constructor(string_path_cast)
|
|
383
|
+
elsif ctor_decls.any? { |decl| constructor_supports_no_args?(decl) }
|
|
384
|
+
{ parent: false }
|
|
385
|
+
else
|
|
386
|
+
{ parent: false, mode: :wrap_only }
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
def constructor_string_path_cast(ctor_decls)
|
|
391
|
+
ctor_decls.filter_map do |decl|
|
|
254
392
|
parsed = parse_method_signature(decl)
|
|
255
393
|
next nil unless parsed && parsed[:params].first
|
|
256
394
|
|
|
257
395
|
constructor_string_like_arg_cast(parsed[:params].first[:type])
|
|
258
396
|
end.first
|
|
259
|
-
widget_child = qt_class != 'QWidget' && class_inherits?(ast, qt_class, 'QWidget')
|
|
260
|
-
parent_ctor =
|
|
261
|
-
if keysequence_parent_type
|
|
262
|
-
{ parent: true, parent_type: keysequence_parent_type, mode: :keysequence_parent, register_in_parent: widget_child }
|
|
263
|
-
elsif parent_type
|
|
264
|
-
parent_constructor_for_type(parent_type, widget_child)
|
|
265
|
-
elsif string_path_cast
|
|
266
|
-
string_path_constructor(string_path_cast)
|
|
267
|
-
else
|
|
268
|
-
{ parent: false }
|
|
269
|
-
end
|
|
270
|
-
base_spec_hash(qt_class, parent_ctor)
|
|
271
397
|
end
|
|
272
398
|
|
|
273
399
|
def base_spec_hash(qt_class, parent_ctor)
|
data/scripts/generate_bridge.rb
CHANGED
|
@@ -246,6 +246,7 @@ def arg_expr(arg)
|
|
|
246
246
|
when :qdate_from_utf8 then "qdate_from_bridge_value(#{arg[:name]})"
|
|
247
247
|
when :qtime_from_utf8 then "qtime_from_bridge_value(#{arg[:name]})"
|
|
248
248
|
when :qkeysequence_from_utf8 then "QKeySequence(as_qstring(#{arg[:name]}))"
|
|
249
|
+
when :qcursor_shape then "QCursor(static_cast<Qt::CursorShape>(#{arg[:name]}))"
|
|
249
250
|
when :qicon_ref then "*static_cast<QIcon*>(#{arg[:name]})"
|
|
250
251
|
when :qany_string_view then "QAnyStringView(as_qstring(#{arg[:name]}))"
|
|
251
252
|
when :qvariant_from_utf8 then "qvariant_from_bridge_value(#{arg[:name]})"
|
|
@@ -385,8 +386,10 @@ def generate_cpp_bridge(specs, free_function_specs)
|
|
|
385
386
|
end
|
|
386
387
|
|
|
387
388
|
def append_cpp_spec_methods(lines, spec)
|
|
388
|
-
|
|
389
|
-
|
|
389
|
+
unless spec[:constructor][:mode] == :wrap_only
|
|
390
|
+
generate_cpp_constructor(lines, spec)
|
|
391
|
+
lines << ''
|
|
392
|
+
end
|
|
390
393
|
spec[:methods].each do |method|
|
|
391
394
|
generate_cpp_method(lines, spec, method)
|
|
392
395
|
lines << ''
|
|
@@ -717,6 +720,8 @@ def setter_alias_base_name(method)
|
|
|
717
720
|
return nil unless method[:args].length == 1
|
|
718
721
|
return nil if method[:property]
|
|
719
722
|
|
|
723
|
+
return 'cursor' if method[:qt_name] == 'setCursor'
|
|
724
|
+
|
|
720
725
|
property_name_from_setter(method[:qt_name] || method[:ruby_name].to_s)
|
|
721
726
|
end
|
|
722
727
|
|
|
@@ -747,7 +752,10 @@ def qapplication_singleton_forwarder_names(methods, singleton_setter_aliases)
|
|
|
747
752
|
end
|
|
748
753
|
|
|
749
754
|
def append_widget_initializer(lines, spec:, widget_root:, indent:)
|
|
750
|
-
if spec[:constructor][:mode] == :
|
|
755
|
+
if spec[:constructor][:mode] == :wrap_only
|
|
756
|
+
append_wrap_only_initializer(lines, spec, indent)
|
|
757
|
+
return
|
|
758
|
+
elsif spec[:constructor][:mode] == :string_path
|
|
751
759
|
append_string_path_initializer(lines, spec, indent)
|
|
752
760
|
elsif spec[:constructor][:mode] == :keysequence_parent
|
|
753
761
|
append_keysequence_parent_initializer(lines, spec, widget_root, indent)
|
|
@@ -761,6 +769,12 @@ def append_widget_initializer(lines, spec:, widget_root:, indent:)
|
|
|
761
769
|
lines << "#{indent}end"
|
|
762
770
|
end
|
|
763
771
|
|
|
772
|
+
def append_wrap_only_initializer(lines, spec, indent)
|
|
773
|
+
lines << "#{indent}def initialize(*)"
|
|
774
|
+
lines << "#{indent} raise NotImplementedError, '#{spec[:ruby_class]} cannot be directly instantiated yet'"
|
|
775
|
+
lines << "#{indent}end"
|
|
776
|
+
end
|
|
777
|
+
|
|
764
778
|
def append_parent_widget_initializer(lines, spec, widget_root, indent)
|
|
765
779
|
lines << "#{indent}def initialize(parent = nil)"
|
|
766
780
|
lines << "#{indent} @handle = Native.#{spec[:prefix]}_new(parent&.handle)"
|
|
@@ -906,13 +920,50 @@ end
|
|
|
906
920
|
def append_ruby_widget_methods(lines, spec)
|
|
907
921
|
spec[:methods].each do |method|
|
|
908
922
|
call_args = ['@handle'] + method[:args].map { |arg| arg[:name] }
|
|
909
|
-
|
|
923
|
+
native_name = method_function_name(spec, method).delete_prefix('qt_ruby_')
|
|
924
|
+
native_call = "Native.#{native_name}(#{call_args.join(', ')})"
|
|
910
925
|
append_ruby_native_call_method(lines, method: method, native_call: native_call, indent: ' ')
|
|
911
926
|
append_ruby_property_writer(lines, method: method, indent: ' ')
|
|
912
927
|
lines << ''
|
|
913
928
|
end
|
|
914
929
|
end
|
|
915
930
|
|
|
931
|
+
def assign_unique_native_method_names(specs)
|
|
932
|
+
used = Set.new
|
|
933
|
+
specs.map do |spec|
|
|
934
|
+
methods = spec[:methods].map do |method|
|
|
935
|
+
assign_unique_native_method_name(spec, method, used)
|
|
936
|
+
end
|
|
937
|
+
spec.merge(methods: methods)
|
|
938
|
+
end
|
|
939
|
+
end
|
|
940
|
+
|
|
941
|
+
def assign_unique_native_method_name(spec, method, used)
|
|
942
|
+
native_name = default_method_function_name(spec, method)
|
|
943
|
+
return used_add_and_return(used, native_name, method) unless used.include?(native_name)
|
|
944
|
+
|
|
945
|
+
unique_name = next_unique_native_method_name(spec, method, used)
|
|
946
|
+
debug_log("native name collision #{native_name} -> #{unique_name}")
|
|
947
|
+
used << unique_name
|
|
948
|
+
method.merge(native_name: unique_name)
|
|
949
|
+
end
|
|
950
|
+
|
|
951
|
+
def used_add_and_return(used, value, result)
|
|
952
|
+
used << value
|
|
953
|
+
result
|
|
954
|
+
end
|
|
955
|
+
|
|
956
|
+
def next_unique_native_method_name(spec, method, used)
|
|
957
|
+
base = "qt_ruby_#{spec[:prefix]}_qt_#{to_snake(method[:qt_name])}"
|
|
958
|
+
candidate = base
|
|
959
|
+
counter = 2
|
|
960
|
+
while used.include?(candidate)
|
|
961
|
+
candidate = "#{base}_#{counter}"
|
|
962
|
+
counter += 1
|
|
963
|
+
end
|
|
964
|
+
candidate
|
|
965
|
+
end
|
|
966
|
+
|
|
916
967
|
def generate_ruby_widget_class(lines, spec, specs_by_qt, super_qt_by_qt, qt_to_ruby)
|
|
917
968
|
metadata = ruby_api_metadata_for_spec(spec, specs_by_qt, super_qt_by_qt)
|
|
918
969
|
super_ruby = ruby_super_class_for_spec(spec, super_qt_by_qt, qt_to_ruby)
|
|
@@ -1049,6 +1100,7 @@ def collect_enum_constants_for_scope(ast, target_scope, warnings = [])
|
|
|
1049
1100
|
next unless node['kind'] == 'EnumDecl'
|
|
1050
1101
|
next unless scope == target_scope
|
|
1051
1102
|
|
|
1103
|
+
next_value = 0
|
|
1052
1104
|
Array(node['inner']).each do |entry|
|
|
1053
1105
|
next unless entry['kind'] == 'EnumConstantDecl'
|
|
1054
1106
|
|
|
@@ -1058,9 +1110,14 @@ def collect_enum_constants_for_scope(ast, target_scope, warnings = [])
|
|
|
1058
1110
|
|
|
1059
1111
|
raw_value = ast_extract_first_value(entry)
|
|
1060
1112
|
value = parse_ast_integer_value(raw_value)
|
|
1061
|
-
|
|
1113
|
+
if value.nil?
|
|
1114
|
+
value = next_value
|
|
1115
|
+
else
|
|
1116
|
+
next_value = value
|
|
1117
|
+
end
|
|
1062
1118
|
|
|
1063
1119
|
append_constant_with_conflict_warning(constants, name, value, warnings, target_scope.join('::'))
|
|
1120
|
+
next_value = value + 1
|
|
1064
1121
|
end
|
|
1065
1122
|
end
|
|
1066
1123
|
|
|
@@ -1075,6 +1132,9 @@ def collect_qt_namespace_enum_constants(ast, warnings = [])
|
|
|
1075
1132
|
|
|
1076
1133
|
append_constant_with_conflict_warning(constants, alias_name, value, warnings, 'Qt::QEventAlias')
|
|
1077
1134
|
end
|
|
1135
|
+
collect_enum_constants_for_scope(ast, ['QCursor'], warnings).each do |name, value|
|
|
1136
|
+
append_constant_with_conflict_warning(constants, name, value, warnings, 'Qt::QCursorAlias')
|
|
1137
|
+
end
|
|
1078
1138
|
constants
|
|
1079
1139
|
end
|
|
1080
1140
|
|
|
@@ -1179,7 +1239,8 @@ free_function_specs = timed('build_free_function_specs') { qt_free_function_spec
|
|
|
1179
1239
|
base_specs = timed('build_base_specs') { build_base_specs(ast) }
|
|
1180
1240
|
timed('validate_qt_api') { validate_qt_api!(ast, base_specs) }
|
|
1181
1241
|
expanded_specs = timed('expand_auto_methods') { expand_auto_methods(base_specs, ast) }
|
|
1182
|
-
|
|
1242
|
+
property_specs = timed('enrich_specs_with_properties') { enrich_specs_with_properties(expanded_specs, ast) }
|
|
1243
|
+
effective_specs = timed('assign_unique_native_method_names') { assign_unique_native_method_names(property_specs) }
|
|
1183
1244
|
super_qt_by_qt, wrapper_qt_classes = timed('build_generated_inheritance') do
|
|
1184
1245
|
build_generated_inheritance(ast, effective_specs)
|
|
1185
1246
|
end
|
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.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Maksim Veynberg
|
|
@@ -100,7 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
100
100
|
- !ruby/object:Gem::Version
|
|
101
101
|
version: '0'
|
|
102
102
|
requirements: []
|
|
103
|
-
rubygems_version:
|
|
103
|
+
rubygems_version: 4.0.10
|
|
104
104
|
specification_version: 4
|
|
105
105
|
summary: Ruby bindings for Qt 6.4.2+
|
|
106
106
|
test_files: []
|