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/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'
|