qt 0.1.9 → 0.2.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 +4 -4
- data/ext/qt_ruby_bridge/extconf.rb +2 -0
- data/ext/qt_ruby_bridge/qt_ruby_runtime.hpp +14 -0
- data/ext/qt_ruby_bridge/runtime_events.cpp +8 -3
- data/ext/qt_ruby_bridge/runtime_value_types.cpp +22 -0
- data/ext/qt_ruby_bridge/runtime_virtual_methods.cpp +78 -0
- data/lib/qt/application_lifecycle.rb +1 -1
- data/lib/qt/event_runtime_dispatch.rb +2 -0
- data/lib/qt/generated_overload_runtime.rb +7 -0
- data/lib/qt/object_wrapper.rb +1 -0
- data/lib/qt/value_wrapper.rb +26 -0
- data/lib/qt/version.rb +1 -1
- data/lib/qt/virtual_method_runtime.rb +165 -0
- data/lib/qt.rb +4 -0
- data/scripts/generate_bridge/ast_introspection.rb +16 -3
- data/scripts/generate_bridge/auto_methods.rb +205 -24
- data/scripts/generate_bridge/cpp_method_return_emitter.rb +75 -24
- data/scripts/generate_bridge/ffi_api.rb +17 -1
- data/scripts/generate_bridge/free_function_specs.rb +0 -153
- data/scripts/generate_bridge/spec_discovery.rb +53 -13
- data/scripts/generate_bridge.rb +410 -31
- metadata +6 -1
|
@@ -2,24 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
RUNTIME_HEADER_PATH = File.expand_path('../../ext/qt_ruby_bridge/qt_ruby_runtime.hpp', __dir__)
|
|
4
4
|
|
|
5
|
-
SCALAR_CLASS_METHOD_FFI_RETURNS = %i[void int bool string pointer].freeze
|
|
6
|
-
QAPPLICATION_STATIC_METHOD_EXCLUSIONS = %w[exec].freeze
|
|
7
|
-
QAPPLICATION_STATIC_TEXT_SETTERS = %w[
|
|
8
|
-
setApplicationName
|
|
9
|
-
setDesktopFileName
|
|
10
|
-
setOrganizationName
|
|
11
|
-
setApplicationDisplayName
|
|
12
|
-
].freeze
|
|
13
|
-
|
|
14
5
|
def qt_free_function_specs(ast)
|
|
15
6
|
@qt_free_function_specs_cache ||= {}.compare_by_identity
|
|
16
7
|
return @qt_free_function_specs_cache[ast] if @qt_free_function_specs_cache.key?(ast)
|
|
17
8
|
|
|
18
9
|
qt_specs = []
|
|
19
10
|
qt_specs << qversion_free_function_spec(ast)
|
|
20
|
-
qt_specs.concat(qapplication_static_free_function_specs(ast))
|
|
21
|
-
qt_specs.concat(qapplication_static_text_setter_specs(ast))
|
|
22
|
-
qt_specs << qapplication_top_level_widgets_count_spec(ast)
|
|
23
11
|
runtime_specs = runtime_free_function_specs_from_header
|
|
24
12
|
@qt_free_function_specs_cache[ast] = merge_free_function_specs(qt_specs, runtime_specs).freeze
|
|
25
13
|
end
|
|
@@ -140,147 +128,6 @@ def qversion_free_function_spec(ast)
|
|
|
140
128
|
}
|
|
141
129
|
end
|
|
142
130
|
|
|
143
|
-
def qapplication_top_level_widgets_count_spec(ast)
|
|
144
|
-
has_top_level_widgets = collect_method_decls_with_bases(ast, 'QApplication', 'topLevelWidgets').any?
|
|
145
|
-
raise 'Unable to find QApplication::topLevelWidgets in AST' unless has_top_level_widgets
|
|
146
|
-
|
|
147
|
-
{
|
|
148
|
-
name: 'qt_ruby_qapplication_top_level_widgets_count',
|
|
149
|
-
ffi_return: :int,
|
|
150
|
-
args: [],
|
|
151
|
-
cpp_return: 'int',
|
|
152
|
-
cpp_args: '',
|
|
153
|
-
cpp_body: ['return QApplication::topLevelWidgets().size();'],
|
|
154
|
-
qapplication_method: { ruby_name: 'topLevelWidgetsCount', native: 'qapplication_top_level_widgets_count', args: [] }
|
|
155
|
-
}
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
def qapplication_static_free_function_specs(ast)
|
|
159
|
-
int_cast_types = ast_int_cast_type_set(ast)
|
|
160
|
-
method_names = collect_method_names_with_bases(ast, 'QApplication').uniq
|
|
161
|
-
method_names -= QAPPLICATION_STATIC_METHOD_EXCLUSIONS
|
|
162
|
-
method_names.filter_map do |qt_name|
|
|
163
|
-
build_qapplication_static_free_function_spec(ast, qt_name, int_cast_types)
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
def qapplication_static_text_setter_specs(ast)
|
|
168
|
-
QAPPLICATION_STATIC_TEXT_SETTERS.filter_map do |qt_name|
|
|
169
|
-
build_qapplication_static_text_setter_spec(ast, qt_name)
|
|
170
|
-
end
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
def build_qapplication_static_text_setter_spec(ast, qt_name)
|
|
174
|
-
return nil unless resolve_qapplication_static_text_setter_candidate(ast, qt_name)
|
|
175
|
-
|
|
176
|
-
native_name = "qapplication_#{to_snake(qt_name)}"
|
|
177
|
-
{
|
|
178
|
-
name: "qt_ruby_#{native_name}",
|
|
179
|
-
ffi_return: :void,
|
|
180
|
-
args: [:string],
|
|
181
|
-
cpp_return: 'void',
|
|
182
|
-
cpp_args: 'const char* value',
|
|
183
|
-
cpp_body: ["QApplication::#{qt_name}(as_qstring(value));"],
|
|
184
|
-
qapplication_method: {
|
|
185
|
-
ruby_name: qt_name,
|
|
186
|
-
native: native_name,
|
|
187
|
-
args: [{ name: :value, ffi: :string, cast: :qstring }],
|
|
188
|
-
required_arg_count: 1
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
def resolve_qapplication_static_text_setter_candidate(ast, qt_name)
|
|
194
|
-
decls = collect_method_decls_with_bases(ast, 'QApplication', qt_name)
|
|
195
|
-
decls.any? do |decl|
|
|
196
|
-
next false unless decl['storageClass'] == 'static'
|
|
197
|
-
next false unless decl['__effective_access'] == 'public'
|
|
198
|
-
|
|
199
|
-
parsed = parse_method_signature(decl)
|
|
200
|
-
next false unless parsed
|
|
201
|
-
next false unless parsed[:return_type].to_s.strip == 'void'
|
|
202
|
-
next false unless parsed[:required_arg_count] == 1
|
|
203
|
-
next false unless parsed[:params].length == 1
|
|
204
|
-
|
|
205
|
-
normalized = normalized_cpp_type_name(parsed[:params].first[:type])
|
|
206
|
-
normalized == 'QString'
|
|
207
|
-
end
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
def build_qapplication_static_free_function_spec(ast, qt_name, int_cast_types)
|
|
211
|
-
candidate = resolve_qapplication_static_noarg_candidate(ast, qt_name, int_cast_types)
|
|
212
|
-
return nil unless candidate
|
|
213
|
-
|
|
214
|
-
native_name = "qapplication_#{to_snake(qt_name)}"
|
|
215
|
-
cpp_body =
|
|
216
|
-
if candidate[:ffi_return] == :void
|
|
217
|
-
["QApplication::#{qt_name}();"]
|
|
218
|
-
elsif candidate[:enum_cast]
|
|
219
|
-
["return static_cast<int>(QApplication::#{qt_name}());"]
|
|
220
|
-
elsif candidate[:ffi_return] == :string
|
|
221
|
-
[
|
|
222
|
-
"const QString value = QApplication::#{qt_name}();",
|
|
223
|
-
'thread_local QByteArray utf8;',
|
|
224
|
-
'utf8 = value.toUtf8();',
|
|
225
|
-
'return utf8.constData();'
|
|
226
|
-
]
|
|
227
|
-
else
|
|
228
|
-
["return QApplication::#{qt_name}();"]
|
|
229
|
-
end
|
|
230
|
-
|
|
231
|
-
{
|
|
232
|
-
name: "qt_ruby_#{native_name}",
|
|
233
|
-
ffi_return: candidate[:ffi_return],
|
|
234
|
-
args: [],
|
|
235
|
-
cpp_return: ffi_return_to_cpp(candidate[:ffi_return]),
|
|
236
|
-
cpp_args: '',
|
|
237
|
-
cpp_body: cpp_body,
|
|
238
|
-
qapplication_method: {
|
|
239
|
-
ruby_name: qt_name,
|
|
240
|
-
native: native_name,
|
|
241
|
-
args: [],
|
|
242
|
-
return_cast: candidate[:return_cast],
|
|
243
|
-
pointer_class: candidate[:pointer_class]
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
def resolve_qapplication_static_noarg_candidate(ast, qt_name, int_cast_types)
|
|
249
|
-
decls = collect_method_decls_with_bases(ast, 'QApplication', qt_name)
|
|
250
|
-
candidates = decls.filter_map do |decl|
|
|
251
|
-
next unless decl['storageClass'] == 'static'
|
|
252
|
-
next unless decl['__effective_access'] == 'public'
|
|
253
|
-
|
|
254
|
-
parsed = parse_method_signature(decl)
|
|
255
|
-
next unless parsed && parsed[:required_arg_count].zero?
|
|
256
|
-
|
|
257
|
-
ret_info = qapplication_static_return_info(parsed[:return_type], int_cast_types, ast: ast)
|
|
258
|
-
next unless ret_info
|
|
259
|
-
|
|
260
|
-
{ decl: decl, param_count: parsed[:params].length }.merge(ret_info)
|
|
261
|
-
end
|
|
262
|
-
return nil if candidates.empty?
|
|
263
|
-
|
|
264
|
-
candidates.min_by { |item| item[:param_count] }
|
|
265
|
-
end
|
|
266
|
-
|
|
267
|
-
def qapplication_static_return_info(return_type, int_cast_types, ast:)
|
|
268
|
-
mapped = map_cpp_return_type(return_type, ast: ast)
|
|
269
|
-
if mapped && SCALAR_CLASS_METHOD_FFI_RETURNS.include?(mapped[:ffi_return])
|
|
270
|
-
return {
|
|
271
|
-
ffi_return: mapped[:ffi_return],
|
|
272
|
-
return_cast: mapped[:return_cast],
|
|
273
|
-
pointer_class: mapped[:pointer_class],
|
|
274
|
-
enum_cast: false
|
|
275
|
-
}
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
normalized = normalized_cpp_type_name(return_type)
|
|
279
|
-
return nil unless normalized.include?('::') && int_cast_types.include?(normalized)
|
|
280
|
-
|
|
281
|
-
{ ffi_return: :int, return_cast: nil, enum_cast: true }
|
|
282
|
-
end
|
|
283
|
-
|
|
284
131
|
def qapplication_class_method_specs(ast)
|
|
285
132
|
qt_free_function_specs(ast).filter_map { |spec| spec[:qapplication_method] }
|
|
286
133
|
end
|
|
@@ -12,7 +12,7 @@ def constructor_supports_parent_only?(decl)
|
|
|
12
12
|
return false if params.empty?
|
|
13
13
|
|
|
14
14
|
first_type = normalized_cpp_type_name(params.first[:type])
|
|
15
|
-
return false unless
|
|
15
|
+
return false unless first_type.match?(/\AQ[A-Z]\w*\*\z/)
|
|
16
16
|
|
|
17
17
|
params.drop(1).all? { |param| param[:has_default] }
|
|
18
18
|
end
|
|
@@ -44,7 +44,7 @@ def parent_constructor_first_type(decl)
|
|
|
44
44
|
return nil if params.empty?
|
|
45
45
|
|
|
46
46
|
first_type = normalized_cpp_type_name(params.first[:type])
|
|
47
|
-
return nil unless
|
|
47
|
+
return nil unless first_type.match?(/\AQ[A-Z]\w*\*\z/)
|
|
48
48
|
return nil unless params.drop(1).all? { |param| param[:has_default] }
|
|
49
49
|
|
|
50
50
|
first_type
|
|
@@ -139,7 +139,7 @@ def discover_related_value_classes(ast, seed_classes, all_classes, template_clas
|
|
|
139
139
|
|
|
140
140
|
until queue.empty?
|
|
141
141
|
klass = queue.shift
|
|
142
|
-
related_qt_type_names(ast, klass).each do |candidate|
|
|
142
|
+
(related_qt_type_names(ast, klass).to_a + derived_qt_classes(ast, klass, all_classes)).uniq.each do |candidate|
|
|
143
143
|
next if discovered.include?(candidate)
|
|
144
144
|
next unless related_value_class_candidate?(ast, candidate, all_classes, template_classes)
|
|
145
145
|
|
|
@@ -151,6 +151,42 @@ def discover_related_value_classes(ast, seed_classes, all_classes, template_clas
|
|
|
151
151
|
discovered.to_a
|
|
152
152
|
end
|
|
153
153
|
|
|
154
|
+
def derived_qt_classes(ast, qt_class, all_classes)
|
|
155
|
+
all_classes.select { |candidate| candidate != qt_class && class_inherits?(ast, candidate, qt_class) }
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def discover_related_static_api_classes(ast, seed_classes, all_classes, template_classes)
|
|
159
|
+
discovered = seed_classes.to_set
|
|
160
|
+
loop do
|
|
161
|
+
added = all_classes.select do |owner|
|
|
162
|
+
next false if discovered.include?(owner) || template_classes.include?(owner)
|
|
163
|
+
|
|
164
|
+
method_decls_for_related_wrapper_scan(ast, owner).any? do |decl|
|
|
165
|
+
next false unless decl['storageClass'] == 'static'
|
|
166
|
+
next false unless decl['__effective_access'] == 'public'
|
|
167
|
+
|
|
168
|
+
parsed = parse_method_signature(decl)
|
|
169
|
+
next false unless parsed
|
|
170
|
+
|
|
171
|
+
return_type = related_qt_type_name(parsed[:return_type])
|
|
172
|
+
next false unless discovered.include?(return_type)
|
|
173
|
+
|
|
174
|
+
!build_auto_method_from_decl(
|
|
175
|
+
decl,
|
|
176
|
+
{ qt_name: decl['name'] },
|
|
177
|
+
qt_class: owner,
|
|
178
|
+
int_cast_types: ast_int_cast_type_set(ast),
|
|
179
|
+
ast: ast
|
|
180
|
+
).nil?
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
break if added.empty?
|
|
184
|
+
|
|
185
|
+
discovered.merge(added)
|
|
186
|
+
end
|
|
187
|
+
discovered.to_a - seed_classes
|
|
188
|
+
end
|
|
189
|
+
|
|
154
190
|
def discover_related_qobject_wrapper_classes(ast, seed_classes, all_classes, template_classes, scope)
|
|
155
191
|
discovered = seed_classes.to_set
|
|
156
192
|
queue = seed_classes.dup
|
|
@@ -265,9 +301,7 @@ def append_related_qt_types_from_decl(out, parsed, method_name)
|
|
|
265
301
|
candidates << [related_qt_type_name(parsed[:return_type]), parsed[:return_type].to_s] if parsed[:return_type]
|
|
266
302
|
parsed[:params].each { |param| candidates << [related_qt_type_name(param[:type]), param[:type].to_s] }
|
|
267
303
|
candidates.each do |candidate, raw_type|
|
|
268
|
-
next
|
|
269
|
-
next unless related_type_name_matches_method?(candidate, method_name) ||
|
|
270
|
-
(qt_pointer_type?(raw_type) && candidate.end_with?('Item'))
|
|
304
|
+
next unless candidate.match?(/\AQ[A-Z]\w*\z/)
|
|
271
305
|
|
|
272
306
|
out << candidate
|
|
273
307
|
end
|
|
@@ -299,8 +333,18 @@ def discover_target_qt_classes(ast, scope)
|
|
|
299
333
|
targets = timed("discover_target_qt_classes/#{scope}/related") do
|
|
300
334
|
discover_related_value_classes(ast, base_targets, all_classes, template_classes).sort
|
|
301
335
|
end
|
|
336
|
+
targets = timed("discover_target_qt_classes/#{scope}/static_api_owners") do
|
|
337
|
+
related_static_owners = discover_related_static_api_classes(
|
|
338
|
+
ast,
|
|
339
|
+
(targets + ['QApplication']).uniq,
|
|
340
|
+
all_classes,
|
|
341
|
+
template_classes
|
|
342
|
+
)
|
|
343
|
+
(targets + related_static_owners).sort
|
|
344
|
+
end
|
|
302
345
|
wrapper_targets = timed("discover_target_qt_classes/#{scope}/related_qobject_wrappers") do
|
|
303
|
-
|
|
346
|
+
seed_classes = (targets + ['QApplication']).uniq
|
|
347
|
+
discover_related_qobject_wrapper_classes(ast, seed_classes, all_classes, template_classes, scope).sort
|
|
304
348
|
end
|
|
305
349
|
targets = (targets + wrapper_targets).uniq.sort
|
|
306
350
|
debug_log(
|
|
@@ -391,17 +435,14 @@ def constructor_string_path_cast(ctor_decls)
|
|
|
391
435
|
ctor_decls.filter_map do |decl|
|
|
392
436
|
parsed = parse_method_signature(decl)
|
|
393
437
|
next nil unless parsed && parsed[:params].first
|
|
438
|
+
next nil unless parsed[:required_arg_count] <= 1
|
|
439
|
+
next nil unless parsed[:params].drop(1).all? { |param| param[:has_default] }
|
|
394
440
|
|
|
395
441
|
constructor_string_like_arg_cast(parsed[:params].first[:type])
|
|
396
442
|
end.first
|
|
397
443
|
end
|
|
398
444
|
|
|
399
445
|
def base_spec_hash(qt_class, parent_ctor)
|
|
400
|
-
auto_method_rules = {}
|
|
401
|
-
# Prefer QShortcut#setKeys(StandardKey) in generated API and provide
|
|
402
|
-
# QKeySequence path via setKey(QKeySequence) + Ruby-level compatibility shim.
|
|
403
|
-
auto_method_rules[:setKeys] = { param_types: ['QKeySequence::StandardKey'] } if qt_class == 'QShortcut'
|
|
404
|
-
|
|
405
446
|
{
|
|
406
447
|
qt_class: qt_class,
|
|
407
448
|
ruby_class: qt_class,
|
|
@@ -410,7 +451,6 @@ def base_spec_hash(qt_class, parent_ctor)
|
|
|
410
451
|
constructor: parent_ctor,
|
|
411
452
|
methods: [],
|
|
412
453
|
auto_methods: :all,
|
|
413
|
-
auto_method_rules: auto_method_rules,
|
|
414
454
|
# Constructor availability is already filtered during discovery.
|
|
415
455
|
# Keep validation lightweight for auto-discovered classes to avoid false negatives
|
|
416
456
|
# on template/specialized constructor names in Clang AST.
|