Module: GlimR::EventListener
- Included in:
- SceneObject
- Defined in:
- lib/glimr/eventlistener.rb
Overview
The EventListener mixin implements an event listening interface similar to <a href=“www.w3.org/TR/DOM-Level-2-Events/”> W3C’s ECMAScript DOM event API</a>, with the addition of event multicasting.
EventListener provides the following methods: #add_event_listener, #remove_event_listener, #dispatch_event and #multicast_event.
The list of event listeners can be modified with #add_event_listener and #remove_event_listener.
E.g.
l = event_listener.add_event_listener(:hello){|listener, event|
puts "Goodbye!"
}
event_listener.remove_event_listener(:hello, l)
event_listener.add_event_listener(:hello){|listener, event|
puts "Hello, World!"
}
another_hello_listener.add_event_listener(:hello){|l, e|
puts "Hello from me too!"
}
event_listener.attach another_hello_listener
Also provides a method_missing that captures method calls starting with on_ and parses them as requests to add event listener for the event type.
E.g. to add a bubbling event listener for the event type :frame, it’s possible to do
obj.on_frame{|o,e| puts 'got frame event'}
And for a capturing listener
obj.on_frame(true){|o,e| puts 'got it first!'}
To send a targeted event to the EventListener, use #dispatch_event.
E.g.
event_listener.dispatch_event(Event.new(:hello))
# Hello, World!
To broadcast an event to all listeners for the event type in the scene graph, use #multicast_event.
E.g.
event_listener.multicast_event(Event.new(:hello))
# Hello from me too!
# Hello, World!
Instance Attribute Summary collapse
-
#event_listeners ⇒ Object
Returns the value of attribute event_listeners.
-
#listener_count ⇒ Object
readonly
Returns the value of attribute listener_count.
Instance Method Summary collapse
-
#add_event_listener(type, listener = nil, use_capture = false, &block) ⇒ Object
Adds an event listener for the given event type.
-
#decrement_listener_count(type, count = 1) ⇒ Object
Subtracts count from the subtree total listener count for type.
-
#dispatch_event(evt) ⇒ Object
Sends the Event evt down to self and bubbles it up.
- #event_root ⇒ Object
-
#increment_listener_count(type, count = 1) ⇒ Object
Adds count to the subtree total listener count for type.
- #initialize(*a, &b) ⇒ Object
- #method_missing(mn, *a, &b) ⇒ Object
-
#multicast_event(evt) ⇒ Object
Sends the Event evt to first the capture listeners, then down to children, then to bubbling listeners.
-
#process_event(evt) ⇒ Object
Processes the Event evt by calling all listeners on this object registered to handle the event.
-
#remove_event_listener(type, listener = nil, use_capture = false) ⇒ Object
Removes the given event listener from the event type.
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(mn, *a, &b) ⇒ Object
101 102 103 104 105 106 107 |
# File 'lib/glimr/eventlistener.rb', line 101 def method_missing(mn, *a, &b) if mn.to_s[0,3] == "on_" add_event_listener(mn.to_s[3..-1].gsub(/=$/,''), *a, &b) else super end end |
Instance Attribute Details
#event_listeners ⇒ Object
Returns the value of attribute event_listeners.
65 66 67 |
# File 'lib/glimr/eventlistener.rb', line 65 def event_listeners @event_listeners end |
#listener_count ⇒ Object (readonly)
Returns the value of attribute listener_count.
66 67 68 |
# File 'lib/glimr/eventlistener.rb', line 66 def listener_count @listener_count end |
Instance Method Details
#add_event_listener(type, listener = nil, use_capture = false, &block) ⇒ Object
Adds an event listener for the given event type. If use_capture is true, listens on capture phase. Otherwise listens on bubble phase. If listener is nil or false, the given block is used as the listener instead.
76 77 78 79 80 81 |
# File 'lib/glimr/eventlistener.rb', line 76 def add_event_listener(type, listener=nil, use_capture=false, &block) listener ||= block type = type.to_sym @event_listeners[type][(use_capture ? :capture : :bubble)] << listener increment_listener_count type end |
#decrement_listener_count(type, count = 1) ⇒ Object
Subtracts count from the subtree total listener count for type. Sends changes to parent to keep its subtree total listener count correct.
189 190 191 |
# File 'lib/glimr/eventlistener.rb', line 189 def decrement_listener_count type, count=1 increment_listener_count type, -count end |
#dispatch_event(evt) ⇒ Object
Sends the Event evt down to self and bubbles it up.
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/glimr/eventlistener.rb', line 133 def dispatch_event(evt) evt.target = self ancestors = [self] o = self while o.parent and !o.parent.event_root and o.parent != evt.sender ancestors << o.parent o = o.parent end i = ancestors.size ancestors.reverse.each{|a| a.process_event(evt) break if evt.stopped i -= 1 break if evt.phase == :bubble } # Now either the event was turned around at i or reached the bottom (i==1). # Continue by setting evt.phase to :bubble and sending it up from i. i += 1 if evt.target == self # bubble already called for self evt.phase = :bubble (ancestors[i..-1] || []).each{|a| break if evt.stopped a.process_event(evt) } return !evt.cancelled end |
#event_root ⇒ Object
159 160 161 |
# File 'lib/glimr/eventlistener.rb', line 159 def event_root false end |
#increment_listener_count(type, count = 1) ⇒ Object
Adds count to the subtree total listener count for type. Sends changes to parent to keep its subtree total listener count correct.
180 181 182 183 184 185 |
# File 'lib/glimr/eventlistener.rb', line 180 def increment_listener_count type, count=1 c = @listener_count[type] raise ArgumentError, "Trying to decrement listener count too much." if -count > c @listener_count[type] += count parent.increment_listener_count type, count if parent.is_a? EventListener end |
#initialize(*a, &b) ⇒ Object
68 69 70 71 |
# File 'lib/glimr/eventlistener.rb', line 68 def initialize(*a,&b) @event_listeners = Hash.new{|h,k| h[k] = Hash.new{|h,k| h[k] = []} } @listener_count = Hash.new{|h,k| h[k] = 0 } end |
#multicast_event(evt) ⇒ Object
Sends the Event evt to first the capture listeners, then down to children, then to bubbling listeners.
Returns immediately if there are no listeners for the event type in or below this node.
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/glimr/eventlistener.rb', line 115 def multicast_event(evt) return !evt.cancelled if @listener_count[evt.type] <= 0 evt.phase = :capture process_event(evt) return !evt.cancelled if evt.stopped if evt.phase == :capture # still going down children.each{|c| c.multicast_event(evt) if c.is_a? EventListener return !evt.cancelled if evt.stopped } end return !evt.cancelled if evt.stopped evt.phase = :bubble process_event(evt) return !evt.cancelled end |
#process_event(evt) ⇒ Object
Processes the Event evt by calling all listeners on this object registered to handle the event.
165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/glimr/eventlistener.rb', line 165 def process_event(evt) listeners_for(evt).each{|l| break if evt.stopped case l when Symbol __send__(l, self, evt) else l.call(self, evt) end } !evt.cancelled end |
#remove_event_listener(type, listener = nil, use_capture = false) ⇒ Object
Removes the given event listener from the event type. If no listener is given, removes all event listeners from the event type. If use_capture is true, removes capturing event listener(s), if false, removes bubbling event listener(s).
86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/glimr/eventlistener.rb', line 86 def remove_event_listener(type, listener=nil, use_capture=false) type = type.to_sym if listener success = @event_listeners[type][(use_capture ? :capture : :bubble)].delete_first(listener) decrement_listener_count type if success else listeners = @event_listeners[type][(use_capture ? :capture : :bubble)] sz = listeners.size if sz > 0 listeners.clear decrement_listener_count type, sz end end end |