AXElements 0.9.0 → 1.0.0.alpha
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 +0 -4
- data/README.markdown +22 -17
- data/Rakefile +1 -1
- data/ext/accessibility/key_coder/extconf.rb +1 -1
- data/ext/accessibility/key_coder/key_coder.c +2 -4
- data/lib/accessibility.rb +3 -3
- data/lib/accessibility/core.rb +948 -0
- data/lib/accessibility/dsl.rb +30 -186
- data/lib/accessibility/enumerators.rb +1 -0
- data/lib/accessibility/factory.rb +78 -134
- data/lib/accessibility/graph.rb +5 -9
- data/lib/accessibility/highlighter.rb +86 -0
- data/lib/accessibility/{pretty_printer.rb → pp_inspector.rb} +4 -3
- data/lib/accessibility/qualifier.rb +3 -5
- data/lib/accessibility/screen_recorder.rb +217 -0
- data/lib/accessibility/statistics.rb +57 -0
- data/lib/accessibility/translator.rb +23 -32
- data/lib/accessibility/version.rb +2 -22
- data/lib/ax/application.rb +20 -159
- data/lib/ax/element.rb +42 -32
- data/lib/ax/scroll_area.rb +5 -6
- data/lib/ax/systemwide.rb +1 -33
- data/lib/ax_elements.rb +1 -9
- data/lib/ax_elements/core_graphics_workaround.rb +5 -0
- data/lib/ax_elements/nsarray_compat.rb +17 -97
- data/lib/ax_elements/vendor/inflection_data.rb +66 -0
- data/lib/ax_elements/vendor/inflections.rb +176 -0
- data/lib/ax_elements/vendor/inflector.rb +306 -0
- data/lib/minitest/ax_elements.rb +180 -0
- data/lib/mouse.rb +227 -0
- data/lib/rspec/expectations/ax_elements.rb +234 -0
- data/rakelib/gem.rake +3 -12
- data/rakelib/test.rake +15 -0
- data/test/helper.rb +20 -10
- data/test/integration/accessibility/test_core.rb +18 -0
- data/test/integration/accessibility/test_dsl.rb +40 -38
- data/test/integration/accessibility/test_enumerators.rb +1 -0
- data/test/integration/accessibility/test_graph.rb +0 -1
- data/test/integration/accessibility/test_qualifier.rb +2 -2
- data/test/integration/ax/test_application.rb +2 -9
- data/test/integration/ax/test_element.rb +0 -40
- data/test/integration/minitest/test_ax_elements.rb +89 -0
- data/test/integration/rspec/expectations/test_ax_elements.rb +102 -0
- data/test/sanity/accessibility/test_factory.rb +2 -2
- data/test/sanity/accessibility/test_highlighter.rb +56 -0
- data/test/sanity/accessibility/{test_pretty_printer.rb → test_pp_inspector.rb} +9 -9
- data/test/sanity/accessibility/test_statistics.rb +57 -0
- data/test/sanity/ax/test_application.rb +1 -16
- data/test/sanity/ax/test_element.rb +2 -2
- data/test/sanity/ax_elements/test_nsobject_inspect.rb +2 -4
- data/test/sanity/minitest/test_ax_elements.rb +17 -0
- data/test/sanity/rspec/expectations/test_ax_elements.rb +15 -0
- data/test/sanity/test_mouse.rb +22 -0
- data/test/test_core.rb +454 -0
- metadata +44 -69
- data/History.markdown +0 -41
- data/lib/accessibility/system_info.rb +0 -230
- data/lib/ax_elements/active_support_selections.rb +0 -10
- data/lib/ax_elements/mri.rb +0 -57
- data/test/sanity/accessibility/test_version.rb +0 -15
data/lib/accessibility/dsl.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
|
3
|
-
require 'active_support/core_ext/object/blank'
|
4
|
-
|
5
3
|
require 'mouse'
|
6
4
|
require 'ax/element'
|
7
5
|
require 'ax/application'
|
@@ -10,8 +8,6 @@ require 'ax/scroll_area'
|
|
10
8
|
require 'ax/menu'
|
11
9
|
require 'accessibility'
|
12
10
|
require 'accessibility/enumerators'
|
13
|
-
require 'accessibility/highlighter'
|
14
|
-
|
15
11
|
|
16
12
|
##
|
17
13
|
# DSL methods for AXElements.
|
@@ -133,7 +129,7 @@ module Accessibility::DSL
|
|
133
129
|
# The normal way to raise an exception.
|
134
130
|
def raise *args
|
135
131
|
arg = args.first
|
136
|
-
arg.kind_of?(AX::Element) ? arg.perform(:raise) :
|
132
|
+
arg.kind_of?(AX::Element) ? arg.perform(:raise) : super(*args)
|
137
133
|
end
|
138
134
|
|
139
135
|
##
|
@@ -251,7 +247,7 @@ module Accessibility::DSL
|
|
251
247
|
# @param path [String,Regexp]
|
252
248
|
# @return [Boolean]
|
253
249
|
def select_menu_item app, *path
|
254
|
-
app.
|
250
|
+
app.select_menu_item *path
|
255
251
|
end
|
256
252
|
|
257
253
|
##
|
@@ -274,7 +270,7 @@ module Accessibility::DSL
|
|
274
270
|
# @param app [AX::Application]
|
275
271
|
# @return [AX::Window]
|
276
272
|
def show_preferences_window_for app
|
277
|
-
app.
|
273
|
+
app.show_preferences_window
|
278
274
|
end
|
279
275
|
|
280
276
|
##
|
@@ -312,31 +308,6 @@ module Accessibility::DSL
|
|
312
308
|
element.ancestor(:menu).scroll_to element
|
313
309
|
end
|
314
310
|
|
315
|
-
##
|
316
|
-
# @note This is a hack to workaround an AXAPI deficiency
|
317
|
-
# @note This method may move or be renamed in the next release, though
|
318
|
-
# it will not be removed
|
319
|
-
#
|
320
|
-
# Find a contextual menu that is open near the mouses current position
|
321
|
-
#
|
322
|
-
# This method assumes that it is being called in the block of a call to
|
323
|
-
# {#right_click}, and that the contextual menu is going to be very close
|
324
|
-
# to the mouse pointer.
|
325
|
-
#
|
326
|
-
# @return [AX::Menu,nil]
|
327
|
-
def contextual_menu
|
328
|
-
# CAST, just like high school trigonometry! :P
|
329
|
-
c, a, s, t = quads = Array.new(4) { Mouse.current_position }
|
330
|
-
c.x -= 10; c.y += 10
|
331
|
-
a.x += 10; a.y += 10
|
332
|
-
s.x += 10; s.y -= 10
|
333
|
-
t.x -= 10; t.y -= 10
|
334
|
-
elements = quads.map { |quad| element_at_point quad }
|
335
|
-
elements.uniq!
|
336
|
-
elements.map! { |el| el.kind_of?(AX::Menu) ? el : el.ancestor(:menu) }
|
337
|
-
elements.find { |element| element.present? }
|
338
|
-
end
|
339
|
-
|
340
311
|
|
341
312
|
# @!group Polling
|
342
313
|
|
@@ -505,7 +476,7 @@ module Accessibility::DSL
|
|
505
476
|
# @option opts [Number] :wait (0.2) in seconds
|
506
477
|
def move_mouse_to arg, opts = {}
|
507
478
|
duration = opts[:duration] || 0.2
|
508
|
-
if Accessibility.debug? && arg.
|
479
|
+
if Accessibility.debug? && arg.respond_to?(:bounds)
|
509
480
|
highlight arg, timeout: duration, color: NSColor.orangeColor
|
510
481
|
end
|
511
482
|
Mouse.move_to arg.to_point, duration
|
@@ -555,27 +526,6 @@ module Accessibility::DSL
|
|
555
526
|
sleep wait
|
556
527
|
end
|
557
528
|
|
558
|
-
##
|
559
|
-
# @todo Need to expose the units option? Would allow scrolling by pixel.
|
560
|
-
#
|
561
|
-
# Horizontally scrolls an arbitrary number of lines at the mouses current
|
562
|
-
# point on the screen
|
563
|
-
#
|
564
|
-
# Use a positive number to scroll left, and a negative number to scroll
|
565
|
-
# right.
|
566
|
-
#
|
567
|
-
# If the second argument is provided then the mouse will move to that
|
568
|
-
# point first; the argument must respond to `#to_point`.
|
569
|
-
#
|
570
|
-
# @param lines [Number]
|
571
|
-
# @param obj [#to_point]
|
572
|
-
# @param wait [Number]
|
573
|
-
def horizontal_scroll lines, obj = nil, wait = 0.1
|
574
|
-
move_mouse_to obj, wait: 0 if obj
|
575
|
-
Mouse.horizontal_scroll lines
|
576
|
-
sleep wait
|
577
|
-
end
|
578
|
-
|
579
529
|
##
|
580
530
|
# Perform a regular click.
|
581
531
|
#
|
@@ -600,30 +550,20 @@ module Accessibility::DSL
|
|
600
550
|
move_mouse_to obj, wait: 0 if obj
|
601
551
|
Mouse.click_down
|
602
552
|
yield if block_given?
|
603
|
-
ensure
|
604
553
|
Mouse.click_up
|
605
554
|
sleep wait
|
606
555
|
end
|
607
556
|
|
608
557
|
##
|
609
|
-
# Perform a right (
|
558
|
+
# Perform a right (aka secondary) click action.
|
610
559
|
#
|
611
560
|
# If an argument is provided then the mouse will move to that point
|
612
561
|
# first; the argument must respond to `#to_point`.
|
613
562
|
#
|
614
|
-
# If a block is given, it will be yielded to between the click down
|
615
|
-
# and click up event. This behaviour is the same as passing a block
|
616
|
-
# to {DSL#click}.
|
617
|
-
#
|
618
|
-
# @yield Optionally take a block that is executed between click down
|
619
|
-
# and click up events.
|
620
563
|
# @param obj [#to_point]
|
621
564
|
def right_click obj = nil, wait = 0.2
|
622
565
|
move_mouse_to obj, wait: 0 if obj
|
623
|
-
Mouse.
|
624
|
-
yield if block_given?
|
625
|
-
ensure
|
626
|
-
Mouse.right_click_up
|
566
|
+
Mouse.right_click
|
627
567
|
sleep wait
|
628
568
|
end
|
629
569
|
alias_method :secondary_click, :right_click
|
@@ -641,110 +581,6 @@ module Accessibility::DSL
|
|
641
581
|
sleep wait
|
642
582
|
end
|
643
583
|
|
644
|
-
##
|
645
|
-
# Perform a triple click action
|
646
|
-
#
|
647
|
-
# If an argument is provided then the mouse will move to that point
|
648
|
-
# first; the argument must respond to `#to_point`.
|
649
|
-
#
|
650
|
-
# @param obj [#to_point]
|
651
|
-
def triple_click obj = nil, wait = 0.2
|
652
|
-
move_mouse_to obj, wait: 0 if obj
|
653
|
-
Mouse.triple_click
|
654
|
-
sleep wait
|
655
|
-
end
|
656
|
-
|
657
|
-
##
|
658
|
-
# Perform a swipe gesture in the given `direction`
|
659
|
-
#
|
660
|
-
# Valid directions are:
|
661
|
-
#
|
662
|
-
# - `:up`
|
663
|
-
# - `:down`
|
664
|
-
# - `:left`
|
665
|
-
# - `:right`
|
666
|
-
#
|
667
|
-
# An optional second argument can be provided. If the argument
|
668
|
-
# is provided then the mouse pointer will move to that point first.
|
669
|
-
#
|
670
|
-
# @example
|
671
|
-
#
|
672
|
-
# swipe :left, safari.web_area
|
673
|
-
#
|
674
|
-
# @param direction [Symbol]
|
675
|
-
# @param obj [#to_point]
|
676
|
-
def swipe direction, obj = nil, wait = 0.2
|
677
|
-
move_mouse_to obj, wait: 0 if obj
|
678
|
-
Mouse.swipe direction
|
679
|
-
sleep wait
|
680
|
-
end
|
681
|
-
|
682
|
-
##
|
683
|
-
# Perform a pinch gesture in the given `direction`
|
684
|
-
#
|
685
|
-
# You can optionally specify the `magnification` factor and
|
686
|
-
# `position` for the pinch event.
|
687
|
-
#*
|
688
|
-
# Available pinch directions are:
|
689
|
-
#
|
690
|
-
# - `:zoom` or `:expand`
|
691
|
-
# - `:unzoom` or `:contract`
|
692
|
-
#
|
693
|
-
# Magnification is a relative magnification setting. A zoom value of
|
694
|
-
# `1.0` means `1.0` more than the current zoom level. `2.0` would be
|
695
|
-
# `2.0` levels higher than the current zoom.
|
696
|
-
#
|
697
|
-
# You can also optionally specify an object/point on screen for the mouse
|
698
|
-
# pointer to be moved to before the gesture begins.
|
699
|
-
#
|
700
|
-
# @param direction [Symbol]
|
701
|
-
# @param magnification [Float]
|
702
|
-
# @param obj [#to_point]
|
703
|
-
def pinch direction, magnification = 1, obj = nil, wait = 0.2
|
704
|
-
move_mouse_to obj, wait: 0 if obj
|
705
|
-
Mouse.pinch direction, magnification
|
706
|
-
sleep wait
|
707
|
-
end
|
708
|
-
|
709
|
-
##
|
710
|
-
# Perform a rotation gesture in the given `direction` the given `angle` degrees
|
711
|
-
#
|
712
|
-
# Possible directions are:
|
713
|
-
#
|
714
|
-
# - `:cw`, ':clockwise`, ':clock_wise` to rotate in the clockwise
|
715
|
-
# direction
|
716
|
-
# - `:ccw`, ':counter_clockwise`, `:counter_clock_wise` to rotate in
|
717
|
-
# the the counter clockwise direction
|
718
|
-
#
|
719
|
-
# The `angle` parameter is a number of degrees to rotate. There are 360
|
720
|
-
# degrees in a full rotation, as you would expect in Euclidian geometry.
|
721
|
-
#
|
722
|
-
# You can also optionally specify an object/point on screen for the mouse
|
723
|
-
# pointer to be moved to before the gesture begins. The movement will
|
724
|
-
# be instantaneous.
|
725
|
-
#
|
726
|
-
# @param direction [Symbol]
|
727
|
-
# @param angle [Float]
|
728
|
-
# @param obj [#to_point]
|
729
|
-
def rotate direction, angle, obj = nil, wait = 0.2
|
730
|
-
move_mouse_to obj, wait: 0 if obj
|
731
|
-
Mouse.rotate direction, angle
|
732
|
-
sleep wait
|
733
|
-
end
|
734
|
-
|
735
|
-
##
|
736
|
-
# Perform a smart magnify (double tap on trackpad)
|
737
|
-
#
|
738
|
-
# You can optionally specify an object/point on the screen where to perform
|
739
|
-
# the smart magnification. The mouse will move to this point first
|
740
|
-
#
|
741
|
-
# @param obj [#to_point]
|
742
|
-
def smart_magnify obj = nil, wait = 0.2
|
743
|
-
move_mouse_to obj, wait: 0 if obj
|
744
|
-
Mouse.smart_magnify
|
745
|
-
sleep wait
|
746
|
-
end
|
747
|
-
|
748
584
|
|
749
585
|
# @!group Debug Helpers
|
750
586
|
|
@@ -775,11 +611,29 @@ module Accessibility::DSL
|
|
775
611
|
# @option opts [NSColor] :colour (NSColor.magentaColor)
|
776
612
|
# @return [Accessibility::Highlighter]
|
777
613
|
def highlight obj, opts = {}
|
614
|
+
require 'accessibility/highlighter'
|
778
615
|
Accessibility::Highlighter.new obj.bounds, opts
|
779
616
|
end
|
780
617
|
|
781
618
|
##
|
782
|
-
# @
|
619
|
+
# @deprecated Use {AX::Element#inspect_subtree} instead.
|
620
|
+
#
|
621
|
+
# Get the dump of the subtree of children and descendants for the given
|
622
|
+
# element. Each generation down the tree will be indented another level,
|
623
|
+
# and each element will be inspected.
|
624
|
+
#
|
625
|
+
# @example
|
626
|
+
#
|
627
|
+
# puts subtree app
|
628
|
+
#
|
629
|
+
# @param element [AX::Element]
|
630
|
+
# @return [String]
|
631
|
+
def subtree element
|
632
|
+
element.inspect_subtree
|
633
|
+
end
|
634
|
+
alias_method :subtree_for, :subtree
|
635
|
+
|
636
|
+
##
|
783
637
|
# @note You will need to have GraphViz command line tools installed
|
784
638
|
# in order for this to work.
|
785
639
|
#
|
@@ -843,26 +697,16 @@ module Accessibility::DSL
|
|
843
697
|
alias_method :capture_screen, :screenshot
|
844
698
|
|
845
699
|
##
|
846
|
-
# See
|
847
|
-
# for details on the screen recording options
|
848
|
-
#
|
849
|
-
# @example
|
850
|
-
#
|
851
|
-
# file = record do
|
852
|
-
# run_tests
|
853
|
-
# end
|
854
|
-
# `open '#{file}'`
|
700
|
+
# See {Accessibility::ScreenRecorder.record} for details.
|
855
701
|
#
|
856
702
|
# @param file [String]
|
857
|
-
# @yield
|
858
|
-
# @yieldparam recorder [ScreenRecorder]
|
859
703
|
# @return [String]
|
860
704
|
def record file = nil, &block
|
861
|
-
require 'screen_recorder'
|
705
|
+
require 'accessibility/screen_recorder'
|
862
706
|
if file
|
863
|
-
ScreenRecorder.record file, &block
|
707
|
+
Accessibility::ScreenRecorder.record file, &block
|
864
708
|
else
|
865
|
-
ScreenRecorder.record &block
|
709
|
+
Accessibility::ScreenRecorder.record &block
|
866
710
|
end
|
867
711
|
end
|
868
712
|
|
@@ -881,7 +725,7 @@ module Accessibility::DSL
|
|
881
725
|
# @param id [String]
|
882
726
|
# @return [AX::Application,nil]
|
883
727
|
def app_with_bundle_identifier id
|
884
|
-
|
728
|
+
Accessibility.application_with_bundle_identifier id
|
885
729
|
end
|
886
730
|
alias_method :app_with_bundle_id, :app_with_bundle_identifier
|
887
731
|
alias_method :launch, :app_with_bundle_identifier
|
@@ -1,27 +1,92 @@
|
|
1
1
|
require 'accessibility/core'
|
2
2
|
require 'accessibility/translator'
|
3
|
+
require 'accessibility/statistics'
|
3
4
|
|
4
5
|
##
|
5
6
|
# Namespace container for all the accessibility objects.
|
6
7
|
module AX; class Element; end end
|
7
8
|
|
8
9
|
|
9
|
-
|
10
|
+
##
|
11
|
+
# Extensions to {Accessibility::Element} for the high level abstraction.
|
12
|
+
# These extensions only make sense in the context of the high level API
|
13
|
+
# and it requires knowledge of both layers, so the code has been placed
|
14
|
+
# in its own file.
|
15
|
+
module Accessibility::Element
|
16
|
+
|
17
|
+
##
|
18
|
+
# @todo Should we handle cases where a subrole has a value of
|
19
|
+
# 'Unknown'? What is the performance impact?
|
20
|
+
#
|
21
|
+
# Wrap the low level wrapper with the appropriate high level wrapper.
|
22
|
+
# This involves determining the proper class in the {AX} namespace,
|
23
|
+
# possibly creating it on demand, and then instantiating the class to
|
24
|
+
# wrap the low level object.
|
25
|
+
#
|
26
|
+
# Some code paths have been unrolled for efficiency. Don't hate player,
|
27
|
+
# hate the game.
|
28
|
+
#
|
29
|
+
# @return [AX::Element]
|
30
|
+
def to_ruby
|
31
|
+
type = AXValueGetType(self)
|
32
|
+
if type.zero?
|
33
|
+
to_element
|
34
|
+
else
|
35
|
+
to_box type
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def to_element
|
42
|
+
STATS.increment :Factory
|
43
|
+
if roll = self.role
|
44
|
+
roll = TRANSLATOR.unprefix roll
|
45
|
+
if attributes.include? KAXSubroleAttribute
|
46
|
+
subroll = self.subrole
|
47
|
+
# Some objects claim to have a subrole but return nil
|
48
|
+
if subroll
|
49
|
+
class_for2(TRANSLATOR.unprefix(subroll), roll).new self
|
50
|
+
else
|
51
|
+
class_for(roll).new self
|
52
|
+
end
|
53
|
+
else
|
54
|
+
class_for(roll).new self
|
55
|
+
end
|
56
|
+
else # failsafe in case object dies before we get the role
|
57
|
+
AX::Element.new self
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_box type
|
62
|
+
STATS.increment :Unwrap
|
63
|
+
ptr = Pointer.new BOX_TYPES[type]
|
64
|
+
AXValueGetValue(self, type, ptr)
|
65
|
+
ptr.value.to_ruby
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
private
|
10
70
|
|
11
71
|
##
|
12
72
|
# @private
|
13
73
|
#
|
14
|
-
#
|
74
|
+
# Reference to the singleton instance of the translator.
|
15
75
|
#
|
16
|
-
# @return [
|
17
|
-
|
76
|
+
# @return [Accessibility::Translator]
|
77
|
+
TRANSLATOR = Accessibility::Translator.instance
|
18
78
|
|
19
79
|
##
|
20
80
|
# @private
|
21
81
|
#
|
22
|
-
#
|
82
|
+
# Serial queue to make sure we only create one class at a time.
|
23
83
|
#
|
24
|
-
#
|
84
|
+
# @return [Dispatch::Queue]
|
85
|
+
CREATE_QUEUE = Dispatch::Queue.new 'com.marketcircle.axelements.create'
|
86
|
+
|
87
|
+
##
|
88
|
+
# Find the class for a given role. If the class does not exist it will
|
89
|
+
# be created.
|
25
90
|
#
|
26
91
|
# @param role [#to_s]
|
27
92
|
# @return [Class]
|
@@ -34,11 +99,8 @@ class << AX
|
|
34
99
|
end
|
35
100
|
|
36
101
|
##
|
37
|
-
#
|
38
|
-
#
|
39
|
-
# Find the class for a given subrole and role
|
40
|
-
#
|
41
|
-
# If the class does not exist it will be created on demand.
|
102
|
+
# Find the class for a given subrole and role. If the class does not
|
103
|
+
# exist it will be created on demand.
|
42
104
|
#
|
43
105
|
# @param subrole [#to_s]
|
44
106
|
# @param role [#to_s]
|
@@ -52,15 +114,13 @@ class << AX
|
|
52
114
|
end
|
53
115
|
|
54
116
|
##
|
55
|
-
#
|
56
|
-
#
|
57
|
-
# Create a class in the {AX} namespace that has {AX::Element} as the
|
58
|
-
# superclass
|
117
|
+
# Create a new class in the {AX} namespace that has {AX::Element}
|
118
|
+
# as the superclass.
|
59
119
|
#
|
60
120
|
# @param name [#to_s]
|
61
121
|
# @return [Class]
|
62
122
|
def create_class name
|
63
|
-
|
123
|
+
CREATE_QUEUE.sync do
|
64
124
|
# re-check now that we are in the critical section
|
65
125
|
@klass = if AX.const_defined? name, false
|
66
126
|
AX.const_get name
|
@@ -73,10 +133,8 @@ class << AX
|
|
73
133
|
end
|
74
134
|
|
75
135
|
##
|
76
|
-
# @private
|
77
|
-
#
|
78
136
|
# Create a new class in the {AX} namesapce that has the given
|
79
|
-
# `superklass` as the superclass
|
137
|
+
# `superklass` as the superclass..
|
80
138
|
#
|
81
139
|
# @param name [#to_s]
|
82
140
|
# @param superklass [#to_s]
|
@@ -85,7 +143,7 @@ class << AX
|
|
85
143
|
unless AX.const_defined? superklass, false
|
86
144
|
create_class superklass
|
87
145
|
end
|
88
|
-
|
146
|
+
CREATE_QUEUE.sync do
|
89
147
|
# re-check now that we are in the critical section
|
90
148
|
@klass = if AX.const_defined? name, false
|
91
149
|
AX.const_get name
|
@@ -98,117 +156,3 @@ class << AX
|
|
98
156
|
end
|
99
157
|
|
100
158
|
end
|
101
|
-
|
102
|
-
|
103
|
-
if on_macruby?
|
104
|
-
|
105
|
-
##
|
106
|
-
# Extensions to {Accessibility::Element} for the higher level abstractions
|
107
|
-
#
|
108
|
-
# These extensions only make sense in the context of the high level API
|
109
|
-
# but needs to be applied on the lower level class, so the code has been
|
110
|
-
# placed in its own file.
|
111
|
-
module Accessibility::Element
|
112
|
-
|
113
|
-
##
|
114
|
-
# @todo Should we handle cases where a subrole has a value of
|
115
|
-
# 'Unknown'? What is the performance impact?
|
116
|
-
#
|
117
|
-
# Wrap the low level wrapper with the appropriate high level wrapper.
|
118
|
-
# This involves determining the proper class in the {AX} namespace,
|
119
|
-
# possibly creating it on demand, and then instantiating the class to
|
120
|
-
# wrap the low level object.
|
121
|
-
#
|
122
|
-
# Some code paths have been unrolled for efficiency. Don't hate player,
|
123
|
-
# hate the game.
|
124
|
-
#
|
125
|
-
# @return [AX::Element]
|
126
|
-
def to_ruby
|
127
|
-
type = AXValueGetType(self)
|
128
|
-
if type.zero?
|
129
|
-
to_element
|
130
|
-
else
|
131
|
-
to_box type
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
|
136
|
-
private
|
137
|
-
|
138
|
-
##
|
139
|
-
# @private
|
140
|
-
#
|
141
|
-
# Reference to the singleton instance of the translator.
|
142
|
-
#
|
143
|
-
# @return [Accessibility::Translator]
|
144
|
-
TRANSLATOR = Accessibility::Translator.instance
|
145
|
-
|
146
|
-
def to_box type
|
147
|
-
ptr = Pointer.new ValueWrapper::BOX_TYPES[type]
|
148
|
-
AXValueGetValue(self, type, ptr)
|
149
|
-
ptr.value.to_ruby
|
150
|
-
end
|
151
|
-
|
152
|
-
def to_element
|
153
|
-
if roll = self.role
|
154
|
-
roll = TRANSLATOR.unprefix roll
|
155
|
-
if attributes.include? KAXSubroleAttribute
|
156
|
-
subroll = self.subrole
|
157
|
-
# Some objects claim to have a subrole but return nil
|
158
|
-
if subroll
|
159
|
-
AX.class_for2(TRANSLATOR.unprefix(subroll), roll).new self
|
160
|
-
else
|
161
|
-
AX.class_for(roll).new self
|
162
|
-
end
|
163
|
-
else
|
164
|
-
AX.class_for(roll).new self
|
165
|
-
end
|
166
|
-
else # failsafe in case object dies before we get the role
|
167
|
-
AX::Element.new self
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
end
|
172
|
-
|
173
|
-
|
174
|
-
else
|
175
|
-
|
176
|
-
|
177
|
-
##
|
178
|
-
# `AXElements` extensions to the `Accessibility::Element` class
|
179
|
-
class Accessibility::Element
|
180
|
-
|
181
|
-
##
|
182
|
-
# Override the default `#to_ruby` so that proper classes are
|
183
|
-
# chosen for each object.
|
184
|
-
#
|
185
|
-
# @return [AX::Element]
|
186
|
-
def to_ruby
|
187
|
-
if roll = self.role
|
188
|
-
roll = TRANSLATOR.unprefix roll
|
189
|
-
if attributes.include? KAXSubroleAttribute
|
190
|
-
subroll = self.subrole
|
191
|
-
# Some objects claim to have a subrole but return nil
|
192
|
-
if subroll
|
193
|
-
AX.class_for2(TRANSLATOR.unprefix(subroll), roll).new self
|
194
|
-
else
|
195
|
-
AX.class_for(roll).new self
|
196
|
-
end
|
197
|
-
else
|
198
|
-
AX.class_for(roll).new self
|
199
|
-
end
|
200
|
-
else # failsafe in case object dies before we get the role
|
201
|
-
AX::Element.new self
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
|
-
##
|
206
|
-
# @private
|
207
|
-
#
|
208
|
-
# Reference to the singleton instance of the translator.
|
209
|
-
#
|
210
|
-
# @return [Accessibility::Translator]
|
211
|
-
TRANSLATOR = Accessibility::Translator.instance
|
212
|
-
end
|
213
|
-
|
214
|
-
end
|