AXElements 0.6.0beta2 → 0.7.5
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +1 -2
- data/README.markdown +152 -88
- data/Rakefile +8 -103
- data/docs/Debugging.markdown +9 -2
- data/docs/KeyboardEvents.markdown +114 -49
- data/docs/Setting.markdown +1 -0
- data/docs/images/next_version.png +0 -0
- data/ext/accessibility/key_coder/extconf.rb +22 -0
- data/ext/accessibility/key_coder/key_coder.c +113 -0
- data/lib/AXElements.rb +2 -0
- data/lib/accessibility/core.rb +897 -0
- data/lib/accessibility/debug.rb +168 -0
- data/lib/accessibility/dsl.rb +697 -0
- data/lib/accessibility/enumerators.rb +104 -0
- data/lib/accessibility/errors.rb +32 -0
- data/lib/accessibility/factory.rb +153 -0
- data/lib/accessibility/graph.rb +150 -0
- data/lib/{ax_elements/inspector.rb → accessibility/pp_inspector.rb} +39 -28
- data/lib/accessibility/qualifier.rb +158 -0
- data/lib/accessibility/string.rb +494 -0
- data/lib/accessibility/translator.rb +178 -0
- data/lib/accessibility/version.rb +7 -0
- data/lib/accessibility.rb +79 -0
- data/lib/ax/application.rb +234 -0
- data/lib/{ax_elements/elements → ax}/button.rb +2 -0
- data/lib/ax/element.rb +518 -0
- data/lib/{ax_elements/elements → ax}/radio_button.rb +2 -0
- data/lib/ax/row.rb +37 -0
- data/lib/{ax_elements/elements → ax}/static_text.rb +2 -0
- data/lib/ax/systemwide.rb +86 -0
- data/lib/ax_elements/awesome_print.rb +25 -0
- data/lib/ax_elements/exception_workaround.rb +8 -0
- data/lib/ax_elements/nsarray_compat.rb +64 -0
- data/lib/ax_elements/vendor/inflection_data.rb +65 -0
- data/lib/ax_elements/vendor/inflections.rb +172 -0
- data/lib/ax_elements/vendor/inflector.rb +306 -0
- data/lib/ax_elements.rb +14 -25
- data/lib/minitest/ax_elements.rb +112 -12
- data/lib/mouse.rb +72 -46
- data/lib/rspec/expectations/ax_elements.rb +133 -6
- data/rakelib/doc.rake +13 -0
- data/rakelib/ext.rake +61 -0
- data/rakelib/gem.rake +28 -0
- data/rakelib/test.rake +53 -0
- data/test/helper.rb +11 -97
- data/test/integration/accessibility/test_core.rb +18 -0
- data/test/integration/accessibility/test_debug.rb +44 -0
- data/test/integration/accessibility/test_dsl.rb +225 -0
- data/test/integration/accessibility/test_enumerators.rb +122 -0
- data/test/integration/accessibility/test_errors.rb +38 -0
- data/test/integration/accessibility/test_notifications.rb +22 -0
- data/test/integration/accessibility/test_qualifier.rb +148 -0
- data/test/integration/ax/test_application.rb +56 -0
- data/test/integration/ax/test_element.rb +46 -0
- data/test/integration/ax/test_row.rb +23 -0
- data/test/integration/ax_elements/test_nsarray_compat.rb +43 -0
- data/test/integration/minitest/test_ax_elements.rb +98 -0
- data/test/integration/rspec/expectations/test_ax_elements.rb +58 -0
- data/test/integration/test_mouse.rb +35 -0
- data/test/sanity/accessibility/test_core.rb +553 -0
- data/test/sanity/accessibility/test_debug.rb +63 -0
- data/test/sanity/accessibility/test_dsl.rb +75 -0
- data/test/sanity/accessibility/test_errors.rb +10 -0
- data/test/sanity/accessibility/test_factory.rb +88 -0
- data/test/sanity/accessibility/test_pp_inspector.rb +110 -0
- data/test/sanity/accessibility/test_qualifier.rb +13 -0
- data/test/sanity/accessibility/test_string.rb +238 -0
- data/test/sanity/accessibility/test_translator.rb +145 -0
- data/test/sanity/ax/test_application.rb +90 -0
- data/test/sanity/ax/test_element.rb +80 -0
- data/test/sanity/ax/test_systemwide.rb +66 -0
- data/test/sanity/ax_elements/test_nsarray_compat.rb +16 -0
- data/test/sanity/ax_elements/test_nsobject_inspect.rb +11 -0
- data/test/sanity/minitest/test_ax_elements.rb +15 -0
- data/test/sanity/rspec/expectations/test_ax_elements.rb +12 -0
- data/test/sanity/test_ax_elements.rb +10 -0
- data/test/sanity/test_mouse.rb +19 -0
- metadata +111 -93
- data/LICENSE.txt +0 -25
- data/ext/key_coder/extconf.rb +0 -6
- data/ext/key_coder/key_coder.m +0 -77
- data/lib/ax_elements/accessibility/enumerators.rb +0 -104
- data/lib/ax_elements/accessibility/graph.rb +0 -118
- data/lib/ax_elements/accessibility/language.rb +0 -347
- data/lib/ax_elements/accessibility/qualifier.rb +0 -73
- data/lib/ax_elements/accessibility.rb +0 -166
- data/lib/ax_elements/core.rb +0 -541
- data/lib/ax_elements/element.rb +0 -593
- data/lib/ax_elements/elements/application.rb +0 -88
- data/lib/ax_elements/elements/row.rb +0 -30
- data/lib/ax_elements/elements/systemwide.rb +0 -46
- data/lib/ax_elements/macruby_extensions.rb +0 -255
- data/lib/ax_elements/notification.rb +0 -37
- data/lib/ax_elements/version.rb +0 -9
- data/test/elements/test_application.rb +0 -72
- data/test/elements/test_row.rb +0 -27
- data/test/elements/test_systemwide.rb +0 -38
- data/test/test_accessibility.rb +0 -127
- data/test/test_blankness.rb +0 -26
- data/test/test_core.rb +0 -448
- data/test/test_element.rb +0 -939
- data/test/test_enumerators.rb +0 -81
- data/test/test_inspector.rb +0 -130
- data/test/test_language.rb +0 -157
- data/test/test_macruby_extensions.rb +0 -303
- data/test/test_mouse.rb +0 -5
- data/test/test_search_semantics.rb +0 -143
@@ -0,0 +1,104 @@
|
|
1
|
+
##
|
2
|
+
# Namespace for enumerators used to navigate accessibility hierarchies.
|
3
|
+
module Accessibility::Enumerators
|
4
|
+
|
5
|
+
##
|
6
|
+
# Enumerator for visiting each element in a UI hierarchy in breadth
|
7
|
+
# first order.
|
8
|
+
class BreadthFirst
|
9
|
+
include Enumerable
|
10
|
+
|
11
|
+
# @param [AX::Element]
|
12
|
+
def initialize root
|
13
|
+
@root = root
|
14
|
+
end
|
15
|
+
|
16
|
+
##
|
17
|
+
# Semi-lazily iterate through the tree.
|
18
|
+
#
|
19
|
+
# @yieldparam [AX::Element]
|
20
|
+
def each
|
21
|
+
# @todo Lazy-wrap element refs, might make things a bit faster
|
22
|
+
# for fat trees; what is impact on thin trees?
|
23
|
+
# @todo See if we can implement the method in a single loop
|
24
|
+
queue = [@root]
|
25
|
+
until queue.empty?
|
26
|
+
queue.shift.attribute(:children).each do |x|
|
27
|
+
queue << x if x.attributes.include? :children
|
28
|
+
yield x
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# @note Explicitly defined so that escaping at the first found element
|
35
|
+
# actually works. Since only a single `break` is called when an
|
36
|
+
# item is found it does not fully escape the method. Technically,
|
37
|
+
# we need to do this with other 'escape-early' iteraters, but
|
38
|
+
# they aren't being used...yet.
|
39
|
+
#
|
40
|
+
# Override `Enumerable#find` for performance reasons.
|
41
|
+
def find
|
42
|
+
each { |x| return x if yield x }
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Enumerator for visitng each element in a UI hierarchy in
|
49
|
+
# depth first order.
|
50
|
+
class DepthFirst
|
51
|
+
include Enumerable
|
52
|
+
|
53
|
+
# @param [AX::Element]
|
54
|
+
def initialize root
|
55
|
+
@root = root
|
56
|
+
end
|
57
|
+
|
58
|
+
# @yieldparam [AX::Element]
|
59
|
+
def each
|
60
|
+
stack = @root.attribute(:children)
|
61
|
+
until stack.empty?
|
62
|
+
current = stack.shift
|
63
|
+
yield current
|
64
|
+
if current.attributes.include? :children
|
65
|
+
# need to reverse it since child ordering seems to matter in practice
|
66
|
+
stack.unshift *current.attribute(:children)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Walk the UI element tree and yield both the element and the level
|
73
|
+
# that the element is at relative to the root.
|
74
|
+
#
|
75
|
+
# @yieldparam [AX::Element]
|
76
|
+
# @yieldparam [Number]
|
77
|
+
def each_with_level &block
|
78
|
+
# @todo A bit of a hack that I would like to fix one day...
|
79
|
+
@root.attribute(:children).each do |element|
|
80
|
+
recursive_each_with_level element, 1, block
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
##
|
88
|
+
# Recursive implementation of a depth first iterator.
|
89
|
+
#
|
90
|
+
# @param [AX::Element]
|
91
|
+
# @param [Number]
|
92
|
+
# @param [#call]
|
93
|
+
def recursive_each_with_level element, depth, block
|
94
|
+
block.call element, depth
|
95
|
+
if element.respond_to? :children
|
96
|
+
element.attribute(:children).each do |x|
|
97
|
+
recursive_each_with_level x, depth + 1, block
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'accessibility/debug'
|
2
|
+
|
3
|
+
##
|
4
|
+
# Error raised when an implicit search fails to return a result.
|
5
|
+
class Accessibility::SearchFailure < NoMethodError
|
6
|
+
|
7
|
+
def initialize searcher, searchee, filters
|
8
|
+
filters = {} unless filters.kind_of? Hash
|
9
|
+
msg = "Could not find `#{pp_searchee searchee, filters}` "
|
10
|
+
msg << "as a child of #{searcher.class}\n"
|
11
|
+
msg << "Element Path:\n\t" << path_to(searcher)
|
12
|
+
# @todo Consider turning this on by default
|
13
|
+
msg << "\nSubtree:\n\t" << debug(searcher) if Accessibility::Debug.on?
|
14
|
+
super msg
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def pp_searchee searchee, filters
|
21
|
+
Accessibility::Qualifier.new(searchee, filters).describe
|
22
|
+
end
|
23
|
+
|
24
|
+
def path_to element
|
25
|
+
Accessibility::Debug.path(element).map! { |x| x.inspect }.join("\n\t")
|
26
|
+
end
|
27
|
+
|
28
|
+
def debug searcher
|
29
|
+
Accessibility::Debug.text_subtree(searcher)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
require 'accessibility/core'
|
2
|
+
require 'accessibility/translator'
|
3
|
+
|
4
|
+
##
|
5
|
+
# Namespace container for all the accessibility objects.
|
6
|
+
module AX; end
|
7
|
+
|
8
|
+
##
|
9
|
+
# Mixin made for processing low level data from AXAPI methods.
|
10
|
+
module Accessibility::Factory
|
11
|
+
|
12
|
+
##
|
13
|
+
# Processes any given data from an AXAPI method and wraps it if
|
14
|
+
# needed. Meant for taking a return value from {Accessibility::Core#attr:for:}
|
15
|
+
# and friends.
|
16
|
+
#
|
17
|
+
# Generally, used to process an `AXValue` into a `CGPoint` or an
|
18
|
+
# `AXUIElementRef` into some kind of {AX::Element} object.
|
19
|
+
def process value
|
20
|
+
return nil if value.nil? # CFGetTypeID(nil) crashes runtime
|
21
|
+
case CFGetTypeID(value)
|
22
|
+
when ARRAY_TYPE then process_array value
|
23
|
+
when REF_TYPE then process_element value
|
24
|
+
else
|
25
|
+
value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
##
|
33
|
+
# @private
|
34
|
+
#
|
35
|
+
# Reference to the singleton instance of the translator.
|
36
|
+
#
|
37
|
+
# @return [Accessibility::Translator]
|
38
|
+
TRANSLATOR = Accessibility::Translator.instance
|
39
|
+
|
40
|
+
##
|
41
|
+
# @private
|
42
|
+
#
|
43
|
+
# Type ID for `AXUIElementRef` objects.
|
44
|
+
#
|
45
|
+
# @return [Number]
|
46
|
+
REF_TYPE = AXUIElementGetTypeID()
|
47
|
+
|
48
|
+
##
|
49
|
+
# @private
|
50
|
+
#
|
51
|
+
# Type ID for `CFArrayRef` objects.
|
52
|
+
#
|
53
|
+
# @return [Number]
|
54
|
+
ARRAY_TYPE = CFArrayGetTypeID()
|
55
|
+
|
56
|
+
##
|
57
|
+
# @todo Should we handle cases where a subrole has a value of
|
58
|
+
# 'Unknown'? What is the performance impact?
|
59
|
+
#
|
60
|
+
# Takes an `AXUIElementRef` and gives you some kind of wrapped
|
61
|
+
# accessibility object.
|
62
|
+
#
|
63
|
+
# Some code paths have been unrolled for efficiency. Don't hate player,
|
64
|
+
# hate the game.
|
65
|
+
#
|
66
|
+
# @param [AXUIElementRef]
|
67
|
+
# @return [AX::Element]
|
68
|
+
def process_element ref
|
69
|
+
role = TRANSLATOR.unprefix ref.role
|
70
|
+
attrs = ref.attributes
|
71
|
+
klass = if attrs.include? KAXSubroleAttribute
|
72
|
+
subrole = ref.subrole
|
73
|
+
# Some objects claim to have a subrole but return nil
|
74
|
+
if subrole
|
75
|
+
class_for TRANSLATOR.unprefix(subrole), and: role
|
76
|
+
else
|
77
|
+
class_for role
|
78
|
+
end
|
79
|
+
else
|
80
|
+
class_for role
|
81
|
+
end
|
82
|
+
klass.new ref
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# We assume a homogeneous array and only wrap element arrays right now.
|
87
|
+
#
|
88
|
+
# @return [Array]
|
89
|
+
def process_array vals
|
90
|
+
return vals if vals.empty?
|
91
|
+
return vals if CFGetTypeID(vals.first) != REF_TYPE
|
92
|
+
return vals.map { |val| process_element val }
|
93
|
+
end
|
94
|
+
|
95
|
+
##
|
96
|
+
# @todo Consider using {AX.const_missing} instead.
|
97
|
+
#
|
98
|
+
# Find the class for a given role. If the class does not exist it will
|
99
|
+
# be created on demand.
|
100
|
+
#
|
101
|
+
# @param [#to_s]
|
102
|
+
# @return [Class]
|
103
|
+
def class_for role
|
104
|
+
if AX.const_defined? role, false
|
105
|
+
AX.const_get role
|
106
|
+
else
|
107
|
+
create_class role
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
##
|
112
|
+
# Find the class for a given subrole and role. If the class does not
|
113
|
+
# exist it will be created on demand.
|
114
|
+
#
|
115
|
+
# @param [#to_s]
|
116
|
+
# @param [#to_s]
|
117
|
+
# @return [Class]
|
118
|
+
def class_for subrole, and: role
|
119
|
+
# @todo it would be nice if we didn't have to lookup twice
|
120
|
+
if AX.const_defined? subrole, false
|
121
|
+
AX.const_get subrole
|
122
|
+
else
|
123
|
+
create_class subrole, with_superclass: role
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
##
|
128
|
+
# Create a new class in the {AX} namespace that has {AX::Element}
|
129
|
+
# as the superclass.
|
130
|
+
#
|
131
|
+
# @param [#to_s]
|
132
|
+
# @return [Class]
|
133
|
+
def create_class name
|
134
|
+
klass = Class.new AX::Element
|
135
|
+
AX.const_set name, klass
|
136
|
+
end
|
137
|
+
|
138
|
+
##
|
139
|
+
# Create a new class in the {AX} namesapce that has the given
|
140
|
+
# `superklass` as the superclass..
|
141
|
+
#
|
142
|
+
# @param [#to_s] name
|
143
|
+
# @param [#to_s] superklass
|
144
|
+
# @return [Class]
|
145
|
+
def create_class name, with_superclass: superklass
|
146
|
+
unless AX.const_defined? superklass, false
|
147
|
+
create_class superklass
|
148
|
+
end
|
149
|
+
klass = Class.new AX.const_get(superklass)
|
150
|
+
AX.const_set name, klass
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
##
|
2
|
+
# DOT graph generator for AXElements. It can generate the digraph code
|
3
|
+
# for a UI subtree. That code can then be given to GraphViz to generate
|
4
|
+
# an image for the graph.
|
5
|
+
#
|
6
|
+
# You can learn more about generating graphs in the
|
7
|
+
# {file:docs/Debugging.markdown Debugging} tutorial.
|
8
|
+
class Accessibility::Graph
|
9
|
+
|
10
|
+
##
|
11
|
+
# @todo Graphs could be a lot nicer looking. That is, nodes could be much
|
12
|
+
# more easily identifiable, by allowing different classes to tell
|
13
|
+
# the node more about itself. A mixin module/protocol should
|
14
|
+
# probably be created, just as with the inspector mixin, and added
|
15
|
+
# to abstract base and overridden as needed in subclasses. In this
|
16
|
+
# way, an object can be more specific about what shape it is, how
|
17
|
+
# it is coloured, etc.
|
18
|
+
# Reference: http://www.graphviz.org/doc/info/attrs.html
|
19
|
+
#
|
20
|
+
# A node in the UI hierarchy. Used by {Accessibility::Graph} in order
|
21
|
+
# to build Graphviz DOT graphs.
|
22
|
+
class Node
|
23
|
+
|
24
|
+
# @return [String]
|
25
|
+
attr_reader :id
|
26
|
+
|
27
|
+
# @return [AX::Element]
|
28
|
+
attr_reader :element
|
29
|
+
|
30
|
+
# @param [AX::Element]
|
31
|
+
def initialize element
|
32
|
+
@element = element
|
33
|
+
@id = "element_#{element.object_id}"
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [String]
|
37
|
+
def to_dot
|
38
|
+
"#{@id} #{identifier} #{shape}"
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
EMPTY_STRING = ''
|
45
|
+
NAMESPACE = '::'
|
46
|
+
|
47
|
+
def identifier
|
48
|
+
klass = @element.class.to_s.split(NAMESPACE).last
|
49
|
+
ident = @element.pp_identifier
|
50
|
+
ident.gsub! /"/, '\"'
|
51
|
+
"[label = \"#{klass}#{ident}\"]"
|
52
|
+
end
|
53
|
+
|
54
|
+
def shape
|
55
|
+
@element.actions.empty? ? OVAL : BOX
|
56
|
+
end
|
57
|
+
|
58
|
+
def enabled
|
59
|
+
FILL if @element.respond_to?(:enabled) && !@element.enabled?
|
60
|
+
end
|
61
|
+
|
62
|
+
def focus
|
63
|
+
BOLD if @element.respond_to?(:focused) && @element.focused?
|
64
|
+
end
|
65
|
+
|
66
|
+
OVAL = '[shape = oval]'
|
67
|
+
BOX = '[shape = box]'
|
68
|
+
BOLD = '[style = bold]'
|
69
|
+
FILL = '[style = filled] [color = "grey"]'
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
73
|
+
# An edge in the UI hierarchy. Used by {Accessibility::Graph} in order
|
74
|
+
# to build Graphviz DOT graphs.
|
75
|
+
class Edge
|
76
|
+
|
77
|
+
##
|
78
|
+
# The style of arrowhead to use
|
79
|
+
#
|
80
|
+
# @return [String]
|
81
|
+
attr_accessor :style
|
82
|
+
|
83
|
+
# @param [Accessibility::Graph::Node]
|
84
|
+
# @param [Accessibility::Graph::Node]
|
85
|
+
def initialize head, tail
|
86
|
+
@head = head
|
87
|
+
@tail = tail
|
88
|
+
end
|
89
|
+
|
90
|
+
# @return [String]
|
91
|
+
def to_dot
|
92
|
+
arrow = style ? style : 'normal'
|
93
|
+
"#{@head.id} -> #{@tail.id} [arrowhead = #{arrow}]"
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
##
|
100
|
+
# List of nodes in the UI hierarchy.
|
101
|
+
#
|
102
|
+
# @return [Array<Accessibility::Graph::Node>]
|
103
|
+
attr_reader :nodes
|
104
|
+
|
105
|
+
##
|
106
|
+
# List of edges in the graph.
|
107
|
+
#
|
108
|
+
# @return [Array<Accessibility::Graph::Edge>]
|
109
|
+
attr_reader :edges
|
110
|
+
|
111
|
+
# @param [AX::Element]
|
112
|
+
def initialize root
|
113
|
+
root_node = Node.new(root)
|
114
|
+
@nodes = [root_node]
|
115
|
+
@edges = []
|
116
|
+
|
117
|
+
# exploit the ordering of a breadth-first enumeration to simplify
|
118
|
+
# the creation of edges for the graph. This only works because
|
119
|
+
# the UI hiearchy is a simple tree.
|
120
|
+
@edge_queue = Array.new(root.size_of(:children), root_node)
|
121
|
+
end
|
122
|
+
|
123
|
+
##
|
124
|
+
# Construct the list of nodes and edges for the graph.
|
125
|
+
#
|
126
|
+
# The secret sauce is that we create an edge queue to exploit the
|
127
|
+
# breadth first ordering of the enumerator, which makes building the
|
128
|
+
# edges very easy.
|
129
|
+
def build!
|
130
|
+
Accessibility::Enumerators::BreadthFirst.new(nodes.last.element).each do |element|
|
131
|
+
nodes << node = Node.new(element)
|
132
|
+
edges << Edge.new(node, @edge_queue.shift)
|
133
|
+
@edge_queue.concat Array.new(element.size_of(:children), node)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
##
|
138
|
+
# Generate the `dot` graph code. You should take this string and
|
139
|
+
# feed it to the `dot` program to have it generate the graph.
|
140
|
+
#
|
141
|
+
# @return [String]
|
142
|
+
def to_dot
|
143
|
+
graph = "digraph {\n"
|
144
|
+
graph << nodes.map(&:to_dot).join("\n")
|
145
|
+
graph << "\n\n"
|
146
|
+
graph << edges.map(&:to_dot).join("\n")
|
147
|
+
graph << "\n}\n"
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
@@ -12,27 +12,16 @@
|
|
12
12
|
#
|
13
13
|
module Accessibility::PPInspector
|
14
14
|
|
15
|
-
|
16
|
-
protected
|
17
|
-
|
18
|
-
##
|
19
|
-
# Added for backwards compatability with Snow Leopard.
|
20
|
-
#
|
21
|
-
# @return [String]
|
22
|
-
KAXIdentifierAttribute = 'AXIdentifier'.freeze
|
23
|
-
|
24
15
|
##
|
25
|
-
# @todo I feel a bit bad about having such a large method that has
|
26
|
-
# some inefficiencies.
|
27
|
-
#
|
28
16
|
# Create an identifier for `self` using various attributes that should
|
29
17
|
# make it very easy to identify the element.
|
30
18
|
#
|
31
19
|
# @return [String]
|
32
20
|
def pp_identifier
|
33
|
-
#
|
21
|
+
# @todo Break this method up into chunks
|
22
|
+
# @note use, or lack of use, of #inspect is intentional for visual effect
|
34
23
|
|
35
|
-
if attributes.include?
|
24
|
+
if attributes.include? :value
|
36
25
|
val = attribute :value
|
37
26
|
if val.kind_of? NSString
|
38
27
|
return " #{val.inspect}" unless val.empty?
|
@@ -42,27 +31,27 @@ module Accessibility::PPInspector
|
|
42
31
|
end
|
43
32
|
end
|
44
33
|
|
45
|
-
if attributes.include?
|
34
|
+
if attributes.include? :title
|
46
35
|
val = attribute(:title)
|
47
36
|
return " #{val.inspect}" if val && !val.empty?
|
48
37
|
end
|
49
38
|
|
50
|
-
if attributes.include?
|
39
|
+
if attributes.include? :title_ui_element
|
51
40
|
val = attribute :title_ui_element
|
52
|
-
return
|
41
|
+
return " #{val.inspect}" if val
|
53
42
|
end
|
54
43
|
|
55
|
-
if attributes.include?
|
44
|
+
if attributes.include? :description
|
56
45
|
val = attribute(:description).to_s
|
57
|
-
return
|
46
|
+
return " #{val}" unless val.empty?
|
58
47
|
end
|
59
48
|
|
60
|
-
if attributes.include?
|
49
|
+
if attributes.include? :identifier
|
61
50
|
return " id=#{attribute(:identifier)}"
|
62
51
|
end
|
63
52
|
|
64
53
|
# @todo should we have other fallbacks?
|
65
|
-
return
|
54
|
+
return EMPTY_STRING
|
66
55
|
end
|
67
56
|
|
68
57
|
##
|
@@ -75,7 +64,7 @@ module Accessibility::PPInspector
|
|
75
64
|
if position
|
76
65
|
" (#{position.x}, #{position.y})"
|
77
66
|
else
|
78
|
-
|
67
|
+
EMPTY_STRING
|
79
68
|
end
|
80
69
|
end
|
81
70
|
|
@@ -89,9 +78,9 @@ module Accessibility::PPInspector
|
|
89
78
|
if child_count > 1
|
90
79
|
" #{child_count} children"
|
91
80
|
elsif child_count == 1
|
92
|
-
|
81
|
+
ONE_CHILD
|
93
82
|
else # there are some odd edge cases
|
94
|
-
|
83
|
+
EMPTY_STRING
|
95
84
|
end
|
96
85
|
end
|
97
86
|
|
@@ -103,7 +92,7 @@ module Accessibility::PPInspector
|
|
103
92
|
# @param [Symbol]
|
104
93
|
# @return [String]
|
105
94
|
def pp_checkbox attr
|
106
|
-
" #{attr}[#{attribute(attr) ?
|
95
|
+
" #{attr}[#{attribute(attr) ? CHECKMARK : CROSS }]"
|
107
96
|
end
|
108
97
|
|
109
98
|
|
@@ -112,10 +101,32 @@ module Accessibility::PPInspector
|
|
112
101
|
##
|
113
102
|
# @private
|
114
103
|
#
|
115
|
-
#
|
116
|
-
#
|
104
|
+
# Constant string used by {#pp_checkbox}.
|
105
|
+
#
|
106
|
+
# @return [String]
|
107
|
+
CHECKMARK = '✔'
|
108
|
+
|
109
|
+
##
|
110
|
+
# @private
|
111
|
+
#
|
112
|
+
# Constant string used by {#pp_checkbox}.
|
113
|
+
#
|
114
|
+
# @return [String]
|
115
|
+
CROSS = '✘'
|
116
|
+
|
117
|
+
##
|
118
|
+
# @private
|
119
|
+
#
|
120
|
+
# Constant string used by {#pp_children}.
|
117
121
|
#
|
118
122
|
# @return [String]
|
119
|
-
|
123
|
+
ONE_CHILD = ' 1 child'
|
120
124
|
|
125
|
+
##
|
126
|
+
# @private
|
127
|
+
#
|
128
|
+
# Constant used all over the place.
|
129
|
+
#
|
130
|
+
# @return [String]
|
131
|
+
EMPTY_STRING = ''
|
121
132
|
end
|