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.
- data/.yardopts +4 -0
- data/History.markdown +41 -0
- data/README.markdown +59 -62
- data/Rakefile +1 -1
- data/ext/accessibility/key_coder/extconf.rb +1 -1
- data/ext/accessibility/key_coder/key_coder.c +8 -5
- data/lib/accessibility/dsl.rb +261 -87
- data/lib/accessibility/enumerators.rb +14 -11
- data/lib/accessibility/errors.rb +4 -3
- data/lib/accessibility/factory.rb +159 -108
- data/lib/accessibility/graph.rb +13 -9
- data/lib/accessibility/{pp_inspector.rb → pretty_printer.rb} +4 -5
- data/lib/accessibility/qualifier.rb +23 -13
- data/lib/accessibility/string.rb +4 -4
- data/lib/accessibility/system_info.rb +230 -0
- data/lib/accessibility/translator.rb +38 -28
- data/lib/accessibility/version.rb +24 -2
- data/lib/accessibility.rb +25 -8
- data/lib/ax/application.rb +207 -77
- data/lib/ax/element.rb +62 -65
- data/lib/ax/menu.rb +5 -1
- data/lib/ax/row.rb +1 -1
- data/lib/ax/scroll_area.rb +7 -6
- data/lib/ax/systemwide.rb +38 -5
- data/lib/ax_elements/active_support_selections.rb +10 -0
- data/lib/ax_elements/mri.rb +57 -0
- data/lib/ax_elements/nsarray_compat.rb +97 -17
- data/lib/ax_elements.rb +9 -1
- data/rakelib/gem.rake +11 -11
- data/rakelib/test.rake +0 -9
- data/test/helper.rb +10 -18
- data/test/integration/accessibility/test_dsl.rb +52 -42
- data/test/integration/accessibility/test_enumerators.rb +0 -1
- data/test/integration/accessibility/test_graph.rb +1 -0
- data/test/integration/accessibility/test_qualifier.rb +2 -2
- data/test/integration/ax/test_application.rb +9 -2
- data/test/integration/ax/test_element.rb +41 -1
- data/test/sanity/accessibility/test_factory.rb +23 -56
- data/test/sanity/accessibility/{test_pp_inspector.rb → test_pretty_printer.rb} +9 -9
- data/test/sanity/accessibility/test_translator.rb +2 -5
- data/test/sanity/accessibility/test_version.rb +15 -0
- data/test/sanity/ax/test_application.rb +17 -2
- data/test/sanity/ax/test_element.rb +2 -2
- data/test/sanity/ax_elements/test_nsobject_inspect.rb +4 -2
- data/test/sanity/test_ax_elements.rb +1 -0
- metadata +69 -39
- data/lib/accessibility/core.rb +0 -973
- data/lib/accessibility/highlighter.rb +0 -86
- data/lib/ax_elements/vendor/inflection_data.rb +0 -66
- data/lib/ax_elements/vendor/inflections.rb +0 -172
- data/lib/ax_elements/vendor/inflector.rb +0 -306
- data/lib/minitest/ax_elements.rb +0 -175
- data/lib/mouse.rb +0 -223
- data/lib/rspec/expectations/ax_elements.rb +0 -234
- data/test/integration/accessibility/test_core.rb +0 -18
- data/test/integration/minitest/test_ax_elements.rb +0 -89
- data/test/integration/rspec/expectations/test_ax_elements.rb +0 -102
- data/test/sanity/accessibility/test_core.rb +0 -561
- data/test/sanity/accessibility/test_highlighter.rb +0 -56
- data/test/sanity/minitest/test_ax_elements.rb +0 -17
- data/test/sanity/rspec/expectations/test_ax_elements.rb +0 -15
- data/test/sanity/test_mouse.rb +0 -19
data/lib/ax/application.rb
CHANGED
@@ -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
|
-
#
|
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
|
-
#
|
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
|
114
|
+
super Accessibility::Element.application_for arg
|
27
115
|
@app = NSRunningApplication.runningApplicationWithProcessIdentifier arg
|
28
116
|
when String
|
29
|
-
@app
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
139
|
+
end
|
140
|
+
super Accessibility::Element.application_for @app.processIdentifier
|
38
141
|
when NSRunningApplication
|
39
|
-
super
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
#
|
251
|
+
# @note This is often async and may return before the action is completed
|
116
252
|
#
|
117
|
-
#
|
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;
|
295
|
+
@app.terminate; spin 0.25; terminated?
|
125
296
|
when :force_terminate
|
126
297
|
return true if terminated?
|
127
|
-
@app.forceTerminate;
|
298
|
+
@app.forceTerminate; spin 0.25; terminated?
|
128
299
|
when :hide
|
129
300
|
return true if hidden?
|
130
|
-
@app.hide;
|
301
|
+
@app.hide; spin 0.25; hidden?
|
131
302
|
when :unhide
|
132
303
|
return true if active?
|
133
|
-
@app.activateWithOptions(NSApplicationActivateIgnoringOtherApps)
|
134
|
-
|
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
|
-
|
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/
|
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
|
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::
|
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
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
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
|
-
|
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
|
69
|
+
attribute(:description).to_ruby
|
70
70
|
end
|
71
71
|
|
72
72
|
##
|
73
|
-
# Fetch the children elements for the current element.
|
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
|
-
|
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
|
93
|
-
elements =
|
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
|
190
|
-
|
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
|
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
|
-
# @
|
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 =
|
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
|
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 :
|
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
|
-
|
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
|
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.
|
461
|
+
@ref.invalid?
|
454
462
|
end
|
455
463
|
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
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
|
-
|
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
|