AXElements 0.7.8 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|