Class: AX::Element Abstract
- Inherits:
-
Object
- Object
- AX::Element
- Includes:
- Accessibility::PrettyPrinter
- Defined in:
- lib/ax/element.rb,
lib/accessibility/factory.rb
Overview
The abstract base class for all accessibility objects. AX::Element
composes low level AXUIElementRef objects into a more Rubyish
interface.
This abstract base class provides generic functionality that all accessibility objects require.
Direct Known Subclasses
Application, Button, Menu, PopUpButton, RadioButton, Row, ScrollArea, StaticText, SystemWide, TextArea, TextField
Attributes collapse
-
#ancestry(elements = self) ⇒ Array<AX::Element>
(also: #lineage)
Get a list of elements, starting with the receiver and riding the hierarchy up to the top level object (i.e. the Application).
-
#attribute(attr) ⇒ Object
Get the value of an attribute.
-
#attributes ⇒ Array<Symbol>
Cache of available attributes.
-
#children ⇒ Array<AX::Element>
Fetch the children elements for the current element.
-
#description ⇒ String
Get the accessibility description for the element.
-
#pid ⇒ Fixnum
Get the process identifier for the application that the element belongs to.
-
#set(attr, value) ⇒ Object
Set a writable attribute on the element to the given value.
-
#size_of(attr) ⇒ Number
Return the
#size
of an attribute. -
#writable?(attr) ⇒ Boolean
Check whether or not an attribute is writable.
Parameterized Attributes collapse
-
#parameterized_attribute(attr, param) ⇒ Object
Get the value for a parameterized attribute.
-
#parameterized_attributes ⇒ Array<Symbol>
List of available parameterized attributes.
Actions collapse
-
#actions ⇒ Array<Symbol>
List of available actions.
-
#perform(action) ⇒ Boolean
Tell an object to trigger an action.
-
#type(string) ⇒ Boolean
Send keyboard events to the receiver.
Search collapse
-
#ancestor(kind, filters = {}) { ... } ⇒ AX::Element?
Search for an ancestor of the current element.
-
#method_missing(method, *args, &block) ⇒ Object
We use #method_missing to dynamically handle requests to lookup attributes or search for elements in the view hierarchy.
-
#search(kind, filters = {}) { ... } ⇒ AX::Element, ...
Perform a breadth first search through the view hierarchy rooted at the current element.
Instance Method Summary collapse
-
#==(other) ⇒ Object
(also: #eql?, #equal?)
Overridden so that equality testing would work.
-
#application ⇒ AX::Application
Get the application object for the element.
- #blank? ⇒ Boolean
-
#bounds ⇒ CGRect
(also: #to_rect)
Get the bounding rectangle for the element.
-
#initialize(ref) ⇒ Element
constructor
A new instance of Element.
-
#inspect ⇒ String
Get relevant details about the current object.
-
#inspect_subtree ⇒ String
Get the relevant details about the receiver and also the children and further descendents of the receiver.
-
#invalid? ⇒ Boolean
Return whether or not the receiver is "dead".
-
#methods(include_super = true) ⇒ Object
Like #respond_to?, this is overriden to include attribute methods.
-
#respond_to?(name) ⇒ Boolean
Overriden to respond properly with regards to dynamic attribute lookups, but will return false for potential implicit searches.
- #to_h ⇒ Hash{Symbol=>Object}
-
#to_point ⇒ CGPoint
(also: #hitpoint)
Get the center point of the element.
-
#to_s ⇒ String
An "alias" for #inspect.
Methods included from Accessibility::PrettyPrinter
#pp_checkbox, #pp_children, #pp_enabled, #pp_focused, #pp_identifier, #pp_position
Constructor Details
#initialize(ref) ⇒ Element
Returns a new instance of Element.
26 27 28 |
# File 'lib/ax/element.rb', line 26 def initialize ref @ref = ref end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *args, &block) ⇒ Object
We use #method_missing to dynamically handle requests to lookup attributes or search for elements in the view hierarchy. An attribute lookup is always tried first, followed by a parameterized attribute lookup, and then finally a search.
Failing all lookups, this method calls super
, which will probably
raise an exception; however, most elements have children and so it
is more likely that you will get an Accessibility::SearchFailure
in cases where you sholud get a NoMethodError
.
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 |
# File 'lib/ax/element.rb', line 353 def method_missing method, *args, &block return set(method.chomp(EQUALS), args.first) if method[-1] == EQUALS key = TRANSLATOR.cocoaify method if @ref.attributes.include? key return attribute(method) elsif @ref.parameterized_attributes.include? key return parameterized_attribute(method, args.first) elsif @ref.attributes.include? KAXChildrenAttribute if (result = search(method, *args, &block)).blank? raise Accessibility::SearchFailure.new(self, method, args.first, &block) else return result end else super end end |
Instance Method Details
#==(other) ⇒ Object Also known as: eql?, equal?
Overridden so that equality testing would work.
A hack, but the only sane way I can think of to test for equivalency.
501 502 503 |
# File 'lib/ax/element.rb', line 501 def == other @ref == other.instance_variable_get(:@ref) end |
#actions ⇒ Array<Symbol>
List of available actions.
207 208 209 |
# File 'lib/ax/element.rb', line 207 def actions @actions ||= TRANSLATOR.rubyize @ref.actions end |
#ancestor(kind, filters = {}) { ... } ⇒ AX::Element?
Search for an ancestor of the current element.
As the opposite of #search, this also takes filters, and can be used to find a specific ancestor for the current element.
Returns nil
if no ancestor is found.
294 295 296 297 298 299 300 301 302 |
# File 'lib/ax/element.rb', line 294 def ancestor kind, filters = {}, &block qualifier = Accessibility::Qualifier.new(kind, filters, &block) element = self until qualifier.qualifies? element element = element.attribute :parent break unless element end element end |
#ancestry(elements = self) ⇒ Array<AX::Element> Also known as: lineage
Get a list of elements, starting with the receiver and riding the hierarchy up to the top level object (i.e. the Application)
91 92 93 94 95 96 97 98 99 |
# File 'lib/ax/element.rb', line 91 def ancestry elements = self elements = Array(elements) element = elements.last if element.attributes.include? :parent ancestry(elements << element.attribute(:parent)) else elements end end |
#application ⇒ AX::Application
Get the application object for the element.
464 465 466 |
# File 'lib/ax/element.rb', line 464 def application @ref.application.to_ruby end |
#attribute(attr) ⇒ Object
Get the value of an attribute. This method will return nil
if
the attribute does not have a value or if the element is dead. The
execption to the rule is that the :children
attribute will always
return an array unless the element does not have the :children
attribute.
57 58 59 |
# File 'lib/ax/element.rb', line 57 def attribute attr @ref.attribute(TRANSLATOR.cocoaify(attr)).to_ruby end |
#attributes ⇒ Array<Symbol>
Cache of available attributes.
41 42 43 |
# File 'lib/ax/element.rb', line 41 def attributes @attrs ||= TRANSLATOR.rubyize @ref.attributes end |
#blank? ⇒ Boolean
469 470 471 |
# File 'lib/ax/element.rb', line 469 def blank? false end |
#bounds ⇒ CGRect Also known as: to_rect
Get the bounding rectangle for the element.
455 456 457 |
# File 'lib/ax/element.rb', line 455 def bounds CGRect.new(attribute(:position), attribute(:size)) end |
#children ⇒ Array<AX::Element>
Fetch the children elements for the current element.
76 77 78 |
# File 'lib/ax/element.rb', line 76 def children @ref.children.to_ruby end |
#description ⇒ String
Get the accessibility description for the element.
This overrides the inherited NSObject#description
. If you want a
description of the object then you should use #inspect instead.
68 69 70 |
# File 'lib/ax/element.rb', line 68 def description attribute(:description).to_ruby end |
#inspect ⇒ String
Get relevant details about the current object.
383 384 385 386 387 |
# File 'lib/ax/element.rb', line 383 def inspect "#<#{self.class}" << pp_identifier.to_s << pp_position << pp_children << pp_enabled << pp_focused << '>' end |
#inspect_subtree ⇒ String
Get the relevant details about the receiver and also the children and further descendents of the receiver. Each generation down the tree will be indented one level further.
417 418 419 420 421 422 423 424 |
# File 'lib/ax/element.rb', line 417 def inspect_subtree output = self.inspect + "\n" enum = Accessibility::Enumerators::DepthFirst.new self enum.each_with_level do |element, depth| output << "\t"*depth + element.inspect + "\n" end output end |
#invalid? ⇒ Boolean
Return whether or not the receiver is "dead".
A dead element is one that is no longer in the app's view hierarchy. This is not directly related to visibility, but an element that is invalid will not be visible, but an invisible element might not be invalid.
480 481 482 |
# File 'lib/ax/element.rb', line 480 def invalid? @ref.invalid? end |
#methods(include_super = true) ⇒ Object
Like #respond_to?, this is overriden to include attribute methods. Though, it does include dynamic predicate methods at the moment.
488 489 490 |
# File 'lib/ax/element.rb', line 488 def methods include_super = true, include_objc_super = false super.concat(attributes).concat(parameterized_attributes) end |
#parameterized_attribute(attr, param) ⇒ Object
Get the value for a parameterized attribute.
189 190 191 192 |
# File 'lib/ax/element.rb', line 189 def parameterized_attribute attr, param param = param.relative_to(@ref.value.size) if param.kind_of? Range @ref.parameterized_attribute(TRANSLATOR.cocoaify(attr), param).to_ruby end |
#parameterized_attributes ⇒ Array<Symbol>
List of available parameterized attributes. Most elements have no parameterized attributes, but the ones that do have many.
176 177 178 |
# File 'lib/ax/element.rb', line 176 def parameterized_attributes @param_attrs ||= TRANSLATOR.rubyize @ref.parameterized_attributes end |
#perform(action) ⇒ Boolean
Tell an object to trigger an action.
For instance, you can tell a button to call the same method that would be called when pressing a button, except that the mouse will not move over to the button to press it, nor will the keyboard be used.
226 227 228 |
# File 'lib/ax/element.rb', line 226 def perform action @ref.perform TRANSLATOR.cocoaify action end |
#pid ⇒ Fixnum
Get the process identifier for the application that the element belongs to.
111 112 113 |
# File 'lib/ax/element.rb', line 111 def pid @ref.pid end |
#respond_to?(name) ⇒ Boolean
Overriden to respond properly with regards to dynamic attribute lookups, but will return false for potential implicit searches.
This does not work for predicate methods at the moment.
431 432 433 434 435 436 |
# File 'lib/ax/element.rb', line 431 def respond_to? name key = TRANSLATOR.cocoaify name.chomp(EQUALS) @ref.attributes.include?(key) || @ref.parameterized_attributes.include?(key) || super end |
#search(kind, filters = {}) { ... } ⇒ AX::Element, ...
Perform a breadth first search through the view hierarchy rooted at the current element. If you are concerned about the return value of this method, you can call #blank? on the return object.
See the Searching wiki for the details on search semantics.
265 266 267 268 269 270 271 272 273 274 275 |
# File 'lib/ax/element.rb', line 265 def search kind, filters = {}, &block kind = kind.to_s qualifier = Accessibility::Qualifier.new(kind, filters, &block) tree = Accessibility::Enumerators::BreadthFirst.new(self) if TRANSLATOR.singularize(kind) == kind tree.find { |element| qualifier.qualifies? element } else tree.find_all { |element| qualifier.qualifies? element } end end |
#set(attr, value) ⇒ Object
Set a writable attribute on the element to the given value.
155 156 157 158 159 160 161 |
# File 'lib/ax/element.rb', line 155 def set attr, value unless writable? attr raise ArgumentError, "#{attr} is read-only for #{inspect}" end value = value.relative_to(@ref.value.size) if value.kind_of? Range @ref.set TRANSLATOR.cocoaify(attr), value end |
#size_of(attr) ⇒ Number
Return the #size
of an attribute. This only works for attributes
that are a collection. This exists because it is much more
efficient to find out how many children
exist using this API
instead of getting the children array and asking for the size.
128 129 130 |
# File 'lib/ax/element.rb', line 128 def size_of attr @ref.size_of TRANSLATOR.cocoaify attr end |
#to_h ⇒ Hash{Symbol=>Object}
403 404 405 |
# File 'lib/ax/element.rb', line 403 def to_h Hash[attributes.zip attributes.map { |attr| attribute(attr) }] end |
#to_point ⇒ CGPoint Also known as: hitpoint
Get the center point of the element.
442 443 444 445 446 447 448 |
# File 'lib/ax/element.rb', line 442 def to_point size = attribute :size point = attribute :position point.x += size.width / 2 point.y += size.height / 2 point end |
#to_s ⇒ String
Since #inspect
is often overridden by subclasses, this cannot
be an alias.
An "alias" for #inspect.
396 397 398 |
# File 'lib/ax/element.rb', line 396 def to_s inspect end |
#type(string) ⇒ Boolean
As of OS X 10.9 (Sea Lion), it is no longer possible to send keyboard events directly to an element. What we have here is only an approximation.
Send keyboard events to the receiver.
239 240 241 242 243 244 |
# File 'lib/ax/element.rb', line 239 def type string set_focus_to self unless focused? keyboard_events_for(string).each do |event| KeyCoder.post_event event end end |
#writable?(attr) ⇒ Boolean
Check whether or not an attribute is writable.
141 142 143 |
# File 'lib/ax/element.rb', line 141 def writable? attr @ref.writable? TRANSLATOR.cocoaify attr end |