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/ax/element.rb
CHANGED
@@ -1,18 +1,22 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'accessibility/core'
|
4
|
+
require 'accessibility/factory'
|
5
|
+
require 'accessibility/translator'
|
4
6
|
require 'accessibility/enumerators'
|
5
7
|
require 'accessibility/qualifier'
|
6
8
|
require 'accessibility/errors'
|
7
9
|
require 'accessibility/pp_inspector'
|
8
|
-
require 'accessibility/factory'
|
9
|
-
require 'accessibility/core'
|
10
10
|
|
11
11
|
##
|
12
12
|
# @abstract
|
13
13
|
#
|
14
|
-
# The abstract base class for all accessibility objects.
|
15
|
-
#
|
14
|
+
# The abstract base class for all accessibility objects. `AX::Element`
|
15
|
+
# composes low level `AXUIElementRef` objects into a more Rubyish
|
16
|
+
# interface.
|
17
|
+
#
|
18
|
+
# This abstract base class provides generic functionality that all
|
19
|
+
# accessibility objects require.
|
16
20
|
class AX::Element
|
17
21
|
include Accessibility::PPInspector
|
18
22
|
include Accessibility::Factory
|
@@ -39,10 +43,11 @@ class AX::Element
|
|
39
43
|
end
|
40
44
|
|
41
45
|
##
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
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.
|
46
51
|
#
|
47
52
|
# @example
|
48
53
|
#
|
@@ -54,9 +59,12 @@ class AX::Element
|
|
54
59
|
end
|
55
60
|
|
56
61
|
##
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
62
|
+
# Get the accessibility description for the element.
|
63
|
+
#
|
64
|
+
# This overrides the inherited `NSObject#description`. If you want a
|
65
|
+
# description of the object then you should use {#inspect} instead.
|
66
|
+
#
|
67
|
+
# @return [String]
|
60
68
|
def description
|
61
69
|
attribute :description
|
62
70
|
end
|
@@ -71,20 +79,24 @@ class AX::Element
|
|
71
79
|
end
|
72
80
|
|
73
81
|
##
|
74
|
-
#
|
75
|
-
#
|
76
|
-
# efficient to find out how many `children` exist using this API
|
77
|
-
# instead of getting the children array and asking for the size.
|
82
|
+
# 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}).
|
78
84
|
#
|
79
85
|
# @example
|
80
86
|
#
|
81
|
-
#
|
82
|
-
#
|
87
|
+
# element = AX::DOCK.list.application_dock_item
|
88
|
+
# element.ancestry
|
89
|
+
# # => [#<AX::ApplicationDockItem...>, #<AX::List...>, #<AX::Application...>]
|
83
90
|
#
|
84
|
-
# @
|
85
|
-
|
86
|
-
|
87
|
-
|
91
|
+
# @return [Array<AX::Element>]
|
92
|
+
def ancestry *elements
|
93
|
+
elements = [self] if elements.empty?
|
94
|
+
element = elements.last
|
95
|
+
if element.attributes.include? :parent
|
96
|
+
ancestry(elements << element.parent)
|
97
|
+
else
|
98
|
+
elements
|
99
|
+
end
|
88
100
|
end
|
89
101
|
|
90
102
|
##
|
@@ -100,6 +112,23 @@ class AX::Element
|
|
100
112
|
@ref.pid
|
101
113
|
end
|
102
114
|
|
115
|
+
##
|
116
|
+
# Return the `#size` of an attribute. This only works for attributes
|
117
|
+
# that are a collection. This exists because it is _much_ more
|
118
|
+
# efficient to find out how many `children` exist using this API
|
119
|
+
# instead of getting the children array and asking for the size.
|
120
|
+
#
|
121
|
+
# @example
|
122
|
+
#
|
123
|
+
# table.size_of :rows # => 111
|
124
|
+
# window.size_of :children # => 16
|
125
|
+
#
|
126
|
+
# @param [#to_sym]
|
127
|
+
# @return [Number]
|
128
|
+
def size_of attr
|
129
|
+
@ref.size_of TRANSLATOR.cocoaify attr
|
130
|
+
end
|
131
|
+
|
103
132
|
##
|
104
133
|
# Check whether or not an attribute is writable.
|
105
134
|
#
|
@@ -125,7 +154,7 @@ class AX::Element
|
|
125
154
|
# @return the value that you were setting is returned
|
126
155
|
def set attr, value
|
127
156
|
unless writable? attr
|
128
|
-
raise
|
157
|
+
raise ArgumentError, "#{attr} is read-only for #{inspect}"
|
129
158
|
end
|
130
159
|
value = value.relative_to(@ref.value.size) if value.kind_of? Range
|
131
160
|
@ref.set TRANSLATOR.cocoaify(attr), value
|
@@ -153,14 +182,12 @@ class AX::Element
|
|
153
182
|
#
|
154
183
|
# @example
|
155
184
|
#
|
156
|
-
# text_field.
|
185
|
+
# text_field.parameterized_attribute :string_for_range, 2..8
|
157
186
|
#
|
158
187
|
# @param [#to_sym]
|
159
|
-
def
|
160
|
-
|
161
|
-
|
162
|
-
process @ref.attribute(rattr, for_parameter: param)
|
163
|
-
end
|
188
|
+
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)
|
164
191
|
end
|
165
192
|
|
166
193
|
|
@@ -196,11 +223,7 @@ class AX::Element
|
|
196
223
|
# @param [#to_sym]
|
197
224
|
# @return [Boolean] true if successful
|
198
225
|
def perform action
|
199
|
-
|
200
|
-
@ref.perform raction
|
201
|
-
else
|
202
|
-
false
|
203
|
-
end
|
226
|
+
@ref.perform TRANSLATOR.cocoaify action
|
204
227
|
end
|
205
228
|
|
206
229
|
|
@@ -208,14 +231,15 @@ class AX::Element
|
|
208
231
|
|
209
232
|
##
|
210
233
|
# Perform a breadth first search through the view hierarchy rooted at
|
211
|
-
# the current element.
|
234
|
+
# the current element. If you are concerned about the return value of
|
235
|
+
# this method, you can call {#blank?} on the return object.
|
212
236
|
#
|
213
|
-
# See the
|
214
|
-
# details on
|
237
|
+
# See the [Searching Tutorial](http://github.com/Marketcircle/AXElements/wiki/Searching)
|
238
|
+
# for the details on search semantics.
|
215
239
|
#
|
216
240
|
# @example Find the dock icon for the Finder app
|
217
241
|
#
|
218
|
-
# AX::DOCK.search(
|
242
|
+
# AX::DOCK.search(:application_dock_item, title:'Finder')
|
219
243
|
#
|
220
244
|
# @param [#to_s]
|
221
245
|
# @param [Hash{Symbol=>Object}]
|
@@ -233,7 +257,7 @@ class AX::Element
|
|
233
257
|
end
|
234
258
|
|
235
259
|
##
|
236
|
-
# Search for an ancestor of the current
|
260
|
+
# Search for an ancestor of the current element.
|
237
261
|
#
|
238
262
|
# As the opposite of {#search}, this also takes filters, and can
|
239
263
|
# be used to find a specific ancestor for the current element.
|
@@ -261,22 +285,30 @@ class AX::Element
|
|
261
285
|
# lookup is always tried first, followed by a parameterized attribute
|
262
286
|
# lookup, and then finally a search.
|
263
287
|
#
|
264
|
-
# Failing all lookups, this method calls `super
|
288
|
+
# Failing all lookups, this method calls `super`, which will probably
|
289
|
+
# raise an exception; however, most elements have children and so it
|
290
|
+
# is more likely that you will get an {Accessibility::SearchFailure}
|
291
|
+
# in cases where you sholud get a `NoMethodError`.
|
265
292
|
#
|
266
293
|
# @example
|
267
294
|
#
|
268
|
-
# mail =
|
295
|
+
# mail = Accessibility.application_with_bundle_identifier 'com.apple.mail'
|
269
296
|
#
|
270
297
|
# # attribute lookup
|
271
298
|
# window = mail.focused_window
|
272
299
|
# # is equivalent to
|
273
300
|
# window = mail.attribute :focused_window
|
274
301
|
#
|
302
|
+
# # attribute setting
|
303
|
+
# window.position = CGPoint.new(100, 100)
|
304
|
+
# # is equivalent to
|
305
|
+
# window.set :position, CGPoint.new(100, 100)
|
306
|
+
#
|
275
307
|
# # parameterized attribute lookup
|
276
|
-
# window.title_ui_element.string_for_range
|
308
|
+
# window.title_ui_element.string_for_range 1..10
|
277
309
|
# # is equivalent to
|
278
310
|
# title = window.attribute :title_ui_element
|
279
|
-
# title.
|
311
|
+
# title.parameterized_attribute :string_for_range, 1..10
|
280
312
|
#
|
281
313
|
# # simple single element search
|
282
314
|
# window.button # => You want the first Button that is found
|
@@ -293,7 +325,7 @@ class AX::Element
|
|
293
325
|
# # is equivalent to
|
294
326
|
# window.search :button, title: 'Log In'
|
295
327
|
#
|
296
|
-
# #
|
328
|
+
# # searching from #method_missing will #raise if nothing is found
|
297
329
|
# window.application # => SearchFailure is raised
|
298
330
|
#
|
299
331
|
def method_missing method, *args, &block
|
@@ -301,10 +333,10 @@ class AX::Element
|
|
301
333
|
|
302
334
|
key = TRANSLATOR.cocoaify method
|
303
335
|
if @ref.attributes.include? key
|
304
|
-
return attribute
|
336
|
+
return attribute(method)
|
305
337
|
|
306
338
|
elsif @ref.parameterized_attributes.include? key
|
307
|
-
return
|
339
|
+
return paramaterized_attribute(method, args.first)
|
308
340
|
|
309
341
|
elsif @ref.attributes.include? KAXChildrenAttribute
|
310
342
|
if (result = search(method, *args, &block)).blank?
|
@@ -319,78 +351,11 @@ class AX::Element
|
|
319
351
|
end
|
320
352
|
end
|
321
353
|
|
322
|
-
|
323
|
-
# @group Notifications
|
324
|
-
|
325
|
-
def notifs
|
326
|
-
@notifs ||= {}
|
327
|
-
end
|
328
|
-
|
329
|
-
##
|
330
|
-
# Register to receive notification of the given event being completed
|
331
|
-
# by the given element.
|
332
|
-
#
|
333
|
-
# {file:docs/Notifications.markdown Notifications} are a way to put
|
334
|
-
# non-polling delays into your scripts.
|
335
|
-
#
|
336
|
-
# Use this method to register to be notified of the specified event in
|
337
|
-
# an application.
|
338
|
-
#
|
339
|
-
# The block is optional. The block will be given the sender of the
|
340
|
-
# notification, which will almost always be `self`, and also the name
|
341
|
-
# of the notification being received. The block should return a
|
342
|
-
# boolean value that decides if the notification received is the
|
343
|
-
# expected one.
|
344
|
-
#
|
345
|
-
# @example
|
346
|
-
#
|
347
|
-
# on_notification(:window_created) { |sender|
|
348
|
-
# puts "#{sender.inspect} sent the ':window_created' notification"
|
349
|
-
# true
|
350
|
-
# }
|
351
|
-
#
|
352
|
-
# @param [#to_s] notif the name of the notification
|
353
|
-
# @yield Validate the notification; the block should return truthy if
|
354
|
-
# the notification received is the expected one and the script can
|
355
|
-
# stop waiting, otherwise should return falsy.
|
356
|
-
# @yieldparam [String] notif the name of the notification
|
357
|
-
# @yieldparam [AXUIElementRef] element the element that sent the notification
|
358
|
-
# @yieldreturn [Boolean]
|
359
|
-
# @return [Array(Observer, String, CFRunLoopSource)]
|
360
|
-
def on_notification name, &block
|
361
|
-
notif = TRANSLATOR.guess_notification name
|
362
|
-
observer = @ref.observer ¬if_callback_for(&block)
|
363
|
-
source = @ref.run_loop_source_for observer
|
364
|
-
@ref.register observer, to_receive: notif
|
365
|
-
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, KCFRunLoopDefaultMode)
|
366
|
-
notifs[name] = [observer, notif, source]
|
367
|
-
end
|
368
|
-
|
369
|
-
def unregister_notification name
|
370
|
-
unless notifs.has_key? name
|
371
|
-
raise ArgumentError, "You have no registrations for #{name}"
|
372
|
-
end
|
373
|
-
_unregister_notification *notifs.delete(name)
|
374
|
-
end
|
375
|
-
|
376
|
-
##
|
377
|
-
# Cancel _all_ notification registrations for this object. Simple and
|
378
|
-
# clean, but a blunt tool at best. This will have to do for the time
|
379
|
-
# being...
|
380
|
-
#
|
381
|
-
# @return [nil]
|
382
|
-
def unregister_all
|
383
|
-
notifs.keys.each do |notif|
|
384
|
-
unregister_notification notif
|
385
|
-
end
|
386
|
-
nil
|
387
|
-
end
|
388
|
-
|
389
354
|
# @endgroup
|
390
355
|
|
391
356
|
|
392
357
|
##
|
393
|
-
#
|
358
|
+
# Get relevant details about the current object.
|
394
359
|
#
|
395
360
|
# @return [String]
|
396
361
|
def inspect
|
@@ -413,6 +378,25 @@ class AX::Element
|
|
413
378
|
inspect
|
414
379
|
end
|
415
380
|
|
381
|
+
##
|
382
|
+
# Get the relevant details about the receiver and also the children
|
383
|
+
# and further descendents of the receiver. Each generation down the
|
384
|
+
# tree will be indented one level further.
|
385
|
+
#
|
386
|
+
# @example
|
387
|
+
#
|
388
|
+
# puts app.inspect_subtree
|
389
|
+
#
|
390
|
+
# @return [String]
|
391
|
+
def inspect_subtree
|
392
|
+
output = self.inspect + "\n"
|
393
|
+
enum = Accessibility::Enumerators::DepthFirst.new self
|
394
|
+
enum.each_with_level do |element, depth|
|
395
|
+
output << "\t"*depth + element.inspect + "\n"
|
396
|
+
end
|
397
|
+
output
|
398
|
+
end
|
399
|
+
|
416
400
|
##
|
417
401
|
# Overriden to respond properly with regards to dynamic attribute
|
418
402
|
# lookups, but will return false for potential implicit searches.
|
@@ -430,22 +414,22 @@ class AX::Element
|
|
430
414
|
#
|
431
415
|
# @return [CGPoint]
|
432
416
|
def to_point
|
433
|
-
size
|
434
|
-
point
|
417
|
+
size = attribute :size
|
418
|
+
point = attribute :position
|
435
419
|
point.x += size.width / 2
|
436
420
|
point.y += size.height / 2
|
437
421
|
point
|
438
422
|
end
|
423
|
+
alias_method :hitpoint, :to_point
|
439
424
|
|
440
425
|
##
|
441
426
|
# Get the bounding rectangle for the element.
|
442
427
|
#
|
443
428
|
# @return [CGRect]
|
444
429
|
def bounds
|
445
|
-
|
446
|
-
size = attribute :size
|
447
|
-
CGRectMake(*point, *size)
|
430
|
+
CGRect.new(attribute(:position), attribute(:size))
|
448
431
|
end
|
432
|
+
alias_method :rect, :bounds
|
449
433
|
|
450
434
|
##
|
451
435
|
# Get the application object for the element.
|
@@ -455,24 +439,31 @@ class AX::Element
|
|
455
439
|
process @ref.application
|
456
440
|
end
|
457
441
|
|
458
|
-
|
459
|
-
# Concept borrowed from `Active Support`. It is used during implicit
|
460
|
-
# searches to determine if searches yielded responses.
|
442
|
+
# (see NilClass#blank?)
|
461
443
|
def blank?
|
462
444
|
false
|
463
445
|
end
|
464
446
|
|
465
447
|
##
|
466
|
-
#
|
448
|
+
# Return whether or not the receiver is "dead".
|
467
449
|
#
|
450
|
+
# A dead element is one that is no longer in the app's view
|
451
|
+
# hierarchy, which is not necessarily related to visibility.
|
452
|
+
def invalid?
|
453
|
+
@ref.role.nil?
|
454
|
+
end
|
455
|
+
|
456
|
+
##
|
468
457
|
# Like {#respond_to?}, this is overriden to include attribute methods.
|
458
|
+
# Though, it does include dynamic predicate methods at the moment.
|
469
459
|
def methods include_super = true, include_objc_super = false
|
470
460
|
super.concat(attributes).concat(parameterized_attributes)
|
471
461
|
end
|
472
462
|
|
473
463
|
##
|
474
|
-
# Overridden so that equality testing would work.
|
475
|
-
#
|
464
|
+
# Overridden so that equality testing would work.
|
465
|
+
#
|
466
|
+
# A hack, but the only sane way I can think of to test for equivalency.
|
476
467
|
def == other
|
477
468
|
@ref == other.instance_variable_get(:@ref)
|
478
469
|
end
|
@@ -482,36 +473,29 @@ class AX::Element
|
|
482
473
|
|
483
474
|
private
|
484
475
|
|
485
|
-
##
|
486
476
|
# @private
|
487
|
-
#
|
488
|
-
# Performance hack.
|
489
|
-
#
|
490
477
|
# @return [String]
|
491
478
|
EQUALS = '='
|
492
479
|
|
493
|
-
|
494
|
-
|
495
|
-
Proc.new do |observer, sender, notif, _|
|
496
|
-
break unless yield(process sender) if block_given?
|
497
|
-
_unregister_notification observer, notif, run_loop_source_for(observer)
|
498
|
-
CFRunLoopStop(CFRunLoopGetCurrent())
|
499
|
-
end
|
500
|
-
end
|
480
|
+
end
|
481
|
+
|
501
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
|
488
|
+
|
489
|
+
# Extensions so checking `#blank?` on search result "just works".
|
490
|
+
class NilClass
|
502
491
|
##
|
503
|
-
#
|
504
|
-
#
|
505
|
-
#
|
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?`.
|
506
495
|
#
|
507
|
-
|
508
|
-
|
509
|
-
|
496
|
+
# This method is used by implicit searching in AXElements to
|
497
|
+
# determine if searches yielded responses.
|
498
|
+
def blank?
|
499
|
+
true
|
510
500
|
end
|
511
|
-
|
512
501
|
end
|
513
|
-
|
514
|
-
|
515
|
-
# Extensions so checking #blank? on search result "just works".
|
516
|
-
class NSArray; alias_method :blank?, :empty? end
|
517
|
-
class NilClass; def blank?; true end end
|
data/lib/ax/radio_button.rb
CHANGED
@@ -13,8 +13,11 @@ class AX::RadioButton < AX::Element
|
|
13
13
|
#
|
14
14
|
# @return [Boolean]
|
15
15
|
def == other
|
16
|
-
|
17
|
-
|
16
|
+
if other.kind_of? NSString
|
17
|
+
attribute(:title) == other
|
18
|
+
else
|
19
|
+
super
|
20
|
+
end
|
18
21
|
end
|
19
22
|
|
20
23
|
end
|
data/lib/ax/row.rb
CHANGED
@@ -2,13 +2,13 @@ require 'ax/element'
|
|
2
2
|
require 'accessibility/qualifier'
|
3
3
|
|
4
4
|
##
|
5
|
-
# UI Element for the row in a table, outline, etc.
|
5
|
+
# UI Element for the row in a table, outline view, etc.
|
6
6
|
class AX::Row < AX::Element
|
7
7
|
|
8
8
|
##
|
9
9
|
# Retrieve the child in a row that corresponds to a specific column.
|
10
10
|
# You must pass filters here in the same way that you would for a
|
11
|
-
# search.
|
11
|
+
# search or wait.
|
12
12
|
#
|
13
13
|
# This is useful for tables where it is difficult to identify which
|
14
14
|
# row item is the one you want based on the row items themselves.
|
data/lib/ax/static_text.rb
CHANGED
@@ -12,8 +12,11 @@ class AX::StaticText < AX::Element
|
|
12
12
|
#
|
13
13
|
# @return [Boolean]
|
14
14
|
def == other
|
15
|
-
|
16
|
-
|
15
|
+
if other.kind_of? NSString
|
16
|
+
attribute(:value) == other
|
17
|
+
else
|
18
|
+
super
|
19
|
+
end
|
17
20
|
end
|
18
21
|
|
19
22
|
end
|
data/lib/ax/systemwide.rb
CHANGED
@@ -23,25 +23,37 @@ class AX::SystemWide < AX::Element
|
|
23
23
|
#
|
24
24
|
# Generate keyboard events by simulating keyboard input.
|
25
25
|
#
|
26
|
-
# See the
|
27
|
-
#
|
26
|
+
# See the
|
27
|
+
# [Keyboarding documentation](http://github.com/Marketcircle/AXElements/wiki/Keyboarding)
|
28
|
+
# for more information on how to format strings.
|
28
29
|
#
|
29
30
|
# @return [Boolean]
|
30
31
|
def type string
|
31
32
|
@ref.post keyboard_events_for string
|
32
33
|
true
|
33
34
|
end
|
35
|
+
alias_method :type_string, :type
|
34
36
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
#
|
42
|
-
|
43
|
-
|
44
|
-
|
37
|
+
##
|
38
|
+
# Press the given modifier key and hold it down while yielding to the
|
39
|
+
# given block.
|
40
|
+
#
|
41
|
+
# @example
|
42
|
+
#
|
43
|
+
# hold_key "\\CONTROL" do
|
44
|
+
# drag_mouse_to point
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# @param [String]
|
48
|
+
# @return [Number,nil]
|
49
|
+
def hold_modifier key
|
50
|
+
code = EventGenerator::CUSTOM[key]
|
51
|
+
raise ArgumentError, "Invalid modifier `#{key}' given" unless code
|
52
|
+
@ref.post [[code, true]]
|
53
|
+
yield
|
54
|
+
ensure # if block raises the button might stuck, so ensure it is released
|
55
|
+
@ref.post [[code,false]] if code
|
56
|
+
code
|
45
57
|
end
|
46
58
|
|
47
59
|
##
|
@@ -1,12 +1,21 @@
|
|
1
1
|
require 'awesome_print'
|
2
2
|
|
3
|
+
##
|
4
|
+
# `AwesomePrint` extension for AXElements.
|
3
5
|
module AwesomePrint::AXElements
|
4
6
|
|
7
|
+
##
|
8
|
+
# Perform the silly `alias_method_chain` stuff that AwesomePrint
|
9
|
+
# expects.
|
5
10
|
def self.included base
|
6
11
|
base.send :alias_method, :cast_without_ax_elements, :cast
|
7
12
|
base.send :alias_method, :cast, :cast_with_ax_elements
|
8
13
|
end
|
9
14
|
|
15
|
+
##
|
16
|
+
# Format {AX::Element} objects for AwesomePrint. For the time
|
17
|
+
# being, just work-around the default AwesomePrint output by
|
18
|
+
# using the default `#inpspect` for {AX::Element}.
|
10
19
|
def cast_with_ax_elements object, type
|
11
20
|
cast = cast_without_ax_elements object, type
|
12
21
|
cast = :ax_element if object.kind_of? ::AX::Element
|
@@ -16,6 +25,10 @@ module AwesomePrint::AXElements
|
|
16
25
|
|
17
26
|
private
|
18
27
|
|
28
|
+
##
|
29
|
+
# Give the awesome output for an {AX::Element} object.
|
30
|
+
#
|
31
|
+
# @return [String]
|
19
32
|
def awesome_ax_element object
|
20
33
|
object.inspect
|
21
34
|
end
|
@@ -2,6 +2,11 @@
|
|
2
2
|
# Workaround for MacRuby ticket #1334
|
3
3
|
class Exception
|
4
4
|
alias_method :original_message, :message
|
5
|
+
#
|
6
|
+
# Override the message method to force the backtrace to be
|
7
|
+
# included.
|
8
|
+
#
|
9
|
+
# @return [String]
|
5
10
|
def message
|
6
11
|
"#{original_message}\n\t#{backtrace.join("\n\t")}"
|
7
12
|
end
|
data/lib/ax_elements.rb
CHANGED
@@ -6,13 +6,14 @@ include Accessibility::DSL
|
|
6
6
|
# The Mac OS X dock application.
|
7
7
|
#
|
8
8
|
# @return [AX::Application]
|
9
|
-
AX::DOCK =
|
9
|
+
AX::DOCK = AX::Application.new('com.apple.dock')
|
10
10
|
|
11
11
|
# Load explicitly defined elements that are optional
|
12
12
|
require 'ax/button'
|
13
13
|
require 'ax/radio_button'
|
14
14
|
require 'ax/row'
|
15
15
|
require 'ax/static_text'
|
16
|
+
require 'ax/pop_up_button'
|
16
17
|
|
17
18
|
# Misc things that we need to load
|
18
19
|
require 'ax_elements/nsarray_compat'
|