qt 0.1.8 → 0.2.0
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 +29 -12
- data/ext/qt_ruby_bridge/qt_ruby_runtime.hpp +6 -0
- data/ext/qt_ruby_bridge/runtime_events.cpp +8 -3
- data/ext/qt_ruby_bridge/runtime_value_types.cpp +22 -0
- data/lib/qt/event_runtime_dispatch.rb +2 -0
- data/lib/qt/object_wrapper.rb +32 -1
- data/lib/qt/value_wrapper.rb +26 -0
- data/lib/qt/version.rb +1 -1
- data/lib/qt.rb +1 -0
- data/scripts/generate_bridge/auto_methods.rb +39 -9
- data/scripts/generate_bridge/core_utils.rb +5 -1
- data/scripts/generate_bridge/cpp_method_return_emitter.rb +60 -24
- data/scripts/generate_bridge/ffi_api.rb +16 -0
- data/scripts/generate_bridge/free_function_specs.rb +6 -1
- data/scripts/generate_bridge/spec_discovery.rb +118 -17
- data/scripts/generate_bridge.rb +76 -3
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c0e7757f47bb17be90f64d3bcdd28ba17e5e8099a505062a7e7c3dd71aaedf37
|
|
4
|
+
data.tar.gz: cbf55606a5a16e12cb4ef47b73756a1f3ef3b65c1e500583928a44a4d28b97c4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1e5e470938bc069179be121de52a5e2986479a7fa8dec030d743de246fef73812729bc57ea67d4958c076e42e8d23ef703c0931e51544f206352e3ba45a3c463
|
|
7
|
+
data.tar.gz: bc3d7083a9617ad49b2b139d0948bec69257ad5991be08525de8228e87aa3457bdd9d4d2745b399bf20f0266143428565e3fb5d88e039255687eb90b38c7efe9
|
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,15 +51,16 @@ 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[
|
|
44
61
|
runtime_events.cpp
|
|
45
62
|
runtime_signals.cpp
|
|
63
|
+
runtime_value_types.cpp
|
|
46
64
|
].map { |name| File.expand_path("../../ext/qt_ruby_bridge/#{name}", __dir__) }
|
|
47
65
|
|
|
48
66
|
unless File.exist?(generated_cpp)
|
|
@@ -52,8 +70,7 @@ abort "Runtime header not found: #{runtime_hpp}" unless File.exist?(runtime_hpp)
|
|
|
52
70
|
missing_runtime = runtime_cpp_files.reject { |path| File.exist?(path) }
|
|
53
71
|
abort "Runtime source not found: #{missing_runtime.join(', ')}" unless missing_runtime.empty?
|
|
54
72
|
|
|
55
|
-
|
|
56
|
-
FileUtils.cp(generated_cpp, local_cpp) unless File.exist?(local_cpp) && File.identical?(generated_cpp, local_cpp)
|
|
73
|
+
FileUtils.cp(generated_cpp, LOCAL_CPP) unless File.exist?(LOCAL_CPP) && File.identical?(generated_cpp, LOCAL_CPP)
|
|
57
74
|
runtime_cpp_files.each do |runtime_cpp|
|
|
58
75
|
local_runtime_cpp = File.expand_path(File.basename(runtime_cpp))
|
|
59
76
|
unless File.exist?(local_runtime_cpp) && File.identical?(runtime_cpp, local_runtime_cpp)
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
3
|
#include <QApplication>
|
|
4
|
+
#include <QList>
|
|
5
|
+
#include <QVariant>
|
|
4
6
|
|
|
5
7
|
namespace QtRubyRuntime {
|
|
6
8
|
using EventCallback = int (*)(void*, int, const char*);
|
|
@@ -13,6 +15,10 @@ QApplication* qapplication_new(const char* argv0);
|
|
|
13
15
|
// Returns false when teardown is rejected by runtime safety checks.
|
|
14
16
|
bool qapplication_delete(void* app_handle);
|
|
15
17
|
|
|
18
|
+
// FFI-safe conversion for public Qt QList<int> API parameters and results.
|
|
19
|
+
QList<int> qint_list_from_variant(const QVariant& value);
|
|
20
|
+
QVariant qvariant_from_qint_list(const QList<int>& value);
|
|
21
|
+
|
|
16
22
|
void set_event_callback(void* callback_ptr);
|
|
17
23
|
void watch_qobject_event(void* object_handle, int event_type);
|
|
18
24
|
void unwatch_qobject_event(void* object_handle, int event_type);
|
|
@@ -19,6 +19,7 @@ namespace QtRubyRuntime {
|
|
|
19
19
|
constexpr int kEventCallbackIgnore = 0;
|
|
20
20
|
constexpr int kEventCallbackContinue = 1;
|
|
21
21
|
constexpr int kEventCallbackConsume = 2;
|
|
22
|
+
constexpr int kEventCallbackReject = 3;
|
|
22
23
|
|
|
23
24
|
EventCallback& event_callback_ref() {
|
|
24
25
|
static EventCallback callback = nullptr;
|
|
@@ -234,6 +235,10 @@ class EventFilter : public QObject {
|
|
|
234
235
|
event->accept();
|
|
235
236
|
return true;
|
|
236
237
|
}
|
|
238
|
+
if (callback_result == kEventCallbackReject) {
|
|
239
|
+
event->ignore();
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
237
242
|
return QObject::eventFilter(watched, event);
|
|
238
243
|
}
|
|
239
244
|
};
|
|
@@ -244,10 +249,10 @@ EventFilter* event_filter_instance() {
|
|
|
244
249
|
}
|
|
245
250
|
|
|
246
251
|
void ensure_event_filter_installed() {
|
|
247
|
-
static
|
|
248
|
-
if (
|
|
252
|
+
static QApplication* installed_on = nullptr;
|
|
253
|
+
if (qApp && installed_on != qApp) {
|
|
249
254
|
qApp->installEventFilter(event_filter_instance());
|
|
250
|
-
|
|
255
|
+
installed_on = qApp;
|
|
251
256
|
}
|
|
252
257
|
}
|
|
253
258
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#include "qt_ruby_runtime.hpp"
|
|
2
|
+
|
|
3
|
+
namespace QtRubyRuntime {
|
|
4
|
+
QList<int> qint_list_from_variant(const QVariant& value) {
|
|
5
|
+
QList<int> result;
|
|
6
|
+
const QVariantList values = value.toList();
|
|
7
|
+
result.reserve(values.size());
|
|
8
|
+
for (const QVariant& entry : values) {
|
|
9
|
+
result.append(entry.toInt());
|
|
10
|
+
}
|
|
11
|
+
return result;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
QVariant qvariant_from_qint_list(const QList<int>& value) {
|
|
15
|
+
QVariantList result;
|
|
16
|
+
result.reserve(value.size());
|
|
17
|
+
for (int entry : value) {
|
|
18
|
+
result.append(entry);
|
|
19
|
+
}
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
} // namespace QtRubyRuntime
|
|
@@ -7,6 +7,7 @@ module Qt
|
|
|
7
7
|
EVENT_RESULT_IGNORE = 0
|
|
8
8
|
EVENT_RESULT_CONTINUE = 1
|
|
9
9
|
EVENT_RESULT_CONSUME = 2
|
|
10
|
+
EVENT_RESULT_REJECT = 3
|
|
10
11
|
|
|
11
12
|
module_function
|
|
12
13
|
|
|
@@ -20,6 +21,7 @@ module Qt
|
|
|
20
21
|
return EVENT_RESULT_CONTINUE unless handlers && !handlers.empty?
|
|
21
22
|
|
|
22
23
|
results = handlers.map { |handler| handler.call(payload) }
|
|
24
|
+
return EVENT_RESULT_REJECT if results.include?(:reject)
|
|
23
25
|
return EVENT_RESULT_CONSUME if results.any? { |result| result == true || result == :consume }
|
|
24
26
|
return EVENT_RESULT_IGNORE if results.any? { |result| result == false || result == :ignore }
|
|
25
27
|
|
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
|
|
@@ -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]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Qt
|
|
4
|
+
# Owns copied Qt value objects returned through opaque FFI handles.
|
|
5
|
+
module ValueWrapper
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def wrap(handle, qt_class, delete_method)
|
|
9
|
+
return nil if handle.nil? || handle.null?
|
|
10
|
+
|
|
11
|
+
wrapper = Qt.const_get(qt_class).allocate
|
|
12
|
+
wrapper.instance_variable_set(:@handle, handle)
|
|
13
|
+
ObjectSpace.define_finalizer(wrapper, finalizer(delete_method, handle))
|
|
14
|
+
wrapper
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def finalizer(delete_method, handle)
|
|
18
|
+
proc do
|
|
19
|
+
Qt::Native.public_send(delete_method, handle) if Qt::Native.available?
|
|
20
|
+
rescue StandardError
|
|
21
|
+
nil
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
private_class_method :finalizer
|
|
25
|
+
end
|
|
26
|
+
end
|
data/lib/qt/version.rb
CHANGED
data/lib/qt.rb
CHANGED
|
@@ -24,6 +24,7 @@ require_relative 'qt/date_time_codec'
|
|
|
24
24
|
require_relative 'qt/key_sequence_codec'
|
|
25
25
|
require_relative 'qt/object_list_codec'
|
|
26
26
|
require_relative 'qt/variant_codec'
|
|
27
|
+
require_relative 'qt/value_wrapper'
|
|
27
28
|
require_relative 'qt/inspectable'
|
|
28
29
|
require_relative 'qt/object_wrapper'
|
|
29
30
|
require_relative 'qt/application_lifecycle'
|
|
@@ -38,7 +38,7 @@ def map_cpp_intlike_arg_type(type_name, qt_class, int_cast_types)
|
|
|
38
38
|
map_qualified_intlike_arg_type(type_name, qt_class, int_cast_types)
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
def map_cpp_arg_type(type_name, qt_class: nil, int_cast_types: nil)
|
|
41
|
+
def map_cpp_arg_type(type_name, qt_class: nil, int_cast_types: nil, ast: nil)
|
|
42
42
|
raw = type_name.to_s.strip
|
|
43
43
|
return nil if raw.end_with?('&') && !raw.start_with?('const ')
|
|
44
44
|
|
|
@@ -46,7 +46,7 @@ def map_cpp_arg_type(type_name, qt_class: nil, int_cast_types: nil)
|
|
|
46
46
|
|
|
47
47
|
type = raw
|
|
48
48
|
type = type.sub(/\Aconst\s+/, '').sub(/\s*&\z/, '').strip
|
|
49
|
-
return
|
|
49
|
+
return { ffi: :string, cast: :qint_list_from_variant } if type == 'QList<int>'
|
|
50
50
|
return { ffi: :string, cast: :qstring } if type == 'QString'
|
|
51
51
|
return { ffi: :string, cast: :qdatetime_from_utf8 } if type == 'QDateTime'
|
|
52
52
|
return { ffi: :string, cast: :qdate_from_utf8 } if type == 'QDate'
|
|
@@ -58,6 +58,12 @@ def map_cpp_arg_type(type_name, qt_class: nil, int_cast_types: nil)
|
|
|
58
58
|
return { ffi: :string, cast: :qvariant_from_utf8 } if type == 'QVariant'
|
|
59
59
|
return { ffi: :string } if compact_raw.match?(/\Aconst\s+char\s*\*\z/)
|
|
60
60
|
|
|
61
|
+
if ast_qt_value_class?(ast, type)
|
|
62
|
+
return { ffi: :pointer, cast: :qt_value_ref, value_class: type }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
return nil if unsupported_cpp_type?(type)
|
|
66
|
+
|
|
61
67
|
map_cpp_pointer_arg_type(type, qt_class) || map_cpp_intlike_arg_type(type, qt_class, int_cast_types)
|
|
62
68
|
end
|
|
63
69
|
|
|
@@ -70,11 +76,25 @@ end
|
|
|
70
76
|
|
|
71
77
|
def map_cpp_return_type(type_name, ast: nil)
|
|
72
78
|
raw = type_name.to_s.strip
|
|
79
|
+
type = raw.sub(/\Aconst\s+/, '').sub(/\s*&\z/, '').strip
|
|
80
|
+
return { ffi_return: :string, return_cast: :qint_list_to_variant } if type == 'QList<int>'
|
|
73
81
|
return nil if unsupported_cpp_type?(raw)
|
|
74
82
|
return nil if raw.start_with?('const ') && raw.end_with?('*')
|
|
75
83
|
|
|
76
|
-
type
|
|
77
|
-
|
|
84
|
+
map_scalar_cpp_return_type(type) || map_pointer_cpp_return_type(type, ast: ast) || map_qt_value_return_type(type, ast: ast)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def ast_qt_value_class?(ast, type)
|
|
88
|
+
return false unless ast && type.match?(/\AQ[A-Z]\w*\z/)
|
|
89
|
+
return false unless ast_class_index(ast)[:methods_by_class].key?(type)
|
|
90
|
+
|
|
91
|
+
!class_inherits?(ast, type, 'QObject')
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def map_qt_value_return_type(type, ast:)
|
|
95
|
+
return nil unless ast_qt_value_class?(ast, type)
|
|
96
|
+
|
|
97
|
+
{ ffi_return: :pointer, return_cast: :qt_value_copy, value_class: type }
|
|
78
98
|
end
|
|
79
99
|
|
|
80
100
|
def map_scalar_cpp_return_type(type)
|
|
@@ -135,7 +155,7 @@ def parse_method_signature(method_decl)
|
|
|
135
155
|
}
|
|
136
156
|
end
|
|
137
157
|
|
|
138
|
-
def build_auto_method_args(parsed, entry, qt_class, int_cast_types)
|
|
158
|
+
def build_auto_method_args(parsed, entry, qt_class, int_cast_types, ast)
|
|
139
159
|
arg_cast_overrides = Array(entry[:arg_casts])
|
|
140
160
|
params = parsed[:params]
|
|
141
161
|
required_arg_count = 0
|
|
@@ -143,7 +163,7 @@ def build_auto_method_args(parsed, entry, qt_class, int_cast_types)
|
|
|
143
163
|
|
|
144
164
|
params.each_with_index do |param, idx|
|
|
145
165
|
cast_override = arg_cast_overrides[idx]
|
|
146
|
-
arg_info = map_cpp_arg_type(param[:type], qt_class: qt_class, int_cast_types: int_cast_types)
|
|
166
|
+
arg_info = map_cpp_arg_type(param[:type], qt_class: qt_class, int_cast_types: int_cast_types, ast: ast)
|
|
147
167
|
arg_info ||= { ffi: :int } if cast_override
|
|
148
168
|
unless arg_info
|
|
149
169
|
return nil unless skip_unsupported_optional_tail?(params, idx, param)
|
|
@@ -151,7 +171,12 @@ def build_auto_method_args(parsed, entry, qt_class, int_cast_types)
|
|
|
151
171
|
break
|
|
152
172
|
end
|
|
153
173
|
|
|
154
|
-
args << {
|
|
174
|
+
args << {
|
|
175
|
+
name: param[:name],
|
|
176
|
+
ffi: arg_info[:ffi],
|
|
177
|
+
cast: cast_override || arg_info[:cast],
|
|
178
|
+
value_class: arg_info[:value_class]
|
|
179
|
+
}.compact
|
|
155
180
|
required_arg_count += 1 unless param[:has_default]
|
|
156
181
|
end
|
|
157
182
|
|
|
@@ -174,6 +199,7 @@ def build_auto_method_hash(entry, ret_info, args, required_arg_count)
|
|
|
174
199
|
}
|
|
175
200
|
method[:return_cast] = ret_info[:return_cast] if ret_info[:return_cast]
|
|
176
201
|
method[:pointer_class] = ret_info[:pointer_class] if ret_info[:pointer_class]
|
|
202
|
+
method[:value_class] = ret_info[:value_class] if ret_info[:value_class]
|
|
177
203
|
method
|
|
178
204
|
end
|
|
179
205
|
|
|
@@ -184,7 +210,7 @@ def build_auto_method_from_decl(method_decl, entry, qt_class:, int_cast_types:,
|
|
|
184
210
|
ret_info = map_cpp_return_type(parsed[:return_type], ast: ast)
|
|
185
211
|
return nil unless ret_info
|
|
186
212
|
|
|
187
|
-
args, required_arg_count = build_auto_method_args(parsed, entry, qt_class, int_cast_types)
|
|
213
|
+
args, required_arg_count = build_auto_method_args(parsed, entry, qt_class, int_cast_types, ast)
|
|
188
214
|
return nil unless args
|
|
189
215
|
|
|
190
216
|
build_auto_method_hash(entry, ret_info, args, required_arg_count)
|
|
@@ -352,7 +378,11 @@ def resolve_auto_method(ast, qt_class, auto_entry)
|
|
|
352
378
|
built = resolve_auto_method_built_candidates(ast, qt_class, entry)
|
|
353
379
|
return per_ast_cache[cache_key] = nil unless built
|
|
354
380
|
|
|
355
|
-
per_ast_cache[cache_key] = built.min_by
|
|
381
|
+
per_ast_cache[cache_key] = built.min_by do |candidate|
|
|
382
|
+
method = candidate[:method]
|
|
383
|
+
value_arg_count = method[:args].count { |arg| arg[:cast] == :qt_value_ref }
|
|
384
|
+
[value_arg_count, method[:args].length]
|
|
385
|
+
end[:method]
|
|
356
386
|
end
|
|
357
387
|
|
|
358
388
|
def auto_entries_for_spec(spec, ast)
|
|
@@ -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
|
|
@@ -10,6 +10,8 @@ class CppMethodReturnEmitter
|
|
|
10
10
|
|
|
11
11
|
def emit
|
|
12
12
|
return emit_void if method[:ffi_return] == :void
|
|
13
|
+
return emit_qt_value_copy if method[:return_cast] == :qt_value_copy
|
|
14
|
+
return emit_qint_list if method[:return_cast] == :qint_list_to_variant
|
|
13
15
|
return emit_qstring if qstring_return?
|
|
14
16
|
return emit_qobject_list if qobject_list_return?
|
|
15
17
|
return emit_qdatetime if qdatetime_return?
|
|
@@ -54,52 +56,86 @@ class CppMethodReturnEmitter
|
|
|
54
56
|
end
|
|
55
57
|
|
|
56
58
|
def emit_qstring
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
lines <<
|
|
60
|
-
lines <<
|
|
59
|
+
result = unique_local_name('result')
|
|
60
|
+
utf8 = unique_local_name('utf8')
|
|
61
|
+
lines << " const QString #{result} = #{invocation};"
|
|
62
|
+
lines << " thread_local QByteArray #{utf8};"
|
|
63
|
+
lines << " #{utf8} = #{result}.toUtf8();"
|
|
64
|
+
lines << " return #{utf8}.constData();"
|
|
61
65
|
end
|
|
62
66
|
|
|
63
67
|
def emit_qvariant
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
lines <<
|
|
67
|
-
lines <<
|
|
68
|
+
result = unique_local_name('result')
|
|
69
|
+
utf8 = unique_local_name('utf8')
|
|
70
|
+
lines << " const QVariant #{result} = #{invocation};"
|
|
71
|
+
lines << " thread_local QByteArray #{utf8};"
|
|
72
|
+
lines << " #{utf8} = qvariant_to_bridge_string(#{result}).toUtf8();"
|
|
73
|
+
lines << " return #{utf8}.constData();"
|
|
68
74
|
end
|
|
69
75
|
|
|
70
76
|
def emit_qobject_list
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
lines <<
|
|
74
|
-
lines <<
|
|
77
|
+
result = unique_local_name('result')
|
|
78
|
+
utf8 = unique_local_name('utf8')
|
|
79
|
+
lines << " const QObjectList #{result} = #{invocation};"
|
|
80
|
+
lines << " thread_local QByteArray #{utf8};"
|
|
81
|
+
lines << " #{utf8} = qobject_list_to_bridge_string(#{result}).toUtf8();"
|
|
82
|
+
lines << " return #{utf8}.constData();"
|
|
75
83
|
end
|
|
76
84
|
|
|
77
85
|
def emit_qdatetime
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
lines <<
|
|
81
|
-
lines <<
|
|
86
|
+
result = unique_local_name('result')
|
|
87
|
+
utf8 = unique_local_name('utf8')
|
|
88
|
+
lines << " const QDateTime #{result} = #{invocation};"
|
|
89
|
+
lines << " thread_local QByteArray #{utf8};"
|
|
90
|
+
lines << " #{utf8} = qdatetime_to_bridge_string(#{result}).toUtf8();"
|
|
91
|
+
lines << " return #{utf8}.constData();"
|
|
82
92
|
end
|
|
83
93
|
|
|
84
94
|
def emit_qdate
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
lines <<
|
|
88
|
-
lines <<
|
|
95
|
+
result = unique_local_name('result')
|
|
96
|
+
utf8 = unique_local_name('utf8')
|
|
97
|
+
lines << " const QDate #{result} = #{invocation};"
|
|
98
|
+
lines << " thread_local QByteArray #{utf8};"
|
|
99
|
+
lines << " #{utf8} = qdate_to_bridge_string(#{result}).toUtf8();"
|
|
100
|
+
lines << " return #{utf8}.constData();"
|
|
89
101
|
end
|
|
90
102
|
|
|
91
103
|
def emit_qtime
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
lines <<
|
|
95
|
-
lines <<
|
|
104
|
+
result = unique_local_name('result')
|
|
105
|
+
utf8 = unique_local_name('utf8')
|
|
106
|
+
lines << " const QTime #{result} = #{invocation};"
|
|
107
|
+
lines << " thread_local QByteArray #{utf8};"
|
|
108
|
+
lines << " #{utf8} = qtime_to_bridge_string(#{result}).toUtf8();"
|
|
109
|
+
lines << " return #{utf8}.constData();"
|
|
96
110
|
end
|
|
97
111
|
|
|
98
112
|
def emit_pointer
|
|
99
113
|
lines << " return const_cast<void*>(static_cast<const void*>(#{invocation}));"
|
|
100
114
|
end
|
|
101
115
|
|
|
116
|
+
def emit_qt_value_copy
|
|
117
|
+
lines << " return new #{method[:value_class]}(#{invocation});"
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def emit_qint_list
|
|
121
|
+
result = unique_local_name('result')
|
|
122
|
+
variant = unique_local_name('variant')
|
|
123
|
+
utf8 = unique_local_name('utf8')
|
|
124
|
+
lines << " const QList<int> #{result} = #{invocation};"
|
|
125
|
+
lines << " const QVariant #{variant} = QtRubyRuntime::qvariant_from_qint_list(#{result});"
|
|
126
|
+
lines << " thread_local QByteArray #{utf8};"
|
|
127
|
+
lines << " #{utf8} = qvariant_to_bridge_string(#{variant}).toUtf8();"
|
|
128
|
+
lines << " return #{utf8}.constData();"
|
|
129
|
+
end
|
|
130
|
+
|
|
102
131
|
def emit_value
|
|
103
132
|
lines << " return #{invocation};"
|
|
104
133
|
end
|
|
134
|
+
|
|
135
|
+
def unique_local_name(suffix)
|
|
136
|
+
used = Array(method[:args]).map { |arg| arg[:name].to_s }.to_set
|
|
137
|
+
candidate = "qt_ruby_#{suffix}"
|
|
138
|
+
candidate += '_' while used.include?(candidate)
|
|
139
|
+
candidate
|
|
140
|
+
end
|
|
105
141
|
end
|
|
@@ -15,9 +15,25 @@ def all_ffi_functions(specs, free_function_specs:)
|
|
|
15
15
|
append_method_ffi_functions(fns, spec)
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
value_wrapper_classes(specs, free_function_specs: free_function_specs).each do |qt_class|
|
|
19
|
+
fns << { name: qt_value_delete_function_name(qt_class), ffi_return: :void, args: [:pointer] }
|
|
20
|
+
end
|
|
21
|
+
|
|
18
22
|
fns
|
|
19
23
|
end
|
|
20
24
|
|
|
25
|
+
def value_wrapper_classes(specs, free_function_specs: [])
|
|
26
|
+
spec_classes = specs.flat_map do |spec|
|
|
27
|
+
spec[:methods].filter_map { |method| method[:value_class] if method[:return_cast] == :qt_value_copy }
|
|
28
|
+
end
|
|
29
|
+
free_classes = free_function_specs.filter_map { |spec| spec[:value_class] }
|
|
30
|
+
(spec_classes + free_classes).uniq.sort
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def qt_value_delete_function_name(qt_class)
|
|
34
|
+
"qt_ruby_#{to_snake(qt_class)}_value_delete"
|
|
35
|
+
end
|
|
36
|
+
|
|
21
37
|
def append_constructor_ffi_function(fns, spec)
|
|
22
38
|
return if spec[:constructor][:mode] == :wrap_only
|
|
23
39
|
|
|
@@ -215,6 +215,8 @@ def build_qapplication_static_free_function_spec(ast, qt_name, int_cast_types)
|
|
|
215
215
|
cpp_body =
|
|
216
216
|
if candidate[:ffi_return] == :void
|
|
217
217
|
["QApplication::#{qt_name}();"]
|
|
218
|
+
elsif candidate[:return_cast] == :qt_value_copy
|
|
219
|
+
["return new #{candidate[:value_class]}(QApplication::#{qt_name}());"]
|
|
218
220
|
elsif candidate[:enum_cast]
|
|
219
221
|
["return static_cast<int>(QApplication::#{qt_name}());"]
|
|
220
222
|
elsif candidate[:ffi_return] == :string
|
|
@@ -231,6 +233,7 @@ def build_qapplication_static_free_function_spec(ast, qt_name, int_cast_types)
|
|
|
231
233
|
{
|
|
232
234
|
name: "qt_ruby_#{native_name}",
|
|
233
235
|
ffi_return: candidate[:ffi_return],
|
|
236
|
+
value_class: candidate[:value_class],
|
|
234
237
|
args: [],
|
|
235
238
|
cpp_return: ffi_return_to_cpp(candidate[:ffi_return]),
|
|
236
239
|
cpp_args: '',
|
|
@@ -240,7 +243,8 @@ def build_qapplication_static_free_function_spec(ast, qt_name, int_cast_types)
|
|
|
240
243
|
native: native_name,
|
|
241
244
|
args: [],
|
|
242
245
|
return_cast: candidate[:return_cast],
|
|
243
|
-
pointer_class: candidate[:pointer_class]
|
|
246
|
+
pointer_class: candidate[:pointer_class],
|
|
247
|
+
value_class: candidate[:value_class]
|
|
244
248
|
}
|
|
245
249
|
}
|
|
246
250
|
end
|
|
@@ -271,6 +275,7 @@ def qapplication_static_return_info(return_type, int_cast_types, ast:)
|
|
|
271
275
|
ffi_return: mapped[:ffi_return],
|
|
272
276
|
return_cast: mapped[:return_cast],
|
|
273
277
|
pointer_class: mapped[:pointer_class],
|
|
278
|
+
value_class: mapped[:value_class],
|
|
274
279
|
enum_cast: false
|
|
275
280
|
}
|
|
276
281
|
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)
|
|
@@ -215,9 +299,14 @@ def discover_target_qt_classes(ast, scope)
|
|
|
215
299
|
targets = timed("discover_target_qt_classes/#{scope}/related") do
|
|
216
300
|
discover_related_value_classes(ast, base_targets, all_classes, template_classes).sort
|
|
217
301
|
end
|
|
302
|
+
wrapper_targets = timed("discover_target_qt_classes/#{scope}/related_qobject_wrappers") do
|
|
303
|
+
seed_classes = (targets + ['QApplication']).uniq
|
|
304
|
+
discover_related_qobject_wrapper_classes(ast, seed_classes, all_classes, template_classes, scope).sort
|
|
305
|
+
end
|
|
306
|
+
targets = (targets + wrapper_targets).uniq.sort
|
|
218
307
|
debug_log(
|
|
219
308
|
"discover_target_qt_classes scope=#{scope} total_q=#{all_classes.length} " \
|
|
220
|
-
"base=#{base_targets.length} targets=#{targets.length}"
|
|
309
|
+
"base=#{base_targets.length} wrappers=#{wrapper_targets.length} targets=#{targets.length}"
|
|
221
310
|
)
|
|
222
311
|
targets
|
|
223
312
|
end
|
|
@@ -264,7 +353,7 @@ def constructor_usable_for_codegen?(ast, qt_class)
|
|
|
264
353
|
ctor_decls = collect_constructor_decls(ast, qt_class)
|
|
265
354
|
ctor_decls.any? do |decl|
|
|
266
355
|
constructor_keysequence_parent_type(decl) ||
|
|
267
|
-
|
|
356
|
+
constructor_supports_parent_only?(decl) ||
|
|
268
357
|
constructor_supports_no_args?(decl) ||
|
|
269
358
|
constructor_supports_string_path?(decl)
|
|
270
359
|
end
|
|
@@ -272,28 +361,40 @@ end
|
|
|
272
361
|
|
|
273
362
|
def build_base_spec_for_qt_class(ast, qt_class)
|
|
274
363
|
ctor_decls = collect_constructor_decls(ast, qt_class)
|
|
364
|
+
parent_ctor = if abstract_class?(ast, qt_class)
|
|
365
|
+
{ parent: false, mode: :wrap_only }
|
|
366
|
+
else
|
|
367
|
+
constructor_spec_for_qt_class(ast, qt_class, ctor_decls)
|
|
368
|
+
end
|
|
369
|
+
base_spec_hash(qt_class, parent_ctor)
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
def constructor_spec_for_qt_class(ast, qt_class, ctor_decls)
|
|
275
373
|
keysequence_parent_type = ctor_decls.filter_map { |decl| constructor_keysequence_parent_type(decl) }.first
|
|
276
374
|
parent_type = ctor_decls.filter_map { |decl| parent_constructor_first_type(decl) }.first
|
|
277
|
-
string_path_cast = ctor_decls
|
|
375
|
+
string_path_cast = constructor_string_path_cast(ctor_decls)
|
|
376
|
+
widget_child = qt_class != 'QWidget' && class_inherits?(ast, qt_class, 'QWidget')
|
|
377
|
+
|
|
378
|
+
if keysequence_parent_type
|
|
379
|
+
{ parent: true, parent_type: keysequence_parent_type, mode: :keysequence_parent, register_in_parent: widget_child }
|
|
380
|
+
elsif parent_type
|
|
381
|
+
parent_constructor_for_type(parent_type, widget_child)
|
|
382
|
+
elsif string_path_cast
|
|
383
|
+
string_path_constructor(string_path_cast)
|
|
384
|
+
elsif ctor_decls.any? { |decl| constructor_supports_no_args?(decl) }
|
|
385
|
+
{ parent: false }
|
|
386
|
+
else
|
|
387
|
+
{ parent: false, mode: :wrap_only }
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def constructor_string_path_cast(ctor_decls)
|
|
392
|
+
ctor_decls.filter_map do |decl|
|
|
278
393
|
parsed = parse_method_signature(decl)
|
|
279
394
|
next nil unless parsed && parsed[:params].first
|
|
280
395
|
|
|
281
396
|
constructor_string_like_arg_cast(parsed[:params].first[:type])
|
|
282
397
|
end.first
|
|
283
|
-
widget_child = qt_class != 'QWidget' && class_inherits?(ast, qt_class, 'QWidget')
|
|
284
|
-
parent_ctor =
|
|
285
|
-
if keysequence_parent_type
|
|
286
|
-
{ parent: true, parent_type: keysequence_parent_type, mode: :keysequence_parent, register_in_parent: widget_child }
|
|
287
|
-
elsif parent_type
|
|
288
|
-
parent_constructor_for_type(parent_type, widget_child)
|
|
289
|
-
elsif string_path_cast
|
|
290
|
-
string_path_constructor(string_path_cast)
|
|
291
|
-
elsif ctor_decls.any? { |decl| constructor_supports_no_args?(decl) }
|
|
292
|
-
{ parent: false }
|
|
293
|
-
else
|
|
294
|
-
{ parent: false, mode: :wrap_only }
|
|
295
|
-
end
|
|
296
|
-
base_spec_hash(qt_class, parent_ctor)
|
|
297
398
|
end
|
|
298
399
|
|
|
299
400
|
def base_spec_hash(qt_class, parent_ctor)
|
data/scripts/generate_bridge.rb
CHANGED
|
@@ -250,7 +250,9 @@ def arg_expr(arg)
|
|
|
250
250
|
when :qicon_ref then "*static_cast<QIcon*>(#{arg[:name]})"
|
|
251
251
|
when :qany_string_view then "QAnyStringView(as_qstring(#{arg[:name]}))"
|
|
252
252
|
when :qvariant_from_utf8 then "qvariant_from_bridge_value(#{arg[:name]})"
|
|
253
|
+
when :qint_list_from_variant then "QtRubyRuntime::qint_list_from_variant(qvariant_from_bridge_value(#{arg[:name]}))"
|
|
253
254
|
when :alignment then "static_cast<Qt::Alignment>(#{arg[:name]})"
|
|
255
|
+
when :qt_value_ref then "*static_cast<#{arg[:value_class]}*>(#{arg[:name]})"
|
|
254
256
|
when String then "static_cast<#{arg[:cast]}>(#{arg[:name]})"
|
|
255
257
|
else
|
|
256
258
|
arg[:name]
|
|
@@ -335,6 +337,15 @@ def generate_cpp_delete(lines)
|
|
|
335
337
|
lines << '}'
|
|
336
338
|
end
|
|
337
339
|
|
|
340
|
+
def generate_cpp_qt_value_deletes(lines, specs, free_function_specs)
|
|
341
|
+
value_wrapper_classes(specs, free_function_specs: free_function_specs).each do |qt_class|
|
|
342
|
+
lines << "extern \"C\" void #{qt_value_delete_function_name(qt_class)}(void* handle) {"
|
|
343
|
+
lines << " delete static_cast<#{qt_class}*>(handle);"
|
|
344
|
+
lines << '}'
|
|
345
|
+
lines << ''
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
|
|
338
349
|
def cpp_method_signature(method)
|
|
339
350
|
['void* handle'] + method[:args].map { |arg| "#{ffi_to_cpp_type(arg[:ffi])} #{arg[:name]}" }
|
|
340
351
|
end
|
|
@@ -381,6 +392,7 @@ def generate_cpp_bridge(specs, free_function_specs)
|
|
|
381
392
|
|
|
382
393
|
specs.each { |spec| append_cpp_spec_methods(lines, spec) }
|
|
383
394
|
|
|
395
|
+
generate_cpp_qt_value_deletes(lines, specs, free_function_specs)
|
|
384
396
|
generate_cpp_delete(lines)
|
|
385
397
|
"#{lines.join("\n")}\n"
|
|
386
398
|
end
|
|
@@ -640,6 +652,7 @@ def optional_arg_replacement(arg, safe)
|
|
|
640
652
|
when :bool then "(#{safe}.nil? ? false : #{safe})"
|
|
641
653
|
when :pointer then safe
|
|
642
654
|
when :string
|
|
655
|
+
return "(#{safe}.nil? ? Qt::VariantCodec.encode([]) : Qt::VariantCodec.encode(#{safe}))" if arg[:cast] == :qint_list_from_variant
|
|
643
656
|
return "(#{safe}.nil? ? '' : Qt::VariantCodec.encode(#{safe}))" if arg[:cast] == :qvariant_from_utf8
|
|
644
657
|
return "(#{safe}.nil? ? '' : Qt::DateTimeCodec.encode_qdatetime(#{safe}))" if arg[:cast] == :qdatetime_from_utf8
|
|
645
658
|
return "(#{safe}.nil? ? '' : Qt::DateTimeCodec.encode_qdate(#{safe}))" if arg[:cast] == :qdate_from_utf8
|
|
@@ -653,8 +666,13 @@ def optional_arg_replacement(arg, safe)
|
|
|
653
666
|
end
|
|
654
667
|
|
|
655
668
|
def ruby_arg_call_value(arg, safe, optional:)
|
|
669
|
+
if arg[:cast] == :qt_value_ref
|
|
670
|
+
return optional ? "(#{safe}.nil? ? nil : #{safe}.handle)" : "#{safe}.handle"
|
|
671
|
+
end
|
|
672
|
+
|
|
656
673
|
return "Qt::StringCodec.to_qt_text(#{safe})" if text_bridge_arg?(arg) && !optional
|
|
657
674
|
return "Qt::VariantCodec.encode(#{safe})" if arg[:cast] == :qvariant_from_utf8 && !optional
|
|
675
|
+
return "Qt::VariantCodec.encode(#{safe})" if arg[:cast] == :qint_list_from_variant && !optional
|
|
658
676
|
return "Qt::DateTimeCodec.encode_qdatetime(#{safe})" if arg[:cast] == :qdatetime_from_utf8 && !optional
|
|
659
677
|
return "Qt::DateTimeCodec.encode_qdate(#{safe})" if arg[:cast] == :qdate_from_utf8 && !optional
|
|
660
678
|
return "Qt::DateTimeCodec.encode_qtime(#{safe})" if arg[:cast] == :qtime_from_utf8 && !optional
|
|
@@ -698,9 +716,14 @@ def ruby_native_method_body(method, rewritten_native_call)
|
|
|
698
716
|
return "Qt::ObjectListCodec.decode(#{rewritten_native_call}, '#{method[:object_list_class]}')"
|
|
699
717
|
end
|
|
700
718
|
return "Qt::VariantCodec.decode(#{rewritten_native_call})" if method[:return_cast] == :qvariant_to_utf8
|
|
719
|
+
return "Qt::VariantCodec.decode(#{rewritten_native_call})" if method[:return_cast] == :qint_list_to_variant
|
|
701
720
|
return "Qt::DateTimeCodec.decode_qdatetime(#{rewritten_native_call})" if method[:return_cast] == :qdatetime_to_utf8
|
|
702
721
|
return "Qt::DateTimeCodec.decode_qdate(#{rewritten_native_call})" if method[:return_cast] == :qdate_to_utf8
|
|
703
722
|
return "Qt::DateTimeCodec.decode_qtime(#{rewritten_native_call})" if method[:return_cast] == :qtime_to_utf8
|
|
723
|
+
if method[:return_cast] == :qt_value_copy
|
|
724
|
+
delete_method = qt_value_delete_function_name(method[:value_class])
|
|
725
|
+
return "Qt::ValueWrapper.wrap(#{rewritten_native_call}, '#{method[:value_class]}', :#{delete_method})"
|
|
726
|
+
end
|
|
704
727
|
return "Qt::ObjectWrapper.wrap(#{rewritten_native_call}, '#{method[:pointer_class]}')" if method[:pointer_class]
|
|
705
728
|
|
|
706
729
|
rewritten_native_call
|
|
@@ -899,6 +922,11 @@ def qapplication_class_method_body(method, native_call)
|
|
|
899
922
|
return " Qt::DateTimeCodec.decode_qdatetime(#{native_call})" if method[:return_cast] == :qdatetime_to_utf8
|
|
900
923
|
return " Qt::DateTimeCodec.decode_qdate(#{native_call})" if method[:return_cast] == :qdate_to_utf8
|
|
901
924
|
return " Qt::DateTimeCodec.decode_qtime(#{native_call})" if method[:return_cast] == :qtime_to_utf8
|
|
925
|
+
return " Qt::VariantCodec.decode(#{native_call})" if method[:return_cast] == :qint_list_to_variant
|
|
926
|
+
if method[:return_cast] == :qt_value_copy
|
|
927
|
+
delete_method = qt_value_delete_function_name(method[:value_class])
|
|
928
|
+
return " Qt::ValueWrapper.wrap(#{native_call}, '#{method[:value_class]}', :#{delete_method})"
|
|
929
|
+
end
|
|
902
930
|
return " Qt::ObjectWrapper.wrap(#{native_call}, '#{method[:pointer_class]}')" if method[:pointer_class]
|
|
903
931
|
|
|
904
932
|
" #{native_call}"
|
|
@@ -920,13 +948,50 @@ end
|
|
|
920
948
|
def append_ruby_widget_methods(lines, spec)
|
|
921
949
|
spec[:methods].each do |method|
|
|
922
950
|
call_args = ['@handle'] + method[:args].map { |arg| arg[:name] }
|
|
923
|
-
|
|
951
|
+
native_name = method_function_name(spec, method).delete_prefix('qt_ruby_')
|
|
952
|
+
native_call = "Native.#{native_name}(#{call_args.join(', ')})"
|
|
924
953
|
append_ruby_native_call_method(lines, method: method, native_call: native_call, indent: ' ')
|
|
925
954
|
append_ruby_property_writer(lines, method: method, indent: ' ')
|
|
926
955
|
lines << ''
|
|
927
956
|
end
|
|
928
957
|
end
|
|
929
958
|
|
|
959
|
+
def assign_unique_native_method_names(specs)
|
|
960
|
+
used = Set.new
|
|
961
|
+
specs.map do |spec|
|
|
962
|
+
methods = spec[:methods].map do |method|
|
|
963
|
+
assign_unique_native_method_name(spec, method, used)
|
|
964
|
+
end
|
|
965
|
+
spec.merge(methods: methods)
|
|
966
|
+
end
|
|
967
|
+
end
|
|
968
|
+
|
|
969
|
+
def assign_unique_native_method_name(spec, method, used)
|
|
970
|
+
native_name = default_method_function_name(spec, method)
|
|
971
|
+
return used_add_and_return(used, native_name, method) unless used.include?(native_name)
|
|
972
|
+
|
|
973
|
+
unique_name = next_unique_native_method_name(spec, method, used)
|
|
974
|
+
debug_log("native name collision #{native_name} -> #{unique_name}")
|
|
975
|
+
used << unique_name
|
|
976
|
+
method.merge(native_name: unique_name)
|
|
977
|
+
end
|
|
978
|
+
|
|
979
|
+
def used_add_and_return(used, value, result)
|
|
980
|
+
used << value
|
|
981
|
+
result
|
|
982
|
+
end
|
|
983
|
+
|
|
984
|
+
def next_unique_native_method_name(spec, method, used)
|
|
985
|
+
base = "qt_ruby_#{spec[:prefix]}_qt_#{to_snake(method[:qt_name])}"
|
|
986
|
+
candidate = base
|
|
987
|
+
counter = 2
|
|
988
|
+
while used.include?(candidate)
|
|
989
|
+
candidate = "#{base}_#{counter}"
|
|
990
|
+
counter += 1
|
|
991
|
+
end
|
|
992
|
+
candidate
|
|
993
|
+
end
|
|
994
|
+
|
|
930
995
|
def generate_ruby_widget_class(lines, spec, specs_by_qt, super_qt_by_qt, qt_to_ruby)
|
|
931
996
|
metadata = ruby_api_metadata_for_spec(spec, specs_by_qt, super_qt_by_qt)
|
|
932
997
|
super_ruby = ruby_super_class_for_spec(spec, super_qt_by_qt, qt_to_ruby)
|
|
@@ -1136,6 +1201,7 @@ def collect_qt_scoped_enum_constants(ast, warnings = [])
|
|
|
1136
1201
|
next unless owner.match?(/\AQ[A-Z]\w*\z/)
|
|
1137
1202
|
next if owner == 'Qt' || owner == 'QEvent'
|
|
1138
1203
|
|
|
1204
|
+
next_value = 0
|
|
1139
1205
|
Array(node['inner']).each do |entry|
|
|
1140
1206
|
next unless entry['kind'] == 'EnumConstantDecl'
|
|
1141
1207
|
|
|
@@ -1144,7 +1210,13 @@ def collect_qt_scoped_enum_constants(ast, warnings = [])
|
|
|
1144
1210
|
|
|
1145
1211
|
raw_value = ast_extract_first_value(entry)
|
|
1146
1212
|
value = parse_ast_integer_value(raw_value)
|
|
1147
|
-
|
|
1213
|
+
value = next_value if value.nil? && !next_value.nil?
|
|
1214
|
+
if value.nil?
|
|
1215
|
+
next_value = nil
|
|
1216
|
+
next
|
|
1217
|
+
end
|
|
1218
|
+
|
|
1219
|
+
next_value = value + 1
|
|
1148
1220
|
|
|
1149
1221
|
append_constant_with_conflict_warning(
|
|
1150
1222
|
constants_by_owner[owner],
|
|
@@ -1202,7 +1274,8 @@ free_function_specs = timed('build_free_function_specs') { qt_free_function_spec
|
|
|
1202
1274
|
base_specs = timed('build_base_specs') { build_base_specs(ast) }
|
|
1203
1275
|
timed('validate_qt_api') { validate_qt_api!(ast, base_specs) }
|
|
1204
1276
|
expanded_specs = timed('expand_auto_methods') { expand_auto_methods(base_specs, ast) }
|
|
1205
|
-
|
|
1277
|
+
property_specs = timed('enrich_specs_with_properties') { enrich_specs_with_properties(expanded_specs, ast) }
|
|
1278
|
+
effective_specs = timed('assign_unique_native_method_names') { assign_unique_native_method_names(property_specs) }
|
|
1206
1279
|
super_qt_by_qt, wrapper_qt_classes = timed('build_generated_inheritance') do
|
|
1207
1280
|
build_generated_inheritance(ast, effective_specs)
|
|
1208
1281
|
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.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Maksim Veynberg
|
|
@@ -47,6 +47,7 @@ files:
|
|
|
47
47
|
- ext/qt_ruby_bridge/qt_ruby_runtime.hpp
|
|
48
48
|
- ext/qt_ruby_bridge/runtime_events.cpp
|
|
49
49
|
- ext/qt_ruby_bridge/runtime_signals.cpp
|
|
50
|
+
- ext/qt_ruby_bridge/runtime_value_types.cpp
|
|
50
51
|
- lib/qt.rb
|
|
51
52
|
- lib/qt/application_lifecycle.rb
|
|
52
53
|
- lib/qt/bridge.rb
|
|
@@ -66,6 +67,7 @@ files:
|
|
|
66
67
|
- lib/qt/object_wrapper.rb
|
|
67
68
|
- lib/qt/shortcut_compat.rb
|
|
68
69
|
- lib/qt/string_codec.rb
|
|
70
|
+
- lib/qt/value_wrapper.rb
|
|
69
71
|
- lib/qt/variant_codec.rb
|
|
70
72
|
- lib/qt/version.rb
|
|
71
73
|
- scripts/generate_bridge.rb
|
|
@@ -100,7 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
100
102
|
- !ruby/object:Gem::Version
|
|
101
103
|
version: '0'
|
|
102
104
|
requirements: []
|
|
103
|
-
rubygems_version: 4.0.
|
|
105
|
+
rubygems_version: 4.0.10
|
|
104
106
|
specification_version: 4
|
|
105
107
|
summary: Ruby bindings for Qt 6.4.2+
|
|
106
108
|
test_files: []
|