Skip to content
This repository
Browse code

Put Notification class/module stuff directly in AX::Element

The abstraction of having it in its own module seems strange, this is easier, though
it does the make the element.rb file that much larger… :/
  • Loading branch information...
commit fe98f435d389af138592f30a1aaabf90a49cc1c4 1 parent 71d7c34
Mark Rada authored February 18, 2012
78  lib/accessibility/notification.rb
... ...
@@ -1,78 +0,0 @@
1  
-##
2  
-# A convenient wrapper around {Accessibility::Core} notification methods.
3  
-#
4  
-# It is expected that you will mix this in to a class that also has had
5  
-# {Accessibility::Core} or equivalent methods implemented.
6  
-module Accessibility::Notification
7  
-
8  
-  ##
9  
-  # Register to receive notification of the given event being completed
10  
-  # by the given element.
11  
-  #
12  
-  # {file:docs/Notifications.markdown Notifications} are a way to put
13  
-  # non-polling delays into your scripts.
14  
-  #
15  
-  # Use this method to register to be notified of the specified event in
16  
-  # an application. You must also pass a block to this method to validate
17  
-  # the notification.
18  
-  #
19  
-  # @example
20  
-  #
21  
-  #   register_to_receive(KAXWindowCreatedNotification, from: safari) { |notif, element|
22  
-  #     puts "#{element.description} sent #{notif.inspect}"
23  
-  #     true
24  
-  #   }
25  
-  #
26  
-  # @param [String] notif the name of the notification
27  
-  # @param [AXUIElementRef] element the element which will send the notification
28  
-  # @yield Validate the notification; the block should return truthy if
29  
-  #        the notification received is the expected one and the script can stop
30  
-  #        waiting, otherwise should return falsy.
31  
-  # @yieldparam [String] notif the name of the notification
32  
-  # @yieldparam [AXUIElementRef] element the element that sent the notification
33  
-  # @yieldreturn [Boolean] determines if the script should continue or wait
34  
-  # @return [Array(Observer, String, AXUIElementRef)] the registration triple
35  
-  def register notif, &block
36  
-    callback = create_callback_for block
37  
-    observer = observer_for @ref, &callback
38  
-    source   = run_loop_source_for observer
39  
-    register observer, to_receive: notif, for: @ref
40  
-    @notifs[notif] = [observer, source]
41  
-  end
42  
-
43  
-  # @todo What are the implications of not removing the run loop source?
44  
-  #       Taking it out would clobber other notifications that are using
45  
-  #       the same source, so we would have to check if we can remove it.
46  
-  #
47  
-  def unregister notif
48  
-    unless @notifs.has_key? notif
49  
-      raise ArgumentError, "You have no registrations for #{notif}"
50  
-    end
51  
-    observer, source = @notifs.delete notif
52  
-    unregister observer, from_receiving: notif, for: @ref
53  
-  end
54  
-
55  
-  ##
56  
-  # Cancel _all_ notification registrations. Simple and clean, but a
57  
-  # blunt tool at best. This will have to do for the time being...
58  
-  #
59  
-  # @return [nil]
60  
-  def unregister_all
61  
-    @notifs.keys.each do |notif|
62  
-      unregister notif
63  
-    end
64  
-  end
65  
-
66  
-
67  
-  private
68  
-
69  
-  def create_callback_for proc
70  
-    # we are ignoring the context pointer since this is OO
71  
-    Proc.new do |observer, sender, received_notif, _|
72  
-      break unless proc.call(received_notif, sender)
73  
-      unregister received_notif
74  
-      CFRunLoopStop(run_loop)
75  
-    end
76  
-  end
77  
-
78  
-end
82  lib/ax/element.rb
@@ -315,9 +315,19 @@ def method_missing method, *args
315 315
 
316 316
   # @group Notifications
317 317
 
  318
+  def notifs
  319
+    @notifs ||= {}
  320
+  end
  321
+
318 322
   ##
319  
-  # Register a callback block to run when a notification is sent by
320  
-  # the object.
  323
+  # Register to receive notification of the given event being completed
  324
+  # by the given element.
  325
+  #
  326
+  # {file:docs/Notifications.markdown Notifications} are a way to put
  327
+  # non-polling delays into your scripts.
  328
+  #
  329
+  # Use this method to register to be notified of the specified event in
  330
+  # an application.
321 331
   #
322 332
   # The block is optional. The block will be given the sender of the
323 333
   # notification, which will almost always be `self`, and also the name
@@ -325,21 +335,48 @@ def method_missing method, *args
325 335
   # boolean value that decides if the notification received is the
326 336
   # expected one.
327 337
   #
328  
-  # Read the {file:docs/Notifications.markdown Notifications tutorial}
329  
-  # for more information.
  338
+  # @example
330 339
   #
331  
-  # @param [#to_s]
332  
-  # @yieldparam [String]
333  
-  # @yieldparam [AX::Element]
  340
+  #   on_notification(:window_created) { |sender|
  341
+  #     puts "#{sender.inspect} sent the ':window_created' notification"
  342
+  #     true
  343
+  #   }
  344
+  #
  345
+  # @param [#to_s] notif the name of the notification
  346
+  # @yield Validate the notification; the block should return truthy if
  347
+  #        the notification received is the expected one and the script can
  348
+  #        stop waiting, otherwise should return falsy.
  349
+  # @yieldparam [String] notif the name of the notification
  350
+  # @yieldparam [AXUIElementRef] element the element that sent the notification
334 351
   # @yieldreturn [Boolean]
335  
-  # @return [Array(String,self)] the notification/element pair
  352
+  # @return [Array(Observer, String, CFRunLoopSource)]
336 353
   def on_notification name, &block
337  
-    notif = TRANSLATOR.guess_notification_for name
338  
-    register_to_receive notif, from: @ref do |notification, sender|
339  
-      element = process sender
340  
-      block ? block.call(notification, element) : true
  354
+    notif    = TRANSLATOR.guess_notification_for name
  355
+    observer = observer_for self.pid, &notif_callback_for(&block)
  356
+    source   = run_loop_source_for observer
  357
+    CFRunLoopAddSource(CFRunLoopGetCurrent(), source, KCFRunLoopDefaultMode)
  358
+    register observer, to_receive: notif, from: @ref
  359
+    notifs[name] = [observer, notif, source]
  360
+  end
  361
+
  362
+  def unregister_notification name
  363
+    unless notifs.has_key? name
  364
+      raise ArgumentError, "You have no registrations for #{name}"
341 365
     end
342  
-    [notif, self]
  366
+    _unregister_notification *notifs.delete(name)[0..1]
  367
+  end
  368
+
  369
+  ##
  370
+  # Cancel _all_ notification registrations for this object. Simple and
  371
+  # clean, but a blunt tool at best. This will have to do for the time
  372
+  # being...
  373
+  #
  374
+  # @return [nil]
  375
+  def unregister_all
  376
+    notifs.keys.each do |notif|
  377
+      unregister_notification notif
  378
+    end
  379
+    nil
343 380
   end
344 381
 
345 382
   # @endgroup
@@ -439,4 +476,23 @@ def lookup key, with: values
439 476
     return value if values.include? value
440 477
   end
441 478
 
  479
+  def notif_callback_for &block
  480
+    # we are ignoring the context pointer since this is OO
  481
+    Proc.new do |observer, sender, notif, _|
  482
+      break unless block.call(process sender) if block
  483
+      _unregister_notification observer, notif
  484
+      CFRunLoopStop(CFRunLoopGetCurrent())
  485
+    end
  486
+  end
  487
+
  488
+  ##
  489
+  # @todo What are the implications of not removing the run loop source?
  490
+  #       Taking it out would clobber other notifications that are using
  491
+  #       the same source, so we would have to check if we can remove it.
  492
+  #
  493
+  def _unregister_notification observer, notif
  494
+    # @todo remove run loop source?
  495
+    unregister observer, from_receiving: notif, from: @ref
  496
+  end
  497
+
442 498
 end

0 notes on commit fe98f43

Please sign in to comment.
Something went wrong with that request. Please try again.