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
@@ -30,11 +30,12 @@ module Accessibility::Enumerators
|
|
30
30
|
end
|
31
31
|
|
32
32
|
##
|
33
|
-
# @note Explicitly defined so that escaping at the first found
|
34
|
-
# actually works. Since only a single `break` is
|
35
|
-
# item is found it does not fully escape the
|
36
|
-
# we need to do this
|
37
|
-
# they aren't
|
33
|
+
# @note Explicitly defined so that escaping at the first found
|
34
|
+
# element actually works. Since only a single `break` is
|
35
|
+
# called when an item is found it does not fully escape the
|
36
|
+
# built in implementation. Technically, we need to do this
|
37
|
+
# with other 'escape-early' iteraters, but they aren't
|
38
|
+
# being used...yet.
|
38
39
|
#
|
39
40
|
# Override `Enumerable#find` for performance reasons.
|
40
41
|
def find
|
@@ -60,14 +61,14 @@ module Accessibility::Enumerators
|
|
60
61
|
until stack.empty?
|
61
62
|
current = stack.shift
|
62
63
|
yield current
|
63
|
-
# needed to reverse
|
64
|
+
# needed to reverse, child ordering matters in practice
|
64
65
|
stack.unshift *current.children
|
65
66
|
end
|
66
67
|
end
|
67
68
|
|
68
69
|
##
|
69
|
-
# Walk the UI element tree and yield both the element and the
|
70
|
-
# that the element is at relative to the root.
|
70
|
+
# Walk the UI element tree and yield both the element and the
|
71
|
+
# level that the element is at relative to the root.
|
71
72
|
#
|
72
73
|
# @yieldparam [AX::Element,AXUIElementRef]
|
73
74
|
# @yieldparam [Number]
|
data/lib/accessibility/errors.rb
CHANGED
@@ -1,32 +1,31 @@
|
|
1
|
-
require 'accessibility/debug'
|
2
|
-
|
3
1
|
##
|
4
2
|
# Error raised when an implicit search fails to return a result.
|
5
3
|
class Accessibility::SearchFailure < NoMethodError
|
6
4
|
|
5
|
+
# @param [AX::Element]
|
6
|
+
# @param [#to_s]
|
7
|
+
# @param [Hash{Symbol=>Object}]
|
7
8
|
def initialize searcher, searchee, filters
|
8
9
|
filters = {} unless filters.kind_of? Hash
|
9
10
|
msg = "Could not find `#{pp_searchee searchee, filters}` "
|
10
11
|
msg << "as a child of #{searcher.class}\n"
|
11
12
|
msg << "Element Path:\n\t" << path_to(searcher)
|
12
13
|
# @todo Consider turning this on by default
|
13
|
-
msg << "\nSubtree:\n\
|
14
|
+
msg << "\nSubtree:\n\n" << subtree_for(searcher) if Accessibility.debug?
|
14
15
|
super msg
|
15
16
|
end
|
16
17
|
|
17
18
|
|
18
19
|
private
|
19
20
|
|
21
|
+
# Nice string representation of what was being searched for
|
20
22
|
def pp_searchee searchee, filters
|
21
23
|
Accessibility::Qualifier.new(searchee, filters).describe
|
22
24
|
end
|
23
25
|
|
26
|
+
# Nice string representation of element's path from the application root
|
24
27
|
def path_to element
|
25
|
-
|
26
|
-
end
|
27
|
-
|
28
|
-
def debug searcher
|
29
|
-
Accessibility::Debug.text_subtree(searcher)
|
28
|
+
element.ancestry.map! { |x| x.inspect }.join("\n\t")
|
30
29
|
end
|
31
30
|
|
32
31
|
end
|
@@ -9,13 +9,20 @@ module AX; end
|
|
9
9
|
# Mixin made for processing low level data from AXAPI methods.
|
10
10
|
module Accessibility::Factory
|
11
11
|
|
12
|
+
# @todo This should provide alternate #to_ruby functionality for
|
13
|
+
# the __NSCFType class in order to avoid the overhead of
|
14
|
+
# checking type information (or at least reducing it).
|
15
|
+
# However, it will force the lower level to always wrap
|
16
|
+
# element references; this should be ok most of the time
|
17
|
+
# but makes testing a bit of a pain...hmmm
|
18
|
+
|
12
19
|
##
|
13
|
-
# Processes any given data from an AXAPI
|
14
|
-
# needed. Meant for taking a return value from
|
15
|
-
# and friends.
|
20
|
+
# Processes any given data from an AXAPI function and wraps it if
|
21
|
+
# needed. Meant for taking a return value from
|
22
|
+
# {Accessibility::Core#attribute} and friends.
|
16
23
|
#
|
17
|
-
# Generally, used to process an `
|
18
|
-
#
|
24
|
+
# Generally, used to process an `AXUIElementRef` into a some kind
|
25
|
+
# of {AX::Element} subclass.
|
19
26
|
def process value
|
20
27
|
return nil if value.nil? # CFGetTypeID(nil) crashes runtime
|
21
28
|
case CFGetTypeID(value)
|
@@ -73,7 +80,7 @@ module Accessibility::Factory
|
|
73
80
|
subrole = ref.subrole
|
74
81
|
# Some objects claim to have a subrole but return nil
|
75
82
|
if subrole
|
76
|
-
|
83
|
+
class_for2(TRANSLATOR.unprefix(subrole), role).new ref
|
77
84
|
else
|
78
85
|
class_for(role).new ref
|
79
86
|
end
|
@@ -118,12 +125,12 @@ module Accessibility::Factory
|
|
118
125
|
# @param [#to_s]
|
119
126
|
# @param [#to_s]
|
120
127
|
# @return [Class]
|
121
|
-
def
|
128
|
+
def class_for2 subrole, role
|
122
129
|
# @todo it would be nice if we didn't have to lookup twice
|
123
130
|
if AX.const_defined? subrole, false
|
124
131
|
AX.const_get subrole
|
125
132
|
else
|
126
|
-
|
133
|
+
create_class2 subrole, role
|
127
134
|
end
|
128
135
|
end
|
129
136
|
|
@@ -145,7 +152,7 @@ module Accessibility::Factory
|
|
145
152
|
# @param [#to_s] name
|
146
153
|
# @param [#to_s] superklass
|
147
154
|
# @return [Class]
|
148
|
-
def
|
155
|
+
def create_class2 name, superklass
|
149
156
|
unless AX.const_defined? superklass, false
|
150
157
|
create_class superklass
|
151
158
|
end
|
data/lib/accessibility/graph.rb
CHANGED
@@ -4,11 +4,13 @@
|
|
4
4
|
# an image for the graph.
|
5
5
|
#
|
6
6
|
# You can learn more about generating graphs in the
|
7
|
-
#
|
7
|
+
# [Debugging tutorial](http://github.com/Marketcircle/AXElements/wiki/Debugging).
|
8
|
+
#
|
9
|
+
# [Learn more about GraphViz](http://www.graphviz.org/).
|
8
10
|
class Accessibility::Graph
|
9
11
|
|
10
12
|
##
|
11
|
-
# @todo Graphs could be
|
13
|
+
# @todo Graphs could be nicer looking. That is, nodes could be much
|
12
14
|
# more easily identifiable, by allowing different classes to tell
|
13
15
|
# the node more about itself. A mixin module/protocol should
|
14
16
|
# probably be created, just as with the inspector mixin, and added
|
@@ -35,38 +37,82 @@ class Accessibility::Graph
|
|
35
37
|
|
36
38
|
# @return [String]
|
37
39
|
def to_dot
|
38
|
-
"#{@id} #{identifier}
|
40
|
+
"#{@id} #{identifier} [shape=#{shape}] [style=#{style}] [color=#{colour}]"
|
39
41
|
end
|
40
42
|
|
41
43
|
|
42
44
|
private
|
43
45
|
|
44
|
-
EMPTY_STRING = ''
|
45
|
-
NAMESPACE = '::'
|
46
|
-
|
47
46
|
def identifier
|
48
47
|
klass = @element.class.to_s.split(NAMESPACE).last
|
49
|
-
ident = @element.pp_identifier
|
48
|
+
ident = @element.pp_identifier.dup
|
49
|
+
if ident.length > 12
|
50
|
+
ident = "#{ident[0...12]}..."
|
51
|
+
end
|
52
|
+
ident << '"' if ident[1] == QUOTE && ident[-1] != QUOTE
|
50
53
|
ident.gsub! /"/, '\"'
|
54
|
+
ident.gsub! /\\/, '\\'
|
51
55
|
"[label = \"#{klass}#{ident}\"]"
|
52
56
|
end
|
53
57
|
|
54
58
|
def shape
|
55
|
-
@element.
|
59
|
+
(@element.attribute(:focused) && OCTAGON) ||
|
60
|
+
(@element.actions.empty? && OVAL) ||
|
61
|
+
BOX
|
56
62
|
end
|
57
63
|
|
58
|
-
def
|
59
|
-
|
64
|
+
def style
|
65
|
+
# fill in the node if it is disabled (greyed out effect)
|
66
|
+
if @element.attributes.include?(:enabled)
|
67
|
+
return FILLED unless @element.attribute(:enabled)
|
68
|
+
end
|
69
|
+
# bold if focused and no children
|
70
|
+
if @element.attribute(:focused)
|
71
|
+
return BOLD if @element.size_of(:children).zero?
|
72
|
+
end
|
73
|
+
SOLID
|
60
74
|
end
|
61
75
|
|
62
|
-
def
|
63
|
-
|
76
|
+
def colour
|
77
|
+
if @element.attributes.include?(:enabled)
|
78
|
+
return GREY unless @element.attribute(:enabled)
|
79
|
+
end
|
80
|
+
BLACK
|
64
81
|
end
|
65
82
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
83
|
+
# @private
|
84
|
+
# @return [String]
|
85
|
+
EMPTY_STRING = ''
|
86
|
+
# @private
|
87
|
+
# @return [String]
|
88
|
+
NAMESPACE = '::'
|
89
|
+
# @private
|
90
|
+
# @return [String]
|
91
|
+
QUOTE = '"'
|
92
|
+
# @private
|
93
|
+
# @return [String]
|
94
|
+
OVAL = 'oval'
|
95
|
+
# @private
|
96
|
+
# @return [String]
|
97
|
+
BOX = 'box'
|
98
|
+
# @private
|
99
|
+
# @return [String]
|
100
|
+
OCTAGON = 'doubleoctagon'
|
101
|
+
# @private
|
102
|
+
# @return [String]
|
103
|
+
BOLD = 'bold'
|
104
|
+
# @private
|
105
|
+
# @return [String]
|
106
|
+
FILLED = 'filled'
|
107
|
+
# @private
|
108
|
+
# @return [String]
|
109
|
+
SOLID = 'solid'
|
110
|
+
# @private
|
111
|
+
# @return [String]
|
112
|
+
GREY = 'grey'
|
113
|
+
# @private
|
114
|
+
# @return [String]
|
115
|
+
BLACK = 'black'
|
70
116
|
end
|
71
117
|
|
72
118
|
##
|
@@ -83,8 +129,7 @@ class Accessibility::Graph
|
|
83
129
|
# @param [Accessibility::Graph::Node]
|
84
130
|
# @param [Accessibility::Graph::Node]
|
85
131
|
def initialize head, tail
|
86
|
-
@head = head
|
87
|
-
@tail = tail
|
132
|
+
@head, @tail = head, tail
|
88
133
|
end
|
89
134
|
|
90
135
|
# @return [String]
|
@@ -117,7 +162,7 @@ class Accessibility::Graph
|
|
117
162
|
# exploit the ordering of a breadth-first enumeration to simplify
|
118
163
|
# the creation of edges for the graph. This only works because
|
119
164
|
# the UI hiearchy is a simple tree.
|
120
|
-
@edge_queue = Array.new(root.
|
165
|
+
@edge_queue = Array.new(root.children.size, root_node)
|
121
166
|
end
|
122
167
|
|
123
168
|
##
|
@@ -130,7 +175,8 @@ class Accessibility::Graph
|
|
130
175
|
Accessibility::Enumerators::BreadthFirst.new(nodes.last.element).each do |element|
|
131
176
|
nodes << node = Node.new(element)
|
132
177
|
edges << Edge.new(node, @edge_queue.shift)
|
133
|
-
|
178
|
+
# should use #size_of(:children), but that doesn't in all cases
|
179
|
+
@edge_queue.concat Array.new(element.children.size, node)
|
134
180
|
end
|
135
181
|
end
|
136
182
|
|
@@ -141,9 +187,9 @@ class Accessibility::Graph
|
|
141
187
|
# @return [String]
|
142
188
|
def to_dot
|
143
189
|
graph = "digraph {\n"
|
144
|
-
graph << nodes.map(&:to_dot).join("
|
190
|
+
graph << nodes.map(&:to_dot).join(";\n")
|
145
191
|
graph << "\n\n"
|
146
|
-
graph << edges.map(&:to_dot).join("
|
192
|
+
graph << edges.map(&:to_dot).join(";\n")
|
147
193
|
graph << "\n}\n"
|
148
194
|
end
|
149
195
|
|
@@ -0,0 +1,86 @@
|
|
1
|
+
framework 'AppKit'
|
2
|
+
require 'accessibility/version'
|
3
|
+
|
4
|
+
##
|
5
|
+
# A screen highlighter for debugging. When you initialize a highligter
|
6
|
+
# object it will highlight the given bounds on the screen.
|
7
|
+
#
|
8
|
+
# Highligter objects can have their colour configured at initialization,
|
9
|
+
# and can also have a timeout to automatically stop displaying.
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
#
|
13
|
+
# h = Accessibility::Highlighter.new(CGRectMake(100,100,100,100))
|
14
|
+
# # when you are done...
|
15
|
+
# h.stop
|
16
|
+
#
|
17
|
+
class Accessibility::Highlighter < NSWindow
|
18
|
+
|
19
|
+
# @param [CGRect]
|
20
|
+
# @param [Hash] opts
|
21
|
+
# @option opts [Number] :timeout
|
22
|
+
# @option opts [NSColor] :colour (NSColor.magentaColor)
|
23
|
+
def initialize bounds, opts = {}
|
24
|
+
colour = opts[:colour] || opts[:color] || NSColor.magentaColor
|
25
|
+
|
26
|
+
bounds.flip! # we assume the rect is in the other co-ordinate system
|
27
|
+
|
28
|
+
initWithContentRect bounds,
|
29
|
+
styleMask: NSBorderlessWindowMask,
|
30
|
+
backing: NSBackingStoreBuffered,
|
31
|
+
defer: true
|
32
|
+
setOpaque false
|
33
|
+
setAlphaValue 0.20
|
34
|
+
setLevel NSStatusWindowLevel
|
35
|
+
setBackgroundColor colour
|
36
|
+
setIgnoresMouseEvents true
|
37
|
+
setFrame bounds, display: false
|
38
|
+
makeKeyAndOrderFront NSApp
|
39
|
+
|
40
|
+
if opts.has_key? :timeout
|
41
|
+
Dispatch::Queue.new(queue_id).after opts[:timeout] do
|
42
|
+
self.stop
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Tell the highlighter to stop displaying.
|
49
|
+
#
|
50
|
+
# @return [self]
|
51
|
+
def stop
|
52
|
+
close
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def queue_id
|
59
|
+
"com.marketcircle.axelements.window_killer_#{hash}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
##
|
65
|
+
# AXElements extensions to `CGRect`.
|
66
|
+
class CGRect
|
67
|
+
##
|
68
|
+
# Treats the rect as belonging to one co-ordinate system and then
|
69
|
+
# converts it to the other system.
|
70
|
+
#
|
71
|
+
# This is useful because accessibility API's expect to work with
|
72
|
+
# the flipped co-ordinate system (origin in top left), but AppKit
|
73
|
+
# prefers to use the cartesian co-ordinate system (origin in bottom
|
74
|
+
# left).
|
75
|
+
#
|
76
|
+
# @return [CGRect]
|
77
|
+
def flip!
|
78
|
+
screen_height = NSMaxY(NSScreen.mainScreen.frame)
|
79
|
+
origin.y = screen_height - NSMaxY(self)
|
80
|
+
self
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
# Initialize the shared application so that windows can be created
|
86
|
+
NSApplication.sharedApplication
|
@@ -13,8 +13,8 @@
|
|
13
13
|
module Accessibility::PPInspector
|
14
14
|
|
15
15
|
##
|
16
|
-
# Create an identifier for
|
17
|
-
# make it very easy to identify the element.
|
16
|
+
# Create an identifier for the receiver by using various attributes
|
17
|
+
# that should make it very easy to identify the element.
|
18
18
|
#
|
19
19
|
# @return [String]
|
20
20
|
def pp_identifier
|
@@ -55,7 +55,7 @@ module Accessibility::PPInspector
|
|
55
55
|
|
56
56
|
##
|
57
57
|
# Create a string that succinctly encodes the screen coordinates
|
58
|
-
# of
|
58
|
+
# of the receiver.
|
59
59
|
#
|
60
60
|
# @return [String]
|
61
61
|
def pp_position
|
@@ -69,7 +69,7 @@ module Accessibility::PPInspector
|
|
69
69
|
|
70
70
|
##
|
71
71
|
# Create a string that nicely presents the number of children
|
72
|
-
# that
|
72
|
+
# that the receiver has.
|
73
73
|
#
|
74
74
|
# @return [String]
|
75
75
|
def pp_children
|
@@ -10,7 +10,7 @@ class Accessibility::Qualifier
|
|
10
10
|
# Initialize a qualifier with the kind of object that you want to
|
11
11
|
# qualify and a dictionary of filter criteria. You can optionally
|
12
12
|
# pass a block if your qualification criteria is too complicated
|
13
|
-
# for key/value pairs
|
13
|
+
# for key/value pairs; the blocks return value will be used to
|
14
14
|
# determine if an element qualifies.
|
15
15
|
#
|
16
16
|
# @example
|
@@ -22,7 +22,7 @@ class Accessibility::Qualifier
|
|
22
22
|
# element.children.size > 5 && NSContainsRect(element.bounds, rect)
|
23
23
|
# end
|
24
24
|
#
|
25
|
-
# @param [#to_s]
|
25
|
+
# @param [#to_s]
|
26
26
|
# @param [Hash]
|
27
27
|
# @yield Optional block that can qualify an element
|
28
28
|
def initialize klass, criteria
|
@@ -41,6 +41,10 @@ class Accessibility::Qualifier
|
|
41
41
|
the_right_type?(element) && meets_criteria?(element)
|
42
42
|
end
|
43
43
|
|
44
|
+
##
|
45
|
+
# Return a compact description of the qualifier. If the qualifier
|
46
|
+
# includes a block then a checkmarked box will be included.
|
47
|
+
#
|
44
48
|
# @return [String]
|
45
49
|
def describe
|
46
50
|
"#{@klass}#{@criteria.ax_pp}#{@block ? '[✔]' : ''}"
|
@@ -49,16 +53,14 @@ class Accessibility::Qualifier
|
|
49
53
|
|
50
54
|
private
|
51
55
|
|
52
|
-
##
|
53
56
|
# @private
|
54
|
-
#
|
55
|
-
# Local reference to the {Accessibility::Translator}.
|
56
|
-
#
|
57
57
|
# @return [Accessibility::Translator]
|
58
58
|
TRANSLATOR = Accessibility::Translator.instance
|
59
59
|
|
60
60
|
##
|
61
61
|
# Take a hash of search filters and generate an optimized search
|
62
|
+
# array. This is done to avoid checking types for each call to
|
63
|
+
# {#qualifies?}.
|
62
64
|
#
|
63
65
|
# @param [Hash]
|
64
66
|
def compile criteria
|
@@ -118,11 +120,11 @@ class Accessibility::Qualifier
|
|
118
120
|
end
|
119
121
|
|
120
122
|
def parameterized_match attr, param, regexp, element
|
121
|
-
element.
|
123
|
+
element.parameterized_attribute(attr, param).to_s.match regexp
|
122
124
|
end
|
123
125
|
|
124
126
|
def parameterized_equality attr, param, value, element
|
125
|
-
element.
|
127
|
+
element.parameterized_attribute(attr, param) == value
|
126
128
|
end
|
127
129
|
|
128
130
|
def block_check element
|
@@ -133,7 +135,7 @@ end
|
|
133
135
|
|
134
136
|
|
135
137
|
##
|
136
|
-
#
|
138
|
+
# AXElements extensions to `NSDictionary`.
|
137
139
|
class NSDictionary
|
138
140
|
##
|
139
141
|
# Format the hash for AXElements pretty printing.
|
data/lib/accessibility/string.rb
CHANGED
@@ -26,7 +26,7 @@ module Accessibility::String
|
|
26
26
|
# {Accessibility::String::EventGenerator::CUSTOM}.
|
27
27
|
#
|
28
28
|
# For more details on event generation, read the
|
29
|
-
#
|
29
|
+
# [Keyboarding documentation](http://github.com/Marketcircle/AXElements/wiki/Keyboarding).
|
30
30
|
#
|
31
31
|
# @param [String]
|
32
32
|
# @return [Array<Array(Fixnum,Boolean)>]
|
@@ -43,7 +43,7 @@ module Accessibility::String
|
|
43
43
|
# @example
|
44
44
|
#
|
45
45
|
# Lexer.new("Hai").lex # => ['H','a','i']
|
46
|
-
# Lexer.new("\\
|
46
|
+
# Lexer.new("\\CONTROL").lex # => [["\\CONTROL"]]
|
47
47
|
# Lexer.new("\\COMMAND+a").lex # => [["\\COMMAND", ['a']]]
|
48
48
|
# Lexer.new("One\nTwo").lex # => ['O','n','e',"\n",'T','w','o']
|
49
49
|
#
|
@@ -132,10 +132,13 @@ module Accessibility::String
|
|
132
132
|
end
|
133
133
|
|
134
134
|
# @private
|
135
|
+
# @return [String]
|
135
136
|
SPACE = " "
|
136
137
|
# @private
|
138
|
+
# @return [String]
|
137
139
|
PLUS = "+"
|
138
140
|
# @private
|
141
|
+
# @return [String]
|
139
142
|
CUSTOM_ESCAPE = "\\"
|
140
143
|
end
|
141
144
|
|
@@ -150,8 +153,8 @@ module Accessibility::String
|
|
150
153
|
# # Upper case 'A'
|
151
154
|
# EventGenerator.new(["A"]).generate # => [[56,true],[70,true],[70,false],[56,false]]
|
152
155
|
#
|
153
|
-
# # Press the
|
154
|
-
# EventGenerator.new([["\\
|
156
|
+
# # Press the volume up key
|
157
|
+
# EventGenerator.new([["\\F12"]]).generate # => [[0x6F,true],[0x6F,false]]
|
155
158
|
#
|
156
159
|
# # Hotkey, press and hold command key and then 'a', then release both
|
157
160
|
# EventGenerator.new([["\\CMD",["a"]]]).generate # => [[55,true],[70,true],[70,false],[55,false]]
|
@@ -470,14 +473,19 @@ module Accessibility::String
|
|
470
473
|
end
|
471
474
|
|
472
475
|
# @private
|
476
|
+
# @return [String]
|
473
477
|
EMPTY_STRING = ""
|
474
478
|
# @private
|
479
|
+
# @return [Array(Number,Boolean)]
|
475
480
|
OPTION_DOWN = [58, true]
|
476
481
|
# @private
|
482
|
+
# @return [Array(Number,Boolean)]
|
477
483
|
OPTION_UP = [58, false]
|
478
484
|
# @private
|
485
|
+
# @return [Array(Number,Boolean)]
|
479
486
|
SHIFT_DOWN = [56, true]
|
480
487
|
# @private
|
488
|
+
# @return [Array(Number,Boolean)]
|
481
489
|
SHIFT_UP = [56, false]
|
482
490
|
end
|
483
491
|
|
@@ -16,9 +16,16 @@ end
|
|
16
16
|
|
17
17
|
##
|
18
18
|
# Maintain all the rules for transforming Cocoa constants into something
|
19
|
-
# a little more Rubyish
|
19
|
+
# a little more Rubyish and taking the Rubyish symbols and translating
|
20
|
+
# them back to Cocoa constants.
|
20
21
|
class Accessibility::Translator
|
21
22
|
|
23
|
+
##
|
24
|
+
# Get the singleton instance of the {Accessibility::Translator} class.
|
25
|
+
# This is meant to mimic the important functionality of the
|
26
|
+
# `Singleton` mix-in.
|
27
|
+
#
|
28
|
+
# @return [Accessibility::Translator]
|
22
29
|
def self.instance
|
23
30
|
@instance ||= new
|
24
31
|
end
|
@@ -54,6 +61,14 @@ class Accessibility::Translator
|
|
54
61
|
@unprefixes[key]
|
55
62
|
end
|
56
63
|
|
64
|
+
##
|
65
|
+
# Take an array of Cocoa accessibility constants and return an
|
66
|
+
# array of shortened Ruby symbols.
|
67
|
+
#
|
68
|
+
# @example
|
69
|
+
#
|
70
|
+
# rubyize ["AXRole", "AXTitleUIElement"] # => [:role, :title_ui_element]
|
71
|
+
#
|
57
72
|
# @return [Array<Symbol>]
|
58
73
|
def rubyize keys
|
59
74
|
keys.map { |x| @rubyisms[x] }
|
@@ -102,7 +117,9 @@ class Accessibility::Translator
|
|
102
117
|
|
103
118
|
##
|
104
119
|
# Try to turn an arbitrary symbol into a notification constant, and
|
105
|
-
# then get the value of the constant.
|
120
|
+
# then get the value of the constant. If it cannot be turned into
|
121
|
+
# a notification constant then the original string parameter will
|
122
|
+
# be returned.
|
106
123
|
#
|
107
124
|
# @param [#to_s]
|
108
125
|
# @return [String]
|
@@ -156,19 +173,11 @@ class Accessibility::Translator
|
|
156
173
|
end
|
157
174
|
end
|
158
175
|
|
159
|
-
##
|
160
176
|
# @private
|
161
|
-
#
|
162
|
-
# Cached for performance.
|
163
|
-
#
|
164
177
|
# @return [String]
|
165
178
|
EMPTY_STRING = ''
|
166
179
|
|
167
|
-
##
|
168
180
|
# @private
|
169
|
-
#
|
170
|
-
# Performance hack.
|
171
|
-
#
|
172
181
|
# @return [String]
|
173
182
|
QUESTION_MARK = '?'
|
174
183
|
end
|