AXElements 0.8.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. data/.yardopts +4 -0
  2. data/History.markdown +41 -0
  3. data/README.markdown +59 -62
  4. data/Rakefile +1 -1
  5. data/ext/accessibility/key_coder/extconf.rb +1 -1
  6. data/ext/accessibility/key_coder/key_coder.c +8 -5
  7. data/lib/accessibility/dsl.rb +261 -87
  8. data/lib/accessibility/enumerators.rb +14 -11
  9. data/lib/accessibility/errors.rb +4 -3
  10. data/lib/accessibility/factory.rb +159 -108
  11. data/lib/accessibility/graph.rb +13 -9
  12. data/lib/accessibility/{pp_inspector.rb → pretty_printer.rb} +4 -5
  13. data/lib/accessibility/qualifier.rb +23 -13
  14. data/lib/accessibility/string.rb +4 -4
  15. data/lib/accessibility/system_info.rb +230 -0
  16. data/lib/accessibility/translator.rb +38 -28
  17. data/lib/accessibility/version.rb +24 -2
  18. data/lib/accessibility.rb +25 -8
  19. data/lib/ax/application.rb +207 -77
  20. data/lib/ax/element.rb +62 -65
  21. data/lib/ax/menu.rb +5 -1
  22. data/lib/ax/row.rb +1 -1
  23. data/lib/ax/scroll_area.rb +7 -6
  24. data/lib/ax/systemwide.rb +38 -5
  25. data/lib/ax_elements/active_support_selections.rb +10 -0
  26. data/lib/ax_elements/mri.rb +57 -0
  27. data/lib/ax_elements/nsarray_compat.rb +97 -17
  28. data/lib/ax_elements.rb +9 -1
  29. data/rakelib/gem.rake +11 -11
  30. data/rakelib/test.rake +0 -9
  31. data/test/helper.rb +10 -18
  32. data/test/integration/accessibility/test_dsl.rb +52 -42
  33. data/test/integration/accessibility/test_enumerators.rb +0 -1
  34. data/test/integration/accessibility/test_graph.rb +1 -0
  35. data/test/integration/accessibility/test_qualifier.rb +2 -2
  36. data/test/integration/ax/test_application.rb +9 -2
  37. data/test/integration/ax/test_element.rb +41 -1
  38. data/test/sanity/accessibility/test_factory.rb +23 -56
  39. data/test/sanity/accessibility/{test_pp_inspector.rb → test_pretty_printer.rb} +9 -9
  40. data/test/sanity/accessibility/test_translator.rb +2 -5
  41. data/test/sanity/accessibility/test_version.rb +15 -0
  42. data/test/sanity/ax/test_application.rb +17 -2
  43. data/test/sanity/ax/test_element.rb +2 -2
  44. data/test/sanity/ax_elements/test_nsobject_inspect.rb +4 -2
  45. data/test/sanity/test_ax_elements.rb +1 -0
  46. metadata +69 -39
  47. data/lib/accessibility/core.rb +0 -973
  48. data/lib/accessibility/highlighter.rb +0 -86
  49. data/lib/ax_elements/vendor/inflection_data.rb +0 -66
  50. data/lib/ax_elements/vendor/inflections.rb +0 -172
  51. data/lib/ax_elements/vendor/inflector.rb +0 -306
  52. data/lib/minitest/ax_elements.rb +0 -175
  53. data/lib/mouse.rb +0 -223
  54. data/lib/rspec/expectations/ax_elements.rb +0 -234
  55. data/test/integration/accessibility/test_core.rb +0 -18
  56. data/test/integration/minitest/test_ax_elements.rb +0 -89
  57. data/test/integration/rspec/expectations/test_ax_elements.rb +0 -102
  58. data/test/sanity/accessibility/test_core.rb +0 -561
  59. data/test/sanity/accessibility/test_highlighter.rb +0 -56
  60. data/test/sanity/minitest/test_ax_elements.rb +0 -17
  61. data/test/sanity/rspec/expectations/test_ax_elements.rb +0 -15
  62. data/test/sanity/test_mouse.rb +0 -19
@@ -11,32 +11,135 @@ require 'accessibility/string'
11
11
  class AX::Application < AX::Element
12
12
  include Accessibility::String
13
13
 
14
+ class << self
15
+ ##
16
+ # Asynchronously launch an application with given the bundle identifier
17
+ #
18
+ # @param bundle [String] bundle identifier for the app
19
+ # @return [Boolean]
20
+ def launch bundle
21
+ NSWorkspace.sharedWorkspace.launchAppWithBundleIdentifier bundle,
22
+ options: NSWorkspace::NSWorkspaceLaunchAsync,
23
+ additionalEventParamDescriptor: nil,
24
+ launchIdentifier: nil
25
+ end
26
+
27
+ ##
28
+ # Find and return the dock application
29
+ #
30
+ # @return [AX::Application]
31
+ def dock
32
+ new 'com.apple.dock'
33
+ end
34
+
35
+ ##
36
+ # Find and return the dock application
37
+ #
38
+ # @return [AX::Application]
39
+ def finder
40
+ new 'com.apple.finder'
41
+ end
42
+
43
+ ##
44
+ # Find and return the notification center UI app
45
+ #
46
+ # Obviously, this will only work on OS X 10.8+
47
+ #
48
+ # @return [AX::Application]
49
+ def notification_center
50
+ new 'com.apple.notificationcenterui'
51
+ end
52
+
53
+ ##
54
+ # Find and return the application which is frontmost
55
+ #
56
+ # This is often, but not necessarily, the same as the app that
57
+ # owns the menu bar.
58
+ #
59
+ # @return [AX::Application]
60
+ def frontmost_application
61
+ new NSWorkspace.sharedWorkspace.frontmostApplication
62
+ end
63
+ alias_method :frontmost_app, :frontmost_application
64
+
65
+ ##
66
+ # Find and return the application which owns the menu bar
67
+ #
68
+ # This is often, but not necessarily, the same as the app that
69
+ # is frontmost.
70
+ #
71
+ # @return [AX::Application]
72
+ def menu_bar_owner
73
+ new NSWorkspace.sharedWorkspace.menuBarOwningApplication
74
+ end
75
+ end
76
+
14
77
  ##
15
- # Overridden so that we can more flexibly manipulate input.
78
+ # Standard way of creating a new application object
16
79
  #
17
80
  # You can initialize an application object with either the process
18
81
  # identifier (pid) of the application, the name of the application,
19
82
  # an `NSRunningApplication` instance for the application, or an
20
83
  # accessibility (`AXUIElementRef`) token.
21
84
  #
22
- # @param [Number,String,NSRunningApplication]
85
+ # Given a PID, we try to lookup the application and wrap it.
86
+ #
87
+ # Given an `NSRunningApplication` instance, we simply wrap it.
88
+ #
89
+ # Given a string we do some complicated magic to try and figure out if
90
+ # the string is a bundle identifier or the localized name of the
91
+ # application. Given a bundle identifier we try to launch the app if
92
+ # it is not already running, given a localized name we search the running
93
+ # applications for the app. We wrap what we get back if we get anything
94
+ # back.
95
+ #
96
+ # Note however, given a bundle identifier to launch the application our
97
+ # implementation is a bit of a hack; I've tried to register for
98
+ # notifications, launch synchronously, etc., but there is always a problem
99
+ # with accessibility not being ready right away, so we will poll the app
100
+ # to see when it is ready with a timeout of ~10 seconds.
101
+ #
102
+ # If this method fails to find an app then an exception will be raised.
103
+ #
104
+ # @example
105
+ #
106
+ # AX::Application.new 'com.apple.mail'
107
+ # AX::Application.new 'Mail'
108
+ # AX::Application.new 43567
109
+ #
110
+ # @param arg [Number,String,NSRunningApplication]
23
111
  def initialize arg
24
112
  case arg
25
113
  when Fixnum
26
- super SYSTEMWIDE.application_for arg
114
+ super Accessibility::Element.application_for arg
27
115
  @app = NSRunningApplication.runningApplicationWithProcessIdentifier arg
28
116
  when String
29
- @app =
30
- NSRunningApplication.runningApplicationsWithBundleIdentifier(arg).first ||
31
- (
32
- SYSTEMWIDE.spin_run_loop
33
- NSWorkspace.sharedWorkspace.runningApplications.find { |app|
34
- app.localizedName == arg
35
- }
117
+ until @app
118
+ @app =
119
+ (
120
+ app = NSRunningApplication.runningApplicationsWithBundleIdentifier arg
121
+ app.first
122
+
123
+ ) || (
124
+ spin
125
+ NSWorkspace.sharedWorkspace.runningApplications.find { |app|
126
+ app.localizedName == arg
127
+ }
128
+
129
+ ) || (
130
+ count ||= 0
131
+ if AX::Application.launch arg
132
+ spin 1
133
+ count += 1
134
+ raise "#{arg} failed to launch in time" if count == 10
135
+ else
136
+ raise "#{arg} is not a registered bundle identifier for the system"
137
+ end
36
138
  )
37
- super SYSTEMWIDE.application_for @app.processIdentifier
139
+ end
140
+ super Accessibility::Element.application_for @app.processIdentifier
38
141
  when NSRunningApplication
39
- super SYSTEMWIDE.application_for arg.processIdentifier
142
+ super Accessibility::Element.application_for arg.processIdentifier
40
143
  @app = arg
41
144
  else
42
145
  super arg # assume it is an AXUIElementRef
@@ -47,9 +150,6 @@ class AX::Application < AX::Element
47
150
 
48
151
  # @group Attributes
49
152
 
50
- ##
51
- # Overridden to handle the {Accessibility::DSL#set_focus_to} case.
52
- #
53
153
  # (see AX::Element#attribute)
54
154
  def attribute attr
55
155
  case attr
@@ -59,8 +159,7 @@ class AX::Application < AX::Element
59
159
  end
60
160
  end
61
161
 
62
- ##
63
- # Overridden to handle the {Accessibility::DSL#set_focus_to} case.
162
+ # (see AX::Element#writable?)
64
163
  def writable? attr
65
164
  case attr
66
165
  when :focused?, :focused, :hidden?, :hidden then true
@@ -73,7 +172,7 @@ class AX::Application < AX::Element
73
172
  # to the dynamic `#focused?` method, but might make more sense to use
74
173
  # in some cases.
75
174
  def active?
76
- @ref.spin_run_loop
175
+ spin
77
176
  @app.active?
78
177
  end
79
178
  alias_method :focused, :active?
@@ -82,20 +181,17 @@ class AX::Application < AX::Element
82
181
  ##
83
182
  # Ask the app whether or not it is hidden.
84
183
  def hidden?
85
- @ref.spin_run_loop
184
+ spin
86
185
  @app.hidden?
87
186
  end
88
187
 
89
188
  ##
90
189
  # Ask the app whether or not it is still running.
91
190
  def terminated?
92
- @ref.spin_run_loop
191
+ spin
93
192
  @app.terminated?
94
193
  end
95
194
 
96
- ##
97
- # Overridden to handle the {Accessibility::Language#set_focus} case.
98
- #
99
195
  # (see AX::Element#set)
100
196
  def set attr, value
101
197
  case attr
@@ -108,30 +204,105 @@ class AX::Application < AX::Element
108
204
  end
109
205
  end
110
206
 
207
+ ##
208
+ # Get the bundle identifier for the application.
209
+ #
210
+ # @example
211
+ #
212
+ # safari.bundle_identifier 'com.apple.safari'
213
+ # daylite.bundle_identifier 'com.marketcircle.Daylite'
214
+ #
215
+ # @return [String]
216
+ def bundle_identifier
217
+ @app.bundleIdentifier
218
+ end
219
+
220
+ ##
221
+ # Return the `Info.plist` data for the application. This is a plist
222
+ # file that all bundles in OS X must contain.
223
+ #
224
+ # Many bits of metadata are stored in the plist, check the
225
+ # [reference](https://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPRuntimeConfig/Articles/ConfigFiles.html)
226
+ # for more details.
227
+ #
228
+ # @return [Hash]
229
+ def info_plist
230
+ bundle.infoDictionary
231
+ end
232
+
233
+ ##
234
+ # Get the version string for the application.
235
+ #
236
+ # @example
237
+ #
238
+ # AX::Application.new("Safari").version # => "5.2"
239
+ # AX::Application.new("Terminal").version # => "2.2.2"
240
+ # AX::Application.new("Daylite").version # => "3.15 (build 3664)"
241
+ #
242
+ # @return [String]
243
+ def version
244
+ bundle.objectForInfoDictionaryKey 'CFBundleShortVersionString'
245
+ end
246
+
111
247
 
112
248
  # @group Actions
113
249
 
114
250
  ##
115
- # Overridden to provide extra actions (e.g. `hide`, `terminate`).
251
+ # @note This is often async and may return before the action is completed
116
252
  #
117
- # (see AX::Element#perform)
253
+ # Ask the app to quit
254
+ #
255
+ # @return [Boolean]
256
+ def terminate
257
+ perform :terminate
258
+ end
259
+
260
+ ##
261
+ # @note This is often async and may return before the action is completed
262
+ #
263
+ # Force the app to quit
118
264
  #
119
265
  # @return [Boolean]
266
+ def terminate!
267
+ perform :force_terminate
268
+ end
269
+
270
+ ##
271
+ # @note This is often async and may return before the action is completed
272
+ #
273
+ # Ask the app to hide itself
274
+ #
275
+ # @return [Boolean]
276
+ def hide
277
+ perform :hide
278
+ end
279
+
280
+ ##
281
+ # @note This is often async and may return before the action is completed
282
+ #
283
+ # As the app to unhide itself and bring to front
284
+ #
285
+ # @return [Boolean]
286
+ def unhide
287
+ perform :unhide
288
+ end
289
+
290
+ # (see AX::Element#perform)
120
291
  def perform name
121
292
  case name
122
293
  when :terminate
123
294
  return true if terminated?
124
- @app.terminate; sleep 0.2; terminated?
295
+ @app.terminate; spin 0.25; terminated?
125
296
  when :force_terminate
126
297
  return true if terminated?
127
- @app.forceTerminate; sleep 0.2; terminated?
298
+ @app.forceTerminate; spin 0.25; terminated?
128
299
  when :hide
129
300
  return true if hidden?
130
- @app.hide; sleep 0.2; hidden?
301
+ @app.hide; spin 0.25; hidden?
131
302
  when :unhide
132
303
  return true if active?
133
- @app.activateWithOptions(NSApplicationActivateIgnoringOtherApps)
134
- sleep 0.2; active?
304
+ @app.activateWithOptions(NSRunningApplication::NSApplicationActivateIgnoringOtherApps)
305
+ spin 0.25; active?
135
306
  else
136
307
  super
137
308
  end
@@ -144,6 +315,7 @@ class AX::Application < AX::Element
144
315
  # For details on how to format the string, check out the
145
316
  # [Keyboarding documentation](http://github.com/Marketcircle/AXElements/wiki/Keyboarding).
146
317
  #
318
+ # @param string [String]
147
319
  # @return [Boolean]
148
320
  def type string
149
321
  @ref.post keyboard_events_for string
@@ -161,7 +333,7 @@ class AX::Application < AX::Element
161
333
  # drag_mouse_to point
162
334
  # end
163
335
  #
164
- # @param [String]
336
+ # @param key [String]
165
337
  # @return [Number,nil]
166
338
  def hold_modifier key
167
339
  code = EventGenerator::CUSTOM[key]
@@ -182,6 +354,7 @@ class AX::Application < AX::Element
182
354
  #
183
355
  # safari.select_menu_item 'Edit', 'Find', /Google/
184
356
  #
357
+ # @param path [String,Regexp]
185
358
  # @return [AX::MenuItem]
186
359
  def select_menu_item *path
187
360
  target = navigate_menu *path
@@ -195,6 +368,7 @@ class AX::Application < AX::Element
195
368
  #
196
369
  # You may also be interested in {#select_menu_item}.
197
370
  #
371
+ # @param path [String,Regexp]
198
372
  # @return [AX::MenuItem]
199
373
  def navigate_menu *path
200
374
  perform :unhide # can't navigate menus unless the app is up front
@@ -249,50 +423,10 @@ class AX::Application < AX::Element
249
423
  #
250
424
  # `nil` will be returned if there was nothing at that point.
251
425
  #
252
- # @param [#to_point]
426
+ # @param point [#to_point]
253
427
  # @return [AX::Element,nil]
254
428
  def element_at point
255
- process @ref.element_at point
256
- end
257
-
258
- ##
259
- # Get the bundle identifier for the application.
260
- #
261
- # @example
262
- #
263
- # safari.bundle_identifier 'com.apple.safari'
264
- # daylite.bundle_identifier 'com.marketcircle.Daylite'
265
- #
266
- # @return [String]
267
- def bundle_identifier
268
- @app.bundleIdentifier
269
- end
270
-
271
- ##
272
- # Return the `Info.plist` data for the application. This is a plist
273
- # file that all bundles in OS X must contain.
274
- #
275
- # Many bits of metadata are stored in the plist, check the
276
- # [reference](https://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPRuntimeConfig/Articles/ConfigFiles.html)
277
- # for more details.
278
- #
279
- # @return [Hash]
280
- def info_plist
281
- bundle.infoDictionary
282
- end
283
-
284
- ##
285
- # Get the version string for the application.
286
- #
287
- # @example
288
- #
289
- # AX::Application.new("Safari").version # => "5.2"
290
- # AX::Application.new("Terminal").version # => "2.2.2"
291
- # AX::Application.new("Daylite").version # => "3.15 (build 3664)"
292
- #
293
- # @return [String]
294
- def version
295
- bundle.objectForInfoDictionaryKey 'CFBundleShortVersionString'
429
+ @ref.element_at(point).to_ruby
296
430
  end
297
431
 
298
432
 
@@ -303,8 +437,4 @@ class AX::Application < AX::Element
303
437
  @bundle ||= NSBundle.bundleWithURL @app.bundleURL
304
438
  end
305
439
 
306
- # @private
307
- # @return [AXUIElementRef]
308
- SYSTEMWIDE = AXUIElementCreateSystemWide()
309
-
310
440
  end
data/lib/ax/element.rb CHANGED
@@ -1,28 +1,28 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
+ require 'active_support/core_ext/object/blank'
3
4
  require 'accessibility/core'
4
5
  require 'accessibility/factory'
5
6
  require 'accessibility/translator'
6
7
  require 'accessibility/enumerators'
7
8
  require 'accessibility/qualifier'
8
9
  require 'accessibility/errors'
9
- require 'accessibility/pp_inspector'
10
+ require 'accessibility/pretty_printer'
11
+
10
12
 
11
13
  ##
12
14
  # @abstract
13
15
  #
14
16
  # The abstract base class for all accessibility objects. `AX::Element`
15
- # composes low level `AXUIElementRef` objects into a more Rubyish
17
+ # composes low level {AXUIElementRef} objects into a more Rubyish
16
18
  # interface.
17
19
  #
18
20
  # This abstract base class provides generic functionality that all
19
21
  # accessibility objects require.
20
22
  class AX::Element
21
- include Accessibility::PPInspector
22
- include Accessibility::Factory
23
-
23
+ include Accessibility::PrettyPrinter
24
24
 
25
- # @param [AXUIElementRef]
25
+ # @param ref [AXUIElementRef]
26
26
  def initialize ref
27
27
  @ref = ref
28
28
  end
@@ -44,18 +44,18 @@ class AX::Element
44
44
 
45
45
  ##
46
46
  # Get the value of an attribute. This method will return `nil` if
47
- # the attribute does not have a value, if the element does not have
48
- # the attribute, or if the element is dead. There is an execption
49
- # for the `:children` attribute which is always guaranteed to return
50
- # an array.
47
+ # the attribute does not have a value or if the element is dead. The
48
+ # execption to the rule is that the `:children` attribute will always
49
+ # return an array unless the element does not have the `:children`
50
+ # attribute.
51
51
  #
52
52
  # @example
53
53
  #
54
54
  # element.attribute :position # => #<CGPoint x=123.0 y=456.0>
55
55
  #
56
- # @param [#to_sym]
56
+ # @param attr [#to_sym]
57
57
  def attribute attr
58
- process @ref.attribute TRANSLATOR.cocoaify attr
58
+ @ref.attribute(TRANSLATOR.cocoaify(attr)).to_ruby
59
59
  end
60
60
 
61
61
  ##
@@ -66,21 +66,20 @@ class AX::Element
66
66
  #
67
67
  # @return [String]
68
68
  def description
69
- attribute :description
69
+ attribute(:description).to_ruby
70
70
  end
71
71
 
72
72
  ##
73
- # Fetch the children elements for the current element. If the current
74
- # element does not have children then an empty array will be returned.
73
+ # Fetch the children elements for the current element.
75
74
  #
76
75
  # @return [Array<AX::Element>]
77
76
  def children
78
- attribute :children
77
+ @ref.children.to_ruby
79
78
  end
80
79
 
81
80
  ##
82
81
  # Get a list of elements, starting with the receiver and riding
83
- # the hierarchy up to the top level object (i.e. the {AX::Application}).
82
+ # the hierarchy up to the top level object (i.e. the {AX::Application})
84
83
  #
85
84
  # @example
86
85
  #
@@ -89,15 +88,16 @@ class AX::Element
89
88
  # # => [#<AX::ApplicationDockItem...>, #<AX::List...>, #<AX::Application...>]
90
89
  #
91
90
  # @return [Array<AX::Element>]
92
- def ancestry *elements
93
- elements = [self] if elements.empty?
91
+ def ancestry elements = self
92
+ elements = Array(elements)
94
93
  element = elements.last
95
94
  if element.attributes.include? :parent
96
- ancestry(elements << element.parent)
95
+ ancestry(elements << element.attribute(:parent))
97
96
  else
98
97
  elements
99
98
  end
100
99
  end
100
+ alias_method :lineage, :ancestry
101
101
 
102
102
  ##
103
103
  # Get the process identifier for the application that the element
@@ -123,7 +123,7 @@ class AX::Element
123
123
  # table.size_of :rows # => 111
124
124
  # window.size_of :children # => 16
125
125
  #
126
- # @param [#to_sym]
126
+ # @param attr [#to_sym]
127
127
  # @return [Number]
128
128
  def size_of attr
129
129
  @ref.size_of TRANSLATOR.cocoaify attr
@@ -137,7 +137,7 @@ class AX::Element
137
137
  # element.writable? :size # => true
138
138
  # element.writable? :value # => false
139
139
  #
140
- # @param [#to_sym]
140
+ # @param attr [#to_sym]
141
141
  def writable? attr
142
142
  @ref.writable? TRANSLATOR.cocoaify attr
143
143
  end
@@ -150,7 +150,7 @@ class AX::Element
150
150
  # element.set :value, 'Hello, world!'
151
151
  # element.set :size, [100, 200].to_size
152
152
  #
153
- # @param [#to_sym]
153
+ # @param attr [#to_sym]
154
154
  # @return the value that you were setting is returned
155
155
  def set attr, value
156
156
  unless writable? attr
@@ -184,10 +184,11 @@ class AX::Element
184
184
  #
185
185
  # text_field.parameterized_attribute :string_for_range, 2..8
186
186
  #
187
- # @param [#to_sym]
187
+ # @param attr [#to_sym]
188
+ # @param param [Object]
188
189
  def parameterized_attribute attr, param
189
- param = param.relative_to(@ref.value.size) if value.kind_of? Range
190
- process @ref.parameterized_attribute(TRANSLATOR.cocoaify(attr), param)
190
+ param = param.relative_to(@ref.value.size) if param.kind_of? Range
191
+ @ref.parameterized_attribute(TRANSLATOR.cocoaify(attr), param).to_ruby
191
192
  end
192
193
 
193
194
 
@@ -220,7 +221,7 @@ class AX::Element
220
221
  # button.perform :press # => true
221
222
  # button.perform :make_pie # => false
222
223
  #
223
- # @param [#to_sym]
224
+ # @param action [#to_sym]
224
225
  # @return [Boolean] true if successful
225
226
  def perform action
226
227
  @ref.perform TRANSLATOR.cocoaify action
@@ -234,15 +235,16 @@ class AX::Element
234
235
  # the current element. If you are concerned about the return value of
235
236
  # this method, you can call {#blank?} on the return object.
236
237
  #
237
- # See the [Searching Tutorial](http://github.com/Marketcircle/AXElements/wiki/Searching)
238
+ # See the [Searching wiki](http://github.com/Marketcircle/AXElements/wiki/Searching)
238
239
  # for the details on search semantics.
239
240
  #
240
241
  # @example Find the dock icon for the Finder app
241
242
  #
242
243
  # AX::DOCK.search(:application_dock_item, title:'Finder')
243
244
  #
244
- # @param [#to_s]
245
- # @param [Hash{Symbol=>Object}]
245
+ # @param kind [#to_s]
246
+ # @param filters [Hash{Symbol=>Object}]
247
+ # @yield Optional block used for filtering
246
248
  # @return [AX::Element,nil,Array<AX::Element>,Array<>]
247
249
  def search kind, filters = {}, &block
248
250
  kind = kind.to_s
@@ -262,19 +264,23 @@ class AX::Element
262
264
  # As the opposite of {#search}, this also takes filters, and can
263
265
  # be used to find a specific ancestor for the current element.
264
266
  #
267
+ # Returns `nil` if no ancestor is found.
268
+ #
265
269
  # @example
266
270
  #
267
271
  # button.ancestor :window # => #<AX::StandardWindow>
268
272
  # row.ancestor :scroll_area # => #<AX::ScrollArea>
269
273
  #
270
- # @param [#to_s]
271
- # @param [Hash{Symbol=>Object}]
272
- # @return [AX::Element]
274
+ # @param kind [#to_s]
275
+ # @param filters [Hash{Symbol=>Object}]
276
+ # @yield Optional block used for search filtering
277
+ # @return [AX::Element,nil]
273
278
  def ancestor kind, filters = {}, &block
274
279
  qualifier = Accessibility::Qualifier.new(kind, filters, &block)
275
- element = attribute :parent
280
+ element = self
276
281
  until qualifier.qualifies? element
277
282
  element = element.attribute :parent
283
+ return nil unless element
278
284
  end
279
285
  element
280
286
  end
@@ -336,7 +342,7 @@ class AX::Element
336
342
  return attribute(method)
337
343
 
338
344
  elsif @ref.parameterized_attributes.include? key
339
- return paramaterized_attribute(method, args.first)
345
+ return parameterized_attribute(method, args.first)
340
346
 
341
347
  elsif @ref.attributes.include? KAXChildrenAttribute
342
348
  if (result = search(method, *args, &block)).blank?
@@ -359,7 +365,7 @@ class AX::Element
359
365
  #
360
366
  # @return [String]
361
367
  def inspect
362
- msg = "#<#{self.class}" << pp_identifier
368
+ msg = "#<#{self.class}" << pp_identifier.to_s
363
369
  msg << pp_position if attributes.include? :position
364
370
  msg << pp_children if attributes.include? :children
365
371
  msg << pp_checkbox(:enabled) if attributes.include? :enabled
@@ -429,14 +435,14 @@ class AX::Element
429
435
  def bounds
430
436
  CGRect.new(attribute(:position), attribute(:size))
431
437
  end
432
- alias_method :rect, :bounds
438
+ alias_method :to_rect, :bounds
433
439
 
434
440
  ##
435
441
  # Get the application object for the element.
436
442
  #
437
443
  # @return [AX::Application]
438
444
  def application
439
- process @ref.application
445
+ @ref.application.to_ruby
440
446
  end
441
447
 
442
448
  # (see NilClass#blank?)
@@ -448,16 +454,24 @@ class AX::Element
448
454
  # Return whether or not the receiver is "dead".
449
455
  #
450
456
  # A dead element is one that is no longer in the app's view
451
- # hierarchy, which is not necessarily related to visibility.
457
+ # hierarchy. This is not directly related to visibility, but an
458
+ # element that is invalid will not be visible, but an invisible
459
+ # element might not be invalid.
452
460
  def invalid?
453
- @ref.role.nil?
461
+ @ref.invalid?
454
462
  end
455
463
 
456
- ##
457
- # Like {#respond_to?}, this is overriden to include attribute methods.
458
- # Though, it does include dynamic predicate methods at the moment.
459
- def methods include_super = true, include_objc_super = false
460
- super.concat(attributes).concat(parameterized_attributes)
464
+ if on_macruby?
465
+ ##
466
+ # Like {#respond_to?}, this is overriden to include attribute methods.
467
+ # Though, it does include dynamic predicate methods at the moment.
468
+ def methods include_super = true, include_objc_super = false
469
+ super.concat(attributes).concat(parameterized_attributes)
470
+ end
471
+ else
472
+ def methods include_super = true
473
+ super.concat(attributes).concat(parameterized_attributes)
474
+ end
461
475
  end
462
476
 
463
477
  ##
@@ -477,25 +491,8 @@ class AX::Element
477
491
  # @return [String]
478
492
  EQUALS = '='
479
493
 
480
- end
481
-
482
-
483
- # Extensions so checking `#blank?` on search result "just works".
484
- class NSArray
485
- # (see NilClass#blank?)
486
- alias_method :blank?, :empty?
487
- end
494
+ # @private
495
+ # @return [Accessibility::Translator]
496
+ TRANSLATOR = Accessibility::Translator.instance
488
497
 
489
- # Extensions so checking `#blank?` on search result "just works".
490
- class NilClass
491
- ##
492
- # Whether or not the object is "blank". The concept of blankness
493
- # borrowed from `Active Support` and is true if the object is falsey
494
- # or `#empty?`.
495
- #
496
- # This method is used by implicit searching in AXElements to
497
- # determine if searches yielded responses.
498
- def blank?
499
- true
500
- end
501
498
  end