AXElements 0.7.8 → 0.8.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 +1 -10
- data/README.markdown +7 -14
- data/ext/accessibility/key_coder/key_coder.c +7 -0
- data/lib/AXElements.rb +0 -2
- data/lib/accessibility/core.rb +180 -123
- data/lib/accessibility/dsl.rb +310 -191
- data/lib/accessibility/enumerators.rb +9 -8
- data/lib/accessibility/errors.rb +7 -8
- data/lib/accessibility/factory.rb +16 -9
- data/lib/accessibility/graph.rb +68 -22
- data/lib/accessibility/highlighter.rb +86 -0
- data/lib/accessibility/pp_inspector.rb +4 -4
- data/lib/accessibility/qualifier.rb +11 -9
- data/lib/accessibility/string.rb +12 -4
- data/lib/accessibility/translator.rb +19 -10
- data/lib/accessibility/version.rb +3 -1
- data/lib/accessibility.rb +42 -17
- data/lib/ax/application.rb +90 -30
- data/lib/ax/button.rb +5 -2
- data/lib/ax/element.rb +133 -149
- data/lib/ax/pop_up_button.rb +12 -0
- data/lib/ax/radio_button.rb +5 -2
- data/lib/ax/row.rb +2 -2
- data/lib/ax/static_text.rb +5 -2
- data/lib/ax/systemwide.rb +24 -12
- data/lib/ax_elements/awesome_print.rb +13 -0
- data/lib/ax_elements/exception_workaround.rb +5 -0
- data/lib/ax_elements/nsarray_compat.rb +1 -0
- data/lib/ax_elements.rb +2 -1
- data/lib/minitest/ax_elements.rb +60 -4
- data/lib/mouse.rb +47 -20
- data/lib/rspec/expectations/ax_elements.rb +180 -88
- data/rakelib/doc.rake +7 -0
- data/test/helper.rb +2 -1
- data/test/integration/accessibility/test_dsl.rb +126 -18
- data/test/integration/accessibility/test_errors.rb +1 -1
- data/test/integration/ax/test_element.rb +17 -0
- data/test/integration/minitest/test_ax_elements.rb +33 -38
- data/test/integration/rspec/expectations/test_ax_elements.rb +68 -19
- data/test/sanity/accessibility/test_core.rb +45 -37
- data/test/sanity/accessibility/test_highlighter.rb +56 -0
- data/test/sanity/ax/test_application.rb +8 -0
- data/test/sanity/ax/test_element.rb +7 -3
- data/test/sanity/minitest/test_ax_elements.rb +2 -0
- data/test/sanity/rspec/expectations/test_ax_elements.rb +3 -0
- data/test/sanity/test_accessibility.rb +9 -0
- data/test/sanity/test_mouse.rb +2 -2
- metadata +11 -38
- data/docs/AccessibilityTips.markdown +0 -119
- data/docs/Acting.markdown +0 -340
- data/docs/Debugging.markdown +0 -165
- data/docs/Inspecting.markdown +0 -261
- data/docs/KeyboardEvents.markdown +0 -122
- data/docs/NewBehaviour.markdown +0 -151
- data/docs/Notifications.markdown +0 -271
- data/docs/Searching.markdown +0 -250
- data/docs/TestingExtensions.markdown +0 -52
- data/docs/images/all_the_buttons.jpg +0 -0
- data/docs/images/next_version.png +0 -0
- data/docs/images/ui_hierarchy.dot +0 -34
- data/docs/images/ui_hierarchy.png +0 -0
- data/lib/accessibility/debug.rb +0 -164
- data/test/integration/accessibility/test_debug.rb +0 -44
- data/test/sanity/accessibility/test_debug.rb +0 -63
data/lib/accessibility/core.rb
CHANGED
@@ -31,8 +31,9 @@ require 'accessibility/version'
|
|
31
31
|
# still work out ok.
|
32
32
|
# @todo I feel a bit weird having to instantiate a new pointer every
|
33
33
|
# time I want to fetch an attribute. Since allocations are costly,
|
34
|
-
#
|
35
|
-
#
|
34
|
+
# and searching requires _many_ AXAPI calls, it hurts performance
|
35
|
+
# a lot when it comes to searches. I wonder if it would pay off
|
36
|
+
# to have a pool of pointers...
|
36
37
|
#
|
37
38
|
# Core abstraction layer that that interacts with OS X Accessibility
|
38
39
|
# APIs (AXAPI). This is actually just a mixin for `AXUIElementRef` objects
|
@@ -85,8 +86,11 @@ module Accessibility::Core
|
|
85
86
|
# will be unwrapped for you, if you expect to get a {CFRange} you
|
86
87
|
# will be given a {Range} instead.
|
87
88
|
#
|
88
|
-
# As a convention, if the backing element is no longer alive
|
89
|
-
#
|
89
|
+
# As a convention, if the backing element is no longer alive, or
|
90
|
+
# the attribute does not exist, or a system failure occurs then
|
91
|
+
# you will receive `nil` for any attribute except for
|
92
|
+
# `KAXChildrenAttribute`. `KAXChildrenAttribute` will always return
|
93
|
+
# an array.
|
90
94
|
#
|
91
95
|
# @example
|
92
96
|
# attribute KAXTitleAttribute # => "HotCocoa Demo"
|
@@ -94,14 +98,15 @@ module Accessibility::Core
|
|
94
98
|
# attribute KAXParentAttribute # => #<AXUIElementRef>
|
95
99
|
# attribute KAXNoValueAttribute # => nil
|
96
100
|
#
|
97
|
-
# @param [String]
|
101
|
+
# @param [String]
|
98
102
|
def attribute name
|
99
103
|
ptr = Pointer.new :id
|
100
104
|
case code = AXUIElementCopyAttributeValue(self, name, ptr)
|
101
105
|
when 0
|
102
106
|
ptr.value.to_ruby
|
103
107
|
when KAXErrorNoValue, KAXErrorAttributeUnsupported,
|
104
|
-
KAXErrorFailure, KAXErrorInvalidUIElement
|
108
|
+
KAXErrorFailure, KAXErrorInvalidUIElement,
|
109
|
+
KAXErrorIllegalArgument then
|
105
110
|
name == KAXChildrenAttribute ? [] : nil
|
106
111
|
else
|
107
112
|
handle_error code, name
|
@@ -109,13 +114,14 @@ module Accessibility::Core
|
|
109
114
|
end
|
110
115
|
|
111
116
|
##
|
112
|
-
# Shortcut for getting the `KAXRoleAttribute`.
|
117
|
+
# Shortcut for getting the `KAXRoleAttribute`. Remember that
|
118
|
+
# dead elements may return `nil` for their role.
|
113
119
|
#
|
114
120
|
# @example
|
115
121
|
#
|
116
122
|
# role # => KAXWindowRole
|
117
123
|
#
|
118
|
-
# @return [String]
|
124
|
+
# @return [String,nil]
|
119
125
|
def role
|
120
126
|
attribute KAXRoleAttribute
|
121
127
|
end
|
@@ -136,7 +142,9 @@ module Accessibility::Core
|
|
136
142
|
end
|
137
143
|
|
138
144
|
##
|
139
|
-
# Shortcut for getting the `KAXChildrenAttribute`.
|
145
|
+
# Shortcut for getting the `KAXChildrenAttribute`. This is guaranteed
|
146
|
+
# to alwayl return an array or raise an exception, unless the object
|
147
|
+
# you are querying is behaving maliciously.
|
140
148
|
#
|
141
149
|
# @example
|
142
150
|
#
|
@@ -159,6 +167,33 @@ module Accessibility::Core
|
|
159
167
|
attribute KAXValueAttribute
|
160
168
|
end
|
161
169
|
|
170
|
+
##
|
171
|
+
# Get the process identifier (PID) of the application that the element
|
172
|
+
# belongs to.
|
173
|
+
#
|
174
|
+
# This method will return `0` if the element is dead or if the receiver
|
175
|
+
# is the the system wide element.
|
176
|
+
#
|
177
|
+
# @example
|
178
|
+
#
|
179
|
+
# pid # => 12345
|
180
|
+
# system_wide.pid # => 0
|
181
|
+
#
|
182
|
+
# @return [Fixnum]
|
183
|
+
def pid
|
184
|
+
@pid ||= (
|
185
|
+
ptr = Pointer.new :int
|
186
|
+
case code = AXUIElementGetPid(self, ptr)
|
187
|
+
when 0
|
188
|
+
ptr.value
|
189
|
+
when KAXErrorInvalidUIElement
|
190
|
+
self == system_wide ? 0 : handle_error(code)
|
191
|
+
else
|
192
|
+
handle_error code
|
193
|
+
end
|
194
|
+
)
|
195
|
+
end
|
196
|
+
|
162
197
|
##
|
163
198
|
# Get the size of the array for attributes that would return an array.
|
164
199
|
# When performance matters, this is much faster than getting the array
|
@@ -172,7 +207,7 @@ module Accessibility::Core
|
|
172
207
|
# size_of KAXChildrenAttribute # => 19
|
173
208
|
# size_of KAXRowsAttribute # => 100
|
174
209
|
#
|
175
|
-
# @param [String]
|
210
|
+
# @param [String]
|
176
211
|
# @return [Number]
|
177
212
|
def size_of name
|
178
213
|
ptr = Pointer.new :long_long
|
@@ -188,14 +223,18 @@ module Accessibility::Core
|
|
188
223
|
end
|
189
224
|
|
190
225
|
##
|
191
|
-
# Returns whether or not an attribute is writable.
|
226
|
+
# Returns whether or not an attribute is writable. You should check
|
227
|
+
# writability before trying call {#set} for the attribute.
|
228
|
+
#
|
229
|
+
# In case of internal error or if the element dies, this method will
|
230
|
+
# return `false`.
|
192
231
|
#
|
193
232
|
# @example
|
194
233
|
#
|
195
234
|
# writable? KAXSizeAttribute # => true
|
196
235
|
# writable? KAXTitleAttribute # => false
|
197
236
|
#
|
198
|
-
# @param [String]
|
237
|
+
# @param [String]
|
199
238
|
def writable? name
|
200
239
|
ptr = Pointer.new :bool
|
201
240
|
case code = AXUIElementIsAttributeSettable(self, name, ptr)
|
@@ -210,24 +249,24 @@ module Accessibility::Core
|
|
210
249
|
end
|
211
250
|
|
212
251
|
##
|
213
|
-
#
|
214
|
-
# you are setting. If you need to check, use {#writable?}
|
215
|
-
# first.
|
216
|
-
#
|
217
|
-
# Set the given value to the given attribute. You do not need to
|
252
|
+
# Set the given value for the given attribute. You do not need to
|
218
253
|
# worry about wrapping objects first, `Range` objects will also
|
219
254
|
# be automatically converted into `CFRange` objects and then
|
220
255
|
# wrapped.
|
221
256
|
#
|
222
|
-
#
|
223
|
-
#
|
257
|
+
# This method does not check writability of the attribute you are
|
258
|
+
# setting. If you need to check, use {#writable?} first to check.
|
259
|
+
#
|
260
|
+
# Unlike when reading attributes, writing to a dead element, and
|
261
|
+
# other error conditions, will raise an exception.
|
224
262
|
#
|
225
263
|
# @example
|
264
|
+
#
|
226
265
|
# set KAXValueAttribute, "hi" # => "hi"
|
227
266
|
# set KAXSizeAttribute, [250,250] # => [250,250]
|
228
267
|
# set KAXVisibleRangeAttribute, 0..-3 # => 0..-3
|
229
268
|
#
|
230
|
-
# @param [String]
|
269
|
+
# @param [String]
|
231
270
|
def set name, value
|
232
271
|
code = AXUIElementSetAttributeValue(self, name, value.to_ax)
|
233
272
|
if code.zero?
|
@@ -248,6 +287,9 @@ module Accessibility::Core
|
|
248
287
|
# Most elements do not have parameterized attributes, but the ones
|
249
288
|
# that do, have many.
|
250
289
|
#
|
290
|
+
# Similar to {#attributes}, this method will also return an empty
|
291
|
+
# array if the element is dead.
|
292
|
+
#
|
251
293
|
# @example
|
252
294
|
#
|
253
295
|
# parameterized_attributes # => ["AXStringForRange", ...]
|
@@ -266,20 +308,24 @@ module Accessibility::Core
|
|
266
308
|
end
|
267
309
|
|
268
310
|
##
|
269
|
-
# Fetch the given pramaeterized attribute value
|
270
|
-
# Only `AXUIElementRef` objects will be
|
271
|
-
# unwrapped for you automatically and
|
272
|
-
#
|
273
|
-
# parameter as that will be done for you.
|
311
|
+
# Fetch the given pramaeterized attribute value for the given parameter.
|
312
|
+
# Only `AXUIElementRef` objects will be returned in their raw form,
|
313
|
+
# {Boxed} objects will be unwrapped for you automatically and {CFRange}
|
314
|
+
# objects will be turned into {Range} objects. Similarly, you do not
|
315
|
+
# need to worry about wrapping the parameter as that will be done for you.
|
316
|
+
#
|
317
|
+
# As a convention, if the backing element is no longer alive, or the
|
318
|
+
# attribute does not exist, or a system failure occurs then you will
|
319
|
+
# receive `nil`.
|
274
320
|
#
|
275
321
|
# @example
|
276
322
|
#
|
277
|
-
#
|
323
|
+
# parameterized_attribute KAXStringForRangeParameterizedAttribute, 1..10
|
278
324
|
# # => "ello, worl"
|
279
325
|
#
|
280
|
-
# @param [String]
|
281
|
-
# @param [Object]
|
282
|
-
def
|
326
|
+
# @param [String]
|
327
|
+
# @param [Object]
|
328
|
+
def parameterized_attribute name, param
|
283
329
|
ptr = Pointer.new :id
|
284
330
|
case code = AXUIElementCopyParameterizedAttributeValue(self, name, param.to_ax, ptr)
|
285
331
|
when 0
|
@@ -297,8 +343,8 @@ module Accessibility::Core
|
|
297
343
|
|
298
344
|
##
|
299
345
|
# Get the list of actions that the element can perform. If an element
|
300
|
-
# does not have actions, then an empty list will be returned.
|
301
|
-
#
|
346
|
+
# does not have actions, then an empty list will be returned. Dead
|
347
|
+
# elements will also return an empty array.
|
302
348
|
#
|
303
349
|
# @example
|
304
350
|
#
|
@@ -318,7 +364,8 @@ module Accessibility::Core
|
|
318
364
|
|
319
365
|
##
|
320
366
|
# Ask an element to perform the given action. This method will always
|
321
|
-
# return true or raise an exception. Actions should never fail
|
367
|
+
# return true or raise an exception. Actions should never fail, but
|
368
|
+
# there are some extreme edge cases (e.g. out of memory, etc.).
|
322
369
|
#
|
323
370
|
# Unlike when reading attributes, performing an action on a dead element
|
324
371
|
# will raise an exception.
|
@@ -327,7 +374,7 @@ module Accessibility::Core
|
|
327
374
|
#
|
328
375
|
# perform KAXPressAction # => true
|
329
376
|
#
|
330
|
-
# @param [String]
|
377
|
+
# @param [String]
|
331
378
|
# @return [Boolean]
|
332
379
|
def perform action
|
333
380
|
code = AXUIElementPerformAction(self, action)
|
@@ -351,7 +398,7 @@ module Accessibility::Core
|
|
351
398
|
# keyup).
|
352
399
|
#
|
353
400
|
# You can learn more about keyboard events from the
|
354
|
-
#
|
401
|
+
# [Keyboard Events documentation](http://github.com/Marketcircle/AXElements/wiki/Keyboarding).
|
355
402
|
#
|
356
403
|
# @example
|
357
404
|
#
|
@@ -365,14 +412,12 @@ module Accessibility::Core
|
|
365
412
|
events.each do |event|
|
366
413
|
code = AXUIElementPostKeyboardEvent(self, 0, *event)
|
367
414
|
handle_error code unless code.zero?
|
368
|
-
sleep
|
415
|
+
sleep @@key_rate
|
369
416
|
end
|
370
417
|
sleep 0.1 # in many cases, UI is not done updating right away
|
371
418
|
end
|
372
419
|
|
373
420
|
##
|
374
|
-
# @todo Make this runtime configurable.
|
375
|
-
#
|
376
421
|
# The delay between key presses. The default value is `0.01`, which
|
377
422
|
# should be about 50 characters per second (down and up are separate
|
378
423
|
# events).
|
@@ -382,12 +427,15 @@ module Accessibility::Core
|
|
382
427
|
# were tried, but were way too big.
|
383
428
|
#
|
384
429
|
# @return [Number]
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
430
|
+
def key_rate
|
431
|
+
@@key_rate
|
432
|
+
end
|
433
|
+
@@key_rate = case ENV['KEY_RATE']
|
434
|
+
when 'VERY_SLOW' then 0.9
|
435
|
+
when 'SLOW' then 0.09
|
436
|
+
when nil then 0.009
|
437
|
+
else ENV['KEY_RATE'].to_f
|
438
|
+
end
|
391
439
|
|
392
440
|
|
393
441
|
# @group Element Hierarchy Entry Points
|
@@ -401,13 +449,16 @@ module Accessibility::Core
|
|
401
449
|
# system (origin is in the top-left, increasing downward and to the right
|
402
450
|
# as if reading a book in English).
|
403
451
|
#
|
404
|
-
# If more than one element is at the position then the
|
405
|
-
#
|
406
|
-
#
|
452
|
+
# If more than one element is at the position then the z-order of the
|
453
|
+
# elements will be used to determine which is "on top".
|
454
|
+
#
|
455
|
+
# This method will safely return `nil` if there is no UI element at the
|
456
|
+
# give point.
|
407
457
|
#
|
408
458
|
# @example
|
409
459
|
#
|
410
460
|
# element_at [453, 200] # table
|
461
|
+
# element_at CGPoint.new(453, 200) # table
|
411
462
|
#
|
412
463
|
# @param [#to_point]
|
413
464
|
# @return [AXUIElementRef,nil]
|
@@ -426,15 +477,15 @@ module Accessibility::Core
|
|
426
477
|
end
|
427
478
|
|
428
479
|
##
|
429
|
-
# Get the application
|
430
|
-
#
|
480
|
+
# Get the application object object/token for an application given the
|
481
|
+
# process identifier (PID) for that application.
|
431
482
|
#
|
432
483
|
# @example
|
433
484
|
#
|
434
485
|
# app = application_for 54743 # => #<AXUIElementRefx00000000>
|
435
486
|
# CFShow(app)
|
436
487
|
#
|
437
|
-
# @param [
|
488
|
+
# @param [Number]
|
438
489
|
# @return [AXUIElementRef]
|
439
490
|
def application_for pid
|
440
491
|
spin_run_loop
|
@@ -505,17 +556,19 @@ module Accessibility::Core
|
|
505
556
|
|
506
557
|
##
|
507
558
|
# @todo Should passing around a context be supported?
|
559
|
+
# @todo If a block is passed instead of an observer, then implicitly create
|
560
|
+
# the observer.
|
508
561
|
#
|
509
562
|
# Register a notification observer for a specific event.
|
510
563
|
#
|
511
564
|
# @example
|
512
565
|
#
|
513
|
-
# register observer,
|
566
|
+
# register observer, KAXWindowCreatedNotification
|
514
567
|
#
|
515
568
|
# @param [AXObserverRef]
|
516
569
|
# @param [String]
|
517
570
|
# @return [Boolean]
|
518
|
-
def register observer,
|
571
|
+
def register observer, notif
|
519
572
|
case code = AXObserverAddNotification(observer, self, notif, nil)
|
520
573
|
when 0 then true
|
521
574
|
else handle_error code, notif, observer, nil, nil
|
@@ -525,10 +578,14 @@ module Accessibility::Core
|
|
525
578
|
##
|
526
579
|
# Unregister a notification that has been previously setup.
|
527
580
|
#
|
581
|
+
# @example
|
582
|
+
#
|
583
|
+
# unregister observer, KAXWindowCreatedNotification
|
584
|
+
#
|
528
585
|
# @param [AXObserverRef]
|
529
586
|
# @param [String]
|
530
587
|
# @return [Boolean]
|
531
|
-
def unregister observer,
|
588
|
+
def unregister observer, notif
|
532
589
|
case code = AXObserverRemoveNotification(observer, self, notif)
|
533
590
|
when 0 then true
|
534
591
|
else handle_error code, notif, observer, nil, nil
|
@@ -538,43 +595,6 @@ module Accessibility::Core
|
|
538
595
|
|
539
596
|
# @group Misc.
|
540
597
|
|
541
|
-
##
|
542
|
-
# Ask whether or not AXAPI is enabled.
|
543
|
-
#
|
544
|
-
# @example
|
545
|
-
#
|
546
|
-
# enabled? # => true
|
547
|
-
#
|
548
|
-
# # After unchecking "Enable access for assistive devices" in System Prefs
|
549
|
-
# enabled? # => false
|
550
|
-
#
|
551
|
-
def enabled?
|
552
|
-
AXAPIEnabled()
|
553
|
-
end
|
554
|
-
|
555
|
-
##
|
556
|
-
# Get the process identifier (PID) of the application that the element
|
557
|
-
# belongs to.
|
558
|
-
#
|
559
|
-
# @example
|
560
|
-
#
|
561
|
-
# pid # => 12345
|
562
|
-
#
|
563
|
-
# @return [Fixnum]
|
564
|
-
def pid
|
565
|
-
@pid ||= (
|
566
|
-
ptr = Pointer.new :int
|
567
|
-
case code = AXUIElementGetPid(self, ptr)
|
568
|
-
when 0
|
569
|
-
ptr.value
|
570
|
-
when KAXErrorInvalidUIElement
|
571
|
-
self == system_wide ? 0 : handle_error(code)
|
572
|
-
else
|
573
|
-
handle_error code
|
574
|
-
end
|
575
|
-
)
|
576
|
-
end
|
577
|
-
|
578
598
|
##
|
579
599
|
# Create a new reference to the system wide object. This is very useful when
|
580
600
|
# working with the system wide object as caching the system wide reference
|
@@ -590,7 +610,8 @@ module Accessibility::Core
|
|
590
610
|
end
|
591
611
|
|
592
612
|
##
|
593
|
-
# Returns the application reference
|
613
|
+
# Returns the application reference for the application that the receiver
|
614
|
+
# belongs to.
|
594
615
|
#
|
595
616
|
# @return [AXUIElementRef]
|
596
617
|
def application
|
@@ -605,7 +626,7 @@ module Accessibility::Core
|
|
605
626
|
#
|
606
627
|
# spin_run_loop # not much to it
|
607
628
|
#
|
608
|
-
# @return [
|
629
|
+
# @return [void]
|
609
630
|
def spin_run_loop
|
610
631
|
NSRunLoop.currentRunLoop.runUntilDate Time.now
|
611
632
|
end
|
@@ -619,8 +640,8 @@ module Accessibility::Core
|
|
619
640
|
#
|
620
641
|
# Setting the global timeout to `0` seconds will reset the timeout value
|
621
642
|
# to the system default. Apple does not appear to have publicly documented
|
622
|
-
# what the system default is
|
623
|
-
# is.
|
643
|
+
# what the system default is and there is no API to check what the value
|
644
|
+
# is set to, so I can't tell you what the current value is.
|
624
645
|
#
|
625
646
|
# @param [Number]
|
626
647
|
# @return [Number]
|
@@ -647,18 +668,21 @@ module Accessibility::Core
|
|
647
668
|
raise klass, msg, caller(1)
|
648
669
|
end
|
649
670
|
|
671
|
+
# @return [String]
|
650
672
|
def handle_failure *args
|
651
673
|
"A system failure occurred with #{inspect}, stopping to be safe"
|
652
674
|
end
|
653
675
|
|
676
|
+
# @todo this is a pretty crappy way of handling the various illegal
|
677
|
+
# argument cases
|
678
|
+
# @return [String]
|
654
679
|
def handle_illegal_argument *args
|
655
680
|
case args.size
|
656
681
|
when 0
|
657
682
|
"#{inspect} is not an AXUIElementRef"
|
658
683
|
when 1
|
659
|
-
"Either the element #{inspect} " +
|
660
|
-
"
|
661
|
-
"is not a legal argument"
|
684
|
+
"Either the element #{inspect} or the attribute/action/callback " +
|
685
|
+
"#{args.first.inspect} is not a legal argument"
|
662
686
|
when 2
|
663
687
|
"You can't get/set #{args.first.inspect} with/to " +
|
664
688
|
"#{args[1].inspect} for #{inspect}"
|
@@ -666,70 +690,76 @@ module Accessibility::Core
|
|
666
690
|
"The point #{args.first.to_point.inspect} is not a valid point, " +
|
667
691
|
"or #{inspect} is not an AXUIElementRef"
|
668
692
|
when 4
|
669
|
-
"Either the observer #{args[1].inspect}, " +
|
670
|
-
"the
|
671
|
-
"the notification #{args.first.inspect} " +
|
672
|
-
"is not a legitimate argument"
|
693
|
+
"Either the observer #{args[1].inspect}, the element #{inspect}, " +
|
694
|
+
"or the notification #{args.first.inspect} is not a legitimate argument"
|
673
695
|
end
|
674
696
|
end
|
675
697
|
|
698
|
+
# @return [String]
|
676
699
|
def handle_invalid_element *args
|
677
700
|
"#{inspect} is no longer a valid reference"
|
678
701
|
end
|
679
702
|
|
703
|
+
# @return [String]
|
680
704
|
def handle_invalid_observer *args
|
681
|
-
"#{args[1].inspect} is no longer a valid observer for "
|
682
|
-
"#{inspect} or was never valid"
|
705
|
+
"#{args[1].inspect} is no longer a valid observer for #{inspect} or was never valid"
|
683
706
|
end
|
684
707
|
|
685
708
|
# @param [AXUIElementRef]
|
709
|
+
# @return [String]
|
686
710
|
def handle_cannot_complete *args
|
687
711
|
spin_run_loop
|
688
712
|
app = NSRunningApplication.runningApplicationWithProcessIdentifier pid
|
689
713
|
if app
|
690
|
-
"An unspecified error occurred using #{inspect} with AXAPI"
|
691
|
-
", maybe a timeout :("
|
714
|
+
"An unspecified error occurred using #{inspect} with AXAPI, maybe a timeout :("
|
692
715
|
else
|
693
716
|
"Application for pid=#{pid} is no longer running. Maybe it crashed?"
|
694
717
|
end
|
695
718
|
end
|
696
719
|
|
720
|
+
# @return [String]
|
697
721
|
def handle_attr_unsupported *args
|
698
722
|
"#{inspect} does not have a #{args.first.inspect} attribute"
|
699
723
|
end
|
700
724
|
|
725
|
+
# @return [String]
|
701
726
|
def handle_action_unsupported *args
|
702
727
|
"#{inspect} does not have a #{args.first.inspect} action"
|
703
728
|
end
|
704
729
|
|
730
|
+
# @return [String]
|
705
731
|
def handle_notif_unsupported *args
|
706
732
|
"#{inspect} does not support the #{args.first.inspect} notification"
|
707
733
|
end
|
708
734
|
|
735
|
+
# @return [String]
|
709
736
|
def handle_not_implemented *args
|
710
737
|
"The program that owns #{inspect} does not work with AXAPI properly"
|
711
738
|
end
|
712
739
|
|
713
740
|
# @todo Does this really neeed to raise an exception? Seems
|
714
741
|
# like a warning would be sufficient.
|
742
|
+
# @return [String]
|
715
743
|
def handle_notif_registered *args
|
716
|
-
"You have already registered to hear about #{args[0].inspect} "
|
717
|
-
"from #{inspect}"
|
744
|
+
"You have already registered to hear about #{args[0].inspect} from #{inspect}"
|
718
745
|
end
|
719
746
|
|
747
|
+
# @return [String]
|
720
748
|
def handle_notif_not_registered *args
|
721
|
-
"You have not registered to hear about #{args[0].inspect} "
|
722
|
-
"from #{inspect}"
|
749
|
+
"You have not registered to hear about #{args[0].inspect} from #{inspect}"
|
723
750
|
end
|
724
751
|
|
752
|
+
# @return [String]
|
725
753
|
def handle_api_disabled *args
|
726
|
-
|
754
|
+
"AXAPI has been disabled"
|
727
755
|
end
|
728
756
|
|
757
|
+
# @return [String]
|
729
758
|
def handle_param_attr_unsupported *args
|
730
759
|
"#{inspect} does not have a #{args[0].inspect} parameterized attribute"
|
731
760
|
end
|
732
761
|
|
762
|
+
# @return [String]
|
733
763
|
def handle_not_enough_precision
|
734
764
|
'AXAPI said there was not enough precision ¯\(°_o)/¯'
|
735
765
|
end
|
@@ -738,24 +768,18 @@ module Accessibility::Core
|
|
738
768
|
|
739
769
|
|
740
770
|
##
|
741
|
-
# @private
|
742
|
-
#
|
743
771
|
# `Pointer` type encoding for `CFArrayRef` objects.
|
744
772
|
#
|
745
773
|
# @return [String]
|
746
774
|
ARRAY = '^{__CFArray}'
|
747
775
|
|
748
776
|
##
|
749
|
-
# @private
|
750
|
-
#
|
751
777
|
# `Pointer` type encoding for `AXUIElementRef` objects.
|
752
778
|
#
|
753
779
|
# @return [String]
|
754
780
|
ELEMENT = '^{__AXUIElement}'
|
755
781
|
|
756
782
|
##
|
757
|
-
# @private
|
758
|
-
#
|
759
783
|
# `Pointer` type encoding for `AXObserverRef` objects.
|
760
784
|
#
|
761
785
|
# @return [String]
|
@@ -821,7 +845,7 @@ module Accessibility::ValueUnwrapper
|
|
821
845
|
end
|
822
846
|
end
|
823
847
|
|
824
|
-
# hack to find the
|
848
|
+
# hack to find the __NSCFType class
|
825
849
|
klass = AXUIElementCreateSystemWide().class
|
826
850
|
klass.send :include, Accessibility::Core
|
827
851
|
klass.send :include, Accessibility::ValueUnwrapper
|
@@ -838,7 +862,7 @@ class Boxed
|
|
838
862
|
#
|
839
863
|
# @return [Number]
|
840
864
|
def self.ax_value
|
841
|
-
raise NotImplementedError, "#{self.class} cannot be wraped"
|
865
|
+
raise NotImplementedError, "#{inspect}:#{self.class} cannot be wraped"
|
842
866
|
end
|
843
867
|
|
844
868
|
##
|
@@ -848,8 +872,8 @@ class Boxed
|
|
848
872
|
#
|
849
873
|
# @example
|
850
874
|
#
|
851
|
-
#
|
852
|
-
#
|
875
|
+
# CGPoint.new(12, 34).to_ax # => #<AXValueRef:0x455678e2>
|
876
|
+
# CGSize.new(56, 78).to_ax # => #<AXValueRef:0x555678e2>
|
853
877
|
#
|
854
878
|
# @return [AXValueRef]
|
855
879
|
def to_ax
|
@@ -861,18 +885,34 @@ class Boxed
|
|
861
885
|
end
|
862
886
|
|
863
887
|
# AXElements extensions for `CFRange`.
|
864
|
-
class << CFRange
|
888
|
+
class << CFRange
|
889
|
+
# (see Boxed.ax_value)
|
890
|
+
def ax_value; KAXValueCFRangeType end
|
891
|
+
end
|
865
892
|
# AXElements extensions for `CGSize`.
|
866
|
-
class << CGSize
|
893
|
+
class << CGSize
|
894
|
+
# (see Boxed.ax_value)
|
895
|
+
def ax_value; KAXValueCGSizeType end
|
896
|
+
end
|
867
897
|
# AXElements extensions for `CGRect`.
|
868
|
-
class << CGRect
|
898
|
+
class << CGRect
|
899
|
+
# (see Boxed.ax_value)
|
900
|
+
def ax_value; KAXValueCGRectType end
|
901
|
+
end
|
869
902
|
# AXElements extensions for `CGPoint`.
|
870
|
-
class << CGPoint
|
903
|
+
class << CGPoint
|
904
|
+
# (see Boxed.ax_value)
|
905
|
+
def ax_value; KAXValueCGPointType end
|
906
|
+
end
|
871
907
|
|
872
908
|
|
873
909
|
# AXElements extensions for `NSObject`.
|
874
910
|
class NSObject
|
911
|
+
##
|
912
|
+
# Return an object safe for passing to AXAPI.
|
875
913
|
def to_ax; self end
|
914
|
+
##
|
915
|
+
# Return a usable object from an AXAPI pointer.
|
876
916
|
def to_ruby; self end
|
877
917
|
end
|
878
918
|
|
@@ -914,3 +954,20 @@ class CGPoint
|
|
914
954
|
# @return [CGPoint]
|
915
955
|
def to_point; self end
|
916
956
|
end
|
957
|
+
|
958
|
+
|
959
|
+
##
|
960
|
+
# AXElements extensions for `NSURL`.
|
961
|
+
class NSString
|
962
|
+
##
|
963
|
+
# Create an NSURL using the receiver as the initialization string.
|
964
|
+
# If the receiver is not a valid URL then `nil` will be returned.
|
965
|
+
#
|
966
|
+
# This exists because of
|
967
|
+
# [rdar://11207662](http://openradar.appspot.com/11207662).
|
968
|
+
#
|
969
|
+
# @return [NSURL,nil]
|
970
|
+
def to_url
|
971
|
+
NSURL.URLWithString self
|
972
|
+
end
|
973
|
+
end
|