Module: Qt::ObjectWrapper

Defined in:
lib/qt/object_wrapper.rb

Overview

Wrap native QObject-derived pointers into generated Ruby wrapper instances.

Defined Under Namespace

Modules: ConstructorCacheHook

Class Method Summary collapse

Class Method Details

.cache_wrapper(wrapper) ⇒ Object



100
101
102
103
104
105
# File 'lib/qt/object_wrapper.rb', line 100

def cache_wrapper(wrapper)
  pointer = wrapper.handle
  wrapper_cache[pointer.address] = wrapper
  ensure_destroy_hook(wrapper, pointer)
  wrapper
end

.cached_wrapper_for(pointer) ⇒ Object



83
84
85
# File 'lib/qt/object_wrapper.rb', line 83

def cached_wrapper_for(pointer)
  wrapper_cache[pointer.address]
end

.candidate_wrapper_classes(expected_qt_class) ⇒ Object



41
42
43
44
45
46
47
48
49
50
# File 'lib/qt/object_wrapper.rb', line 41

def candidate_wrapper_classes(expected_qt_class)
  normalized_qt_class = normalize_expected_qt_class(expected_qt_class)
  @candidate_wrapper_classes ||= {}
  @candidate_wrapper_classes[normalized_qt_class] ||= begin
    base = fallback_wrapper_class(normalized_qt_class)
    wrappers = qobject_wrapper_classes
    wrappers = wrappers.select { |klass| klass <= base } if base
    wrappers.sort_by { |klass| -inheritance_depth(klass) }
  end
end

.destroy_hook_addressesObject



135
136
137
# File 'lib/qt/object_wrapper.rb', line 135

def destroy_hook_addresses
  @destroy_hook_addresses ||= {}
end

.ensure_destroy_hook(wrapper, pointer) ⇒ Object



166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/qt/object_wrapper.rb', line 166

def ensure_destroy_hook(wrapper, pointer)
  address = pointer.address
  return if destroy_hook_addresses[address]

  destroy_hook_addresses[address] = true
  Qt::EventRuntime.on_internal_signal(pointer, 'destroyed()') do |_payload|
    destroy_hook_addresses.delete(address)
    Qt::EventRuntime.clear_signal_registrations_for_address(address)
    invalidate_cached_wrapper(address, wrapper)
  end
rescue StandardError
  destroy_hook_addresses.delete(address)
  raise
end

.fallback_wrapper_class(expected_qt_class) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/qt/object_wrapper.rb', line 63

def fallback_wrapper_class(expected_qt_class)
  normalized_qt_class = normalize_expected_qt_class(expected_qt_class)
  return nil unless normalized_qt_class
  return nil unless Qt.const_defined?(normalized_qt_class, false)

  klass = Qt.const_get(normalized_qt_class, false)
  return nil unless klass.is_a?(Class)
  return nil unless klass.const_defined?(:QT_CLASS, false)

  klass
rescue NameError
  nil
end

.inheritance_depth(klass) ⇒ Object



181
182
183
184
185
186
187
188
189
# File 'lib/qt/object_wrapper.rb', line 181

def inheritance_depth(klass)
  depth = 0
  current = klass
  while current.is_a?(Class)
    depth += 1
    current = current.superclass
  end
  depth
end

.install_constructor_cache_hooks!Object



122
123
124
125
126
127
128
129
# File 'lib/qt/object_wrapper.rb', line 122

def install_constructor_cache_hooks!
  qobject_wrapper_classes.each do |klass|
    next if klass.instance_variable_defined?(:@__qt_object_wrapper_constructor_hook_installed)

    klass.prepend(ConstructorCacheHook)
    klass.instance_variable_set(:@__qt_object_wrapper_constructor_hook_installed, true)
  end
end

.instantiate_wrapper(klass, pointer) ⇒ Object



77
78
79
80
81
# File 'lib/qt/object_wrapper.rb', line 77

def instantiate_wrapper(klass, pointer)
  wrapped = klass.allocate
  wrapped.instance_variable_set(:@handle, pointer)
  wrapped
end

.invalidate_cached_wrapper(pointer_or_address, expected_wrapper = nil) ⇒ Object



107
108
109
110
111
112
113
114
# File 'lib/qt/object_wrapper.rb', line 107

def invalidate_cached_wrapper(pointer_or_address, expected_wrapper = nil)
  address = pointer_or_address.is_a?(Integer) ? pointer_or_address : pointer_or_address.address
  cached = wrapper_cache[address]
  return unless cached
  return if expected_wrapper && !cached.equal?(expected_wrapper)

  wrapper_cache.delete(address)
end

.normalize_expected_qt_class(expected_qt_class) ⇒ Object



195
196
197
198
199
200
201
202
203
204
205
# File 'lib/qt/object_wrapper.rb', line 195

def normalize_expected_qt_class(expected_qt_class)
  return nil if expected_qt_class.nil?

  value = expected_qt_class.to_s.strip
  return nil if value.empty?

  value = value.delete_prefix('Qt::')
  return nil unless value.match?(/\A[A-Z]\w*\z/)

  value
end

.null_pointer?(pointer) ⇒ Boolean

Returns:

  • (Boolean)


31
32
33
# File 'lib/qt/object_wrapper.rb', line 31

def null_pointer?(pointer)
  pointer.nil? || (pointer.respond_to?(:null?) && pointer.null?)
end

.object_wrapper_debug_enabled?Boolean

Returns:

  • (Boolean)


139
140
141
# File 'lib/qt/object_wrapper.rb', line 139

def object_wrapper_debug_enabled?
  ENV['QT_RUBY_OBJECT_WRAPPER_DEBUG'] == '1'
end

.qobject_wrapper?(klass) ⇒ Boolean

Returns:

  • (Boolean)


191
192
193
# File 'lib/qt/object_wrapper.rb', line 191

def qobject_wrapper?(klass)
  klass.is_a?(Class) && klass <= Qt::QObject
end

.qobject_wrapper_classesObject



52
53
54
55
56
57
58
59
60
61
# File 'lib/qt/object_wrapper.rb', line 52

def qobject_wrapper_classes
  Qt.constants(false).filter_map do |const_name|
    klass = Qt.const_get(const_name, false)
    next unless klass.is_a?(Class)
    next unless klass.const_defined?(:QT_CLASS, false)
    next unless klass <= Qt::QObject

    klass
  end
end

.register_wrapper(wrapper) ⇒ Object



87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/qt/object_wrapper.rb', line 87

def register_wrapper(wrapper)
  return wrapper unless wrapper.respond_to?(:handle)

  pointer = wrapper.handle
  return wrapper if null_pointer?(pointer)
  return wrapper unless qobject_wrapper?(wrapper.class)

  cached = cached_wrapper_for(pointer)
  return cached if cached

  cache_wrapper(wrapper)
end

.reset_cache!Object



116
117
118
119
120
# File 'lib/qt/object_wrapper.rb', line 116

def reset_cache!
  @wrapper_cache = {}
  @destroy_hook_addresses = {}
  @unwrapped_pointer_warning_keys = {}
end

.resolve_wrapper_class(pointer, expected_qt_class) ⇒ Object



35
36
37
38
39
# File 'lib/qt/object_wrapper.rb', line 35

def resolve_wrapper_class(pointer, expected_qt_class)
  candidate_wrapper_classes(expected_qt_class).find do |klass|
    Native.qobject_inherits(pointer, klass::QT_CLASS)
  end
end

.unwrapped_pointer_warning_keysObject



162
163
164
# File 'lib/qt/object_wrapper.rb', line 162

def unwrapped_pointer_warning_keys
  @unwrapped_pointer_warning_keys ||= {}
end

.warn_unwrapped_pointer(pointer, expected_qt_class) ⇒ Object



143
144
145
146
147
148
149
150
151
152
# File 'lib/qt/object_wrapper.rb', line 143

def warn_unwrapped_pointer(pointer, expected_qt_class)
  return unless object_wrapper_debug_enabled?

  label = warning_qt_class_label(expected_qt_class)
  return if unwrapped_pointer_warning_keys[label]

  unwrapped_pointer_warning_keys[label] = true
  warn "[qt-ruby-wrapper] returning raw pointer for #{label}; no generated wrapper class matched " \
       "address=0x#{pointer.address.to_s(16)}"
end

.warning_qt_class_label(expected_qt_class) ⇒ Object



154
155
156
157
158
159
160
# File 'lib/qt/object_wrapper.rb', line 154

def warning_qt_class_label(expected_qt_class)
  normalized = normalize_expected_qt_class(expected_qt_class)
  return normalized if normalized

  raw = expected_qt_class.to_s.strip
  raw.empty? ? '(unknown Qt class)' : raw
end

.wrap(pointer, expected_qt_class = nil) ⇒ Object



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/qt/object_wrapper.rb', line 15

def wrap(pointer, expected_qt_class = nil)
  return nil if null_pointer?(pointer)
  return pointer if pointer.respond_to?(:handle)

  cached = cached_wrapper_for(pointer)
  return cached if cached

  klass = resolve_wrapper_class(pointer, expected_qt_class) || fallback_wrapper_class(expected_qt_class)
  unless klass
    warn_unwrapped_pointer(pointer, expected_qt_class)
    return pointer
  end

  register_wrapper(instantiate_wrapper(klass, pointer))
end

.wrapper_cacheObject



131
132
133
# File 'lib/qt/object_wrapper.rb', line 131

def wrapper_cache
  @wrapper_cache ||= {}
end