Module: Qt::EventRuntime

Defined in:
lib/qt/event_runtime.rb,
lib/qt/event_runtime_qobject_methods.rb

Overview

Event/signal subscription runtime backed by generated native callbacks.

Defined Under Namespace

Modules: QObjectMethods

Constant Summary collapse

WidgetMethods =

Backward-compatible alias for already-generated code.

QObjectMethods

Class Method Summary collapse

Class Method Details

.acquire_signal_registration(handle, signal_key) ⇒ Object



160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/qt/event_runtime.rb', line 160

def acquire_signal_registration(handle, signal_key)
  @signal_registrations ||= {}
  per_widget = (@signal_registrations[handle.address] ||= {})
  registration = (per_widget[signal_key] ||= { index: nil, refcount: 0 })
  if registration[:index].nil?
    index = Qt::Native.qobject_connect_signal(handle, signal_key)
    raise ArgumentError, "failed to connect signal #{signal_key.inspect} (code=#{index})" if index.negative?

    registration[:index] = index
  end
  registration[:refcount] += 1
  registration[:index]
end

.clear_signal_registrations_for_address(address) ⇒ Object



191
192
193
194
195
# File 'lib/qt/event_runtime.rb', line 191

def clear_signal_registrations_for_address(address)
  @signal_handlers&.delete(address)
  @internal_signal_handlers&.delete(address)
  @signal_registrations&.delete(address)
end

.decode_event_payload(event_type, payload_json) ⇒ Object



133
134
135
136
137
138
139
140
141
142
# File 'lib/qt/event_runtime.rb', line 133

def decode_event_payload(event_type, payload_json)
  payload =
    if payload_json.nil? || payload_json.empty?
      {}
    else
      JSON.parse(Qt::StringCodec.from_qt_text(payload_json), symbolize_names: true)
    end
  payload[:type] ||= event_type
  payload
end

.ensure_event_callback!Object



101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/qt/event_runtime.rb', line 101

def ensure_event_callback!
  return if @event_callback

  @event_callback = FFI::Function.new(
    :int, %i[pointer int string]
  ) do |object_handle, event_type, payload_json|
    payload = decode_event_payload(event_type, payload_json)
    EventRuntimeDispatch.dispatch_event(@event_handlers, object_handle, event_type, payload)
  end

  Qt::Native.set_event_callback(@event_callback)
end

.ensure_native_bridge_ready!Object



144
145
146
147
# File 'lib/qt/event_runtime.rb', line 144

def ensure_native_bridge_ready!
  Qt::Native.ensure_loaded!
  Qt::Native.define_bridge_wrappers!
end

.ensure_signal_callback!Object



114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/qt/event_runtime.rb', line 114

def ensure_signal_callback!
  return if @signal_callback

  @signal_callback = FFI::Function.new(:void, %i[pointer int string]) do |object_handle, signal_index, payload|
    normalized_payload = payload.nil? ? nil : Qt::StringCodec.from_qt_text(payload)
    EventRuntimeDispatch.dispatch_signal(
      @internal_signal_handlers, @signal_handlers, object_handle, signal_index, normalized_payload
    )
  end

  Qt::Native.set_signal_callback(@signal_callback)
end

.event_type_for(event_name) ⇒ Object

Raises:

  • (ArgumentError)


93
94
95
96
97
98
99
# File 'lib/qt/event_runtime.rb', line 93

def event_type_for(event_name)
  key = event_name.to_sym
  event_type = Qt::GENERATED_EVENT_TYPES[key]
  raise ArgumentError, "unknown event: #{event_name.inspect}" unless event_type

  event_type
end

.off_event(widget, event_name = nil) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
# File 'lib/qt/event_runtime.rb', line 81

def off_event(widget, event_name = nil)
  ensure_native_bridge_ready!
  handle = widget_handle(widget)
  return false if handle.nil? || @event_handlers.nil?

  per_widget = @event_handlers[handle.address]
  return false unless per_widget

  off_event_for_widget(handle, per_widget, event_name)
  true
end

.off_event_for_widget(handle, per_widget, event_name) ⇒ Object



149
150
151
152
153
154
155
156
157
158
# File 'lib/qt/event_runtime.rb', line 149

def off_event_for_widget(handle, per_widget, event_name)
  if event_name
    event_type = event_type_for(event_name)
    per_widget.delete(event_type)
    Qt::Native.unwatch_qobject_event(handle, event_type)
  else
    per_widget.each_key { |event_type| Qt::Native.unwatch_qobject_event(handle, event_type) }
    @event_handlers.delete(handle.address)
  end
end

.off_signal(widget, signal_name = nil) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/qt/event_runtime.rb', line 61

def off_signal(widget, signal_name = nil)
  ensure_native_bridge_ready!
  handle = widget_handle(widget)
  return false if handle.nil? || @signal_handlers.nil?

  per_widget = @signal_handlers[handle.address]
  return false if per_widget.nil?

  signal_key = signal_name&.to_s
  if signal_key
    per_widget.delete(signal_key)
    release_signal_registration(handle, signal_key)
  else
    per_widget.keys.each { |registered_signal| release_signal_registration(handle, registered_signal) }
    per_widget.clear
  end
  @signal_handlers.delete(handle.address) if per_widget.empty?
  true
end

.on_event(widget, event_name, &block) ⇒ Object

Raises:

  • (ArgumentError)


10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/qt/event_runtime.rb', line 10

def on_event(widget, event_name, &block)
  raise ArgumentError, 'pass block to on_event' unless block

  ensure_native_bridge_ready!
  ensure_event_callback!

  event_type = event_type_for(event_name)
  handle = widget_handle(widget) || raise(ArgumentError, 'widget handle is required')

  @event_handlers ||= {}
  ((@event_handlers[handle.address] ||= {})[event_type] ||= []) << block

  Qt::Native.watch_qobject_event(handle, event_type)
  true
end

.on_internal_signal(widget, signal_name, &block) ⇒ Object

Raises:

  • (ArgumentError)


38
39
40
41
42
43
44
45
46
47
48
# File 'lib/qt/event_runtime.rb', line 38

def on_internal_signal(widget, signal_name, &block)
  raise ArgumentError, 'pass block to on_internal_signal' unless block

  ensure_native_bridge_ready!
  ensure_signal_callback!

  handle = widget_handle(widget) || raise(ArgumentError, 'widget handle is required')
  per_signal = prepare_signal_registration(@internal_signal_handlers ||= {}, handle, signal_name)
  per_signal[:blocks] << block
  true
end

.on_signal(widget, signal_name, &block) ⇒ Object

Raises:

  • (ArgumentError)


26
27
28
29
30
31
32
33
34
35
36
# File 'lib/qt/event_runtime.rb', line 26

def on_signal(widget, signal_name, &block)
  raise ArgumentError, 'pass block to on_signal' unless block

  ensure_native_bridge_ready!
  ensure_signal_callback!

  handle = widget_handle(widget) || raise(ArgumentError, 'widget handle is required')
  per_signal = prepare_signal_registration(@signal_handlers ||= {}, handle, signal_name)
  per_signal[:blocks] << block
  true
end

.prepare_signal_registration(signal_store, handle, signal_name) ⇒ Object

Raises:

  • (ArgumentError)


50
51
52
53
54
55
56
57
58
59
# File 'lib/qt/event_runtime.rb', line 50

def prepare_signal_registration(signal_store, handle, signal_name)
  signal_key = signal_name.to_s
  raise ArgumentError, 'signal name is required' if signal_key.empty?

  per_signal = ((signal_store[handle.address] ||= {})[signal_key] ||= { index: nil, blocks: [] })
  if per_signal[:index].nil?
    per_signal[:index] = acquire_signal_registration(handle, signal_key)
  end
  per_signal
end

.release_signal_registration(handle, signal_key) ⇒ Object



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/qt/event_runtime.rb', line 174

def release_signal_registration(handle, signal_key)
  return if @signal_registrations.nil?

  per_widget = @signal_registrations[handle.address]
  return if per_widget.nil?

  registration = per_widget[signal_key]
  return if registration.nil?

  registration[:refcount] -= 1
  return if registration[:refcount].positive?

  Qt::Native.qobject_disconnect_signal(handle, signal_key)
  per_widget.delete(signal_key)
  @signal_registrations.delete(handle.address) if per_widget.empty?
end

.widget_handle(widget) ⇒ Object



127
128
129
130
131
# File 'lib/qt/event_runtime.rb', line 127

def widget_handle(widget)
  return nil if widget.nil?

  widget.respond_to?(:handle) ? widget.handle : widget
end