Module: Ruby2D::Interactive
- Included in:
- Button, Renderable
- Defined in:
- lib/ruby2d/interactive.rb
Overview
Per-object event handling for objects that participate in Window's
interactive registry. Provides on / off / interactive? and the
_fire_event dispatch entry point. Mixed into Renderable and Button.
Including objects must expose x, y, z, width, height, and
contains?(x, y) so Window::ObjectEventDispatch can hit-test them.
Constant Summary collapse
- OBJECT_EVENT_FILTER_PREDICATES =
Per-object events that take a value matcher (button) in the kwarg form.
:hover,:hover_out, and:mouse_scrollcarry no matchable field. { mouse_down: :button?, mouse_held: :button?, mouse_up: :button?, click: :button?, drag: :button? }.freeze
- OBJECT_EVENTS =
The full per-object event vocabulary: the filterable events above plus the three that carry no matchable field. Derived from the predicates map so the two can't drift, and mirrors what Window::ObjectEventDispatch fires.
(OBJECT_EVENT_FILTER_PREDICATES.keys + %i[hover hover_out mouse_scroll]).freeze
Instance Method Summary collapse
-
#_fire_event(type, event) ⇒ Object
Dispatch an event to stored handlers (called by Window).
-
#interactive?(event = nil) ⇒ Boolean
Check if this object has any event handlers.
-
#off(descriptor) ⇒ Object
Remove a per-object event handler (or several, given an array).
-
#on(event = nil, **filters, &proc) ⇒ Object
Register a per-object event handler.
Instance Method Details
#_fire_event(type, event) ⇒ Object
Dispatch an event to stored handlers (called by Window)
80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/ruby2d/interactive.rb', line 80 def _fire_event(type, event) return unless @_object_events handlers = @_object_events[type] return unless handlers # Snapshot the values: a handler may register another handler for the same # event type mid-dispatch, which would otherwise mutate the hash we're # iterating ("can't add a new key into hash during iteration"). handlers.values.each { |proc| proc.call(event) } end |
#interactive?(event = nil) ⇒ Boolean
Check if this object has any event handlers
69 70 71 72 73 74 75 76 77 |
# File 'lib/ruby2d/interactive.rb', line 69 def interactive?(event = nil) return false unless @_object_events if event @_object_events.key?(event) && !@_object_events[event].empty? else @_object_events.any? { |_type, handlers| !handlers.empty? } end end |
#off(descriptor) ⇒ Object
Remove a per-object event handler (or several, given an array).
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/ruby2d/interactive.rb', line 49 def off(descriptor) return descriptor.each { |d| off(d) } if descriptor.is_a?(Array) unless descriptor.is_a?(Window::ObjectEventDescriptor) raise Error, "Cannot remove event handler: expected a descriptor returned by `on`, got #{descriptor.inspect}" end return unless @_object_events handlers = @_object_events[descriptor.type] return unless handlers handlers.delete(descriptor.id) @_object_events.delete(descriptor.type) if handlers.empty? Window.unregister_interactive(self) unless interactive? end |
#on(event = nil, **filters, &proc) ⇒ Object
Register a per-object event handler. Two forms:
obj.on(:click) { |event| ... }
obj.on(click: :left) { ... } # filtered
obj.on(click: [:left, :right]) { ... } # array → match any
obj.on(mouse_down: :left, click: :left) { ... } # multi-event
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/ruby2d/interactive.rb', line 29 def on(event = nil, **filters, &proc) raise Error, '`on` requires a block' unless proc if event.is_a?(Symbol) && filters.empty? raise Error, "`#{event}` is not a valid object event" unless OBJECT_EVENTS.include?(event) register_object_event_handler(event, proc) elsif event.nil? && !filters.empty? descriptors = filters.map do |type, matcher| predicate = OBJECT_EVENT_FILTER_PREDICATES[type] or raise Error, "`#{type}` does not support filtering with `on event: value`" values = Array(matcher) wrapped = ->(e) { proc.call(e) if values.any? { |v| e.send(predicate, v) } } register_object_event_handler(type, wrapped) end descriptors.size == 1 ? descriptors.first : descriptors else raise Error, '`on` requires either an event symbol or event filters' end end |