Class: Accessibility::Element

Inherits:
Object
  • Object
show all
Defined in:
ext/accessibility/core/core.c,
ext/accessibility/core/core.c,
ext/accessibility/bridge/bridge.c

Element Hierarchy Entry Points collapse

Attributes collapse

Parameterized Attributes collapse

Actions collapse

Misc. collapse

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.application_for(pid) ⇒ Accessibility::Element

Get the application object object for an application given the process identifier (PID) for that application.

Examples:


app = Element.application_for 54743  # => #<Accessibility::Element>

Parameters:

  • pid (Number)

Returns:



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'ext/accessibility/core/core.c', line 48

static
VALUE
rb_acore_application_for(VALUE self, VALUE pid)
{
  NSDate* date = [NSDate date];
  [[NSRunLoop currentRunLoop] runUntilDate:date];
  CFRelease(date);

  pid_t                     the_pid = NUM2PIDT(pid);
  NSRunningApplication* running_app =
    [NSRunningApplication runningApplicationWithProcessIdentifier:the_pid];

  if (running_app) {
    VALUE app = wrap_ref(AXUIElementCreateApplication(the_pid));
    CFRelease(running_app);
    return app;
  }

  rb_raise(
	   rb_eArgError,
	   "pid `%d' must belong to a running application",
	   the_pid
	   );

  return Qnil; // unreachable
}

.element_at(point) ⇒ Accessibility::Element?

Find the top most element at the given point on the screen

This is the same as #element_at except that the check for the topmost element does not care which app the element belongs to.

The coordinates should be specified using the flipped coordinate system (origin is in the top-left, increasing downward and to the right as if reading a book in English).

If more than one element is at the position then the z-order of the elements will be used to determine which is "on top".

This method will safely return nil if there is no UI element at the give point.

Examples:


Element.element_at [453, 200]             # table
Element.element_at CGPoint.new(453, 200)  # table

Parameters:

  • point (#to_point)

Returns:



589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
# File 'ext/accessibility/core/core.c', line 589

static
VALUE
rb_acore_element_at(VALUE self, VALUE point)
{
  if (self == rb_cElement)
    self = rb_acore_system_wide(self);

  AXUIElementRef ref = NULL;
  CGPoint          p = unwrap_point(point);
  AXError       code = AXUIElementCopyElementAtPosition(
							unwrap_ref(self),
							p.x,
							p.y,
							&ref
							);
  switch (code)
    {
    case kAXErrorSuccess:
      return wrap_ref(ref);
    case kAXErrorNoValue:
      return Qnil;
    case kAXErrorInvalidUIElement:
      if (!IS_SYSTEM_WIDE(self))
	return rb_acore_element_at(rb_acore_system_wide(rb_cElement), point);
      else
	return Qnil;
    default:
      return handle_error(self, code);
    }
}

.key_rateNumber

The delay between keyboard events used by #post

The default value is 0.009 (:normal), which should be about 50 characters per second (down and up are separate events).

This is just a magic number from trial and error. Both the repeat interval (NXKeyRepeatInterval) and threshold (NXKeyRepeatThreshold) were tried, but were way too big.

Returns:

  • (Number)


84
85
86
87
88
89
# File 'ext/accessibility/core/core.c', line 84

static
VALUE
rb_acore_key_rate(VALUE self)
{
  return rb_ivar_get(self, ivar_key_rate);
}

.key_rate=(value) ⇒ Object

Set the delay between key events

This value is used by #post to slow down the typing speed so apps do not get overloaded by all the events arriving at the same time.

You can pass either an exact value for sleeping (a Float or Fixnum), or you can use a preset symbol:

  • :very_slow
  • :slow
  • :normal/:default
  • :fast
  • :zomg

The :zomg setting will be too fast in almost all cases, but it is fun to watch.

Parameters:

  • value (Number, Symbol)


92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'ext/accessibility/core/core.c', line 92

static
VALUE
rb_acore_set_key_rate(VALUE self, VALUE rate)
{
  if (TYPE(rate) == T_SYMBOL) {
    ID key_rate = SYM2ID(rate);
    if (key_rate == rate_very_slow)
      rate = DBL2NUM(0.9);
    else if (key_rate == rate_slow)
      rate = DBL2NUM(0.09);
    else if (key_rate == rate_normal || rate == rate_default)
      rate = DBL2NUM(0.009);
    else if (key_rate == rate_fast)
      rate = DBL2NUM(0.0009);
    else if (key_rate == rate_zomg)
      rate = DBL2NUM(0.00009);
    else
      rb_raise(rb_eArgError, "Unknown rate `%s'", rb_id2name(key_rate));
  }
  else {
    rate = rb_funcall(rate, sel_to_f, 0);
  }

  return rb_ivar_set(self, ivar_key_rate, rate);
}

.system_wideAccessibility::Element

Create a new reference to the system wide object

This is very useful when working with the system wide object as caching the system wide reference does not seem to work.

Examples:


system_wide  # => #<Accessibility::Element>

Returns:



76
77
78
79
80
81
# File 'ext/accessibility/core/core.c', line 76

static
VALUE
rb_acore_system_wide(VALUE self)
{
  return wrap_ref(AXUIElementCreateSystemWide());
}

Instance Method Details

#==(other) ⇒ Object



621
622
623
624
625
626
627
628
629
# File 'ext/accessibility/core/core.c', line 621

static
VALUE
rb_acore_equality(VALUE self, VALUE other)
{
  if (CLASS_OF(other) == rb_cElement)
    if (CFEqual(unwrap_ref(self), unwrap_ref(other)))
      return Qtrue;
  return Qfalse;
}

#actionsArray<String>

Get the list of actions that the element can perform

If an element does not have actions, then an empty list will be returned. Dead elements will also return an empty array.

Examples:


button.actions  # => ["AXPress"]

Returns:



463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
# File 'ext/accessibility/core/core.c', line 463

static
VALUE
rb_acore_actions(VALUE self)
{
  VALUE cached_actions = rb_ivar_get(self, ivar_actions);
  if (cached_actions != Qnil)
    return cached_actions;

  CFArrayRef actions = NULL;
  AXError       code = AXUIElementCopyActionNames(unwrap_ref(self), &actions);
  switch (code)
    {
    case kAXErrorSuccess:
      cached_actions = wrap_array_strings(actions);
      rb_ivar_set(self, ivar_actions, cached_actions);
      CFRelease(actions);
      return cached_actions;
    case kAXErrorInvalidUIElement:
      return rb_ary_new();
    default:
      return handle_error(self, code);
    }
}

#applicationAccessibility::Element

Returns the application reference (toplevel element) for the receiver



564
565
566
567
568
569
# File 'ext/accessibility/core/core.c', line 564

static
VALUE
rb_acore_application(VALUE self)
{
  return rb_acore_application_for(rb_cElement, rb_acore_pid(self));
}

#attribute(name) ⇒ Object

Fetch the value for the given attribute from the receiver

CoreFoundation wrapped objects will be unwrapped for you, if you expect to get a CFRange you will be given a Range instead.

As a convention, if the backing element is no longer alive then any attribute value will return nil. If the attribute is not supported by the element then nil will be returned instead. These conventions are debatably necessary, inquire for details.

Examples:

window.attribute 'AXTitle'    # => "HotCocoa Demo"
window.attribute 'AXSize'     # => #<CGSize width=10.0 height=88>
window.attribute 'AXParent'   # => #<Accessibility::Element>
window.attribute 'AXHerpDerp' # => nil

Parameters:



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'ext/accessibility/core/core.c', line 161

static
VALUE
rb_acore_attribute(VALUE self, VALUE name)
{
  CFTypeRef        attr = NULL;
  CFStringRef attr_name = unwrap_string(name);
  AXError          code = AXUIElementCopyAttributeValue(
							unwrap_ref(self),
							attr_name,
							&attr
							);
  CFRelease(attr_name);
  switch (code)
    {
    case kAXErrorSuccess:
      return to_ruby(attr);
    case kAXErrorFailure:
    case kAXErrorNoValue:
    case kAXErrorInvalidUIElement:
    case kAXErrorAttributeUnsupported:
      return Qnil;
    default:
      return handle_error(self, code);
    }
}

#attributesArray<String>

TODO:

Invalid elements do not always raise an error. This is a bug that should be logged with Apple (but I keep procrastinating).

Get the list of attributes for the element

As a convention, this method will return an empty array if the backing element is no longer alive.

Examples:


button.attributes # => ["AXRole", "AXRoleDescription", ...]

Returns:



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'ext/accessibility/core/core.c', line 131

static
VALUE
rb_acore_attributes(VALUE self)
{
  VALUE cached_attrs = rb_ivar_get(self, ivar_attrs);
  if (cached_attrs != Qnil)
    return cached_attrs;

  CFArrayRef attrs = NULL;
  AXError     code = AXUIElementCopyAttributeNames(unwrap_ref(self), &attrs);
  switch (code)
    {
    case kAXErrorSuccess:
      cached_attrs = wrap_array_strings(attrs);
      rb_ivar_set(self, ivar_attrs, cached_attrs);
      CFRelease(attrs);
      return cached_attrs;
    case kAXErrorInvalidUIElement:
      return rb_ary_new();
    default:
      // TODO we should actually allow for a grace period and try again in
      //      every case where would be deferring to the default error handler,
      //      and maybe even in every case where we get a non-zero result code
      //
      //      WE SHOULD HANDLE THINGS LIKE FAILURE AND CANNOT COMPLETE LIKE THIS
      return handle_error(self, code);
    }
}

#childrenArray<Accessibility::Element>

Shortcut for getting the "AXChildren"

The "children" attribute of an element is the general way in which you would navigate downwards through the hierarchy of the views in an app.

An array will always be returned, even if the element is dead or has no children (but the array will be empty in those cases).

Examples:


app.children # => [MenuBar, Window, ...]

Returns:



334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
# File 'ext/accessibility/core/core.c', line 334

static
VALUE
rb_acore_children(VALUE self)
{
  CFTypeRef value = NULL;
  AXError    code = AXUIElementCopyAttributeValue(
						  unwrap_ref(self),
						  kAXChildrenAttribute,
						  &value
						  );
  switch (code)
    {
    case kAXErrorSuccess:
      return wrap_array_refs(value);
    case kAXErrorNoValue:
    case kAXErrorInvalidUIElement:
      return rb_ary_new();
    default:
      return handle_error(self, code);
    }
}

#element_at(point) ⇒ Accessibility::Element?

Find the topmost element at the given point for the receiver's app

If the receiver is the system wide object then the return is the topmost element regardless of application.

The coordinates should be specified using the flipped coordinate system (origin is in the top-left, increasing downward and to the right as if reading a book in English).

If more than one element is at the position then the z-order of the elements will be used to determine which is "on top".

This method will safely return nil if there is no UI element at the give point.

Examples:


Element.system_wide.element_at [453, 200]  # table
app.element_at CGPoint.new(453, 200)       # table

Parameters:

  • point (#to_point)

Returns:



589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
# File 'ext/accessibility/core/core.c', line 589

static
VALUE
rb_acore_element_at(VALUE self, VALUE point)
{
  if (self == rb_cElement)
    self = rb_acore_system_wide(self);

  AXUIElementRef ref = NULL;
  CGPoint          p = unwrap_point(point);
  AXError       code = AXUIElementCopyElementAtPosition(
							unwrap_ref(self),
							p.x,
							p.y,
							&ref
							);
  switch (code)
    {
    case kAXErrorSuccess:
      return wrap_ref(ref);
    case kAXErrorNoValue:
      return Qnil;
    case kAXErrorInvalidUIElement:
      if (!IS_SYSTEM_WIDE(self))
	return rb_acore_element_at(rb_acore_system_wide(rb_cElement), point);
      else
	return Qnil;
    default:
      return handle_error(self, code);
    }
}

#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 the same as visibility; an element that is invalid will not be visible, but an invisible element might still be valid (it depends on the clients implementation of the API).

Returns:

  • (Boolean)


548
549
550
551
552
553
554
555
556
557
558
559
560
561
# File 'ext/accessibility/core/core.c', line 548

static
VALUE
rb_acore_is_invalid(VALUE self)
{
  CFTypeRef value = NULL;
  AXError    code = AXUIElementCopyAttributeValue(
						  unwrap_ref(self),
						  kAXRoleAttribute,
						  &value
						  );
  if (value)
    CFRelease(value);
  return (code == kAXErrorInvalidUIElement ? Qtrue : Qfalse);
}

#key_rateObject

TODO make this meaningful, currently has no effect on calling rb_acore_post



84
85
86
87
88
89
# File 'ext/accessibility/core/core.c', line 84

static
VALUE
rb_acore_key_rate(VALUE self)
{
  return rb_ivar_get(self, ivar_key_rate);
}

#key_rate=(rate) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'ext/accessibility/core/core.c', line 92

static
VALUE
rb_acore_set_key_rate(VALUE self, VALUE rate)
{
  if (TYPE(rate) == T_SYMBOL) {
    ID key_rate = SYM2ID(rate);
    if (key_rate == rate_very_slow)
      rate = DBL2NUM(0.9);
    else if (key_rate == rate_slow)
      rate = DBL2NUM(0.09);
    else if (key_rate == rate_normal || rate == rate_default)
      rate = DBL2NUM(0.009);
    else if (key_rate == rate_fast)
      rate = DBL2NUM(0.0009);
    else if (key_rate == rate_zomg)
      rate = DBL2NUM(0.00009);
    else
      rb_raise(rb_eArgError, "Unknown rate `%s'", rb_id2name(key_rate));
  }
  else {
    rate = rb_funcall(rate, sel_to_f, 0);
  }

  return rb_ivar_set(self, ivar_key_rate, rate);
}

#parameterized_attribute(name, param) ⇒ Object

Fetch the given pramaeterized attribute value for the given parameter

Low level objects, such as Accessibility::Element and Boxed objects, will be unwrapped for you automatically and CFRange objects will be turned into Range objects. Similarly, you do not need to worry about wrapping the parameter as that will be done for you (except for Range objects that use a negative index).

As a convention, if the backing element is no longer alive, or the attribute does not exist, or a system failure occurs then you will receive nil.

Examples:


parameterized_attribute KAXStringForRangeParameterizedAttribute, 1..10
  # => "ello, worl"

Parameters:



435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
# File 'ext/accessibility/core/core.c', line 435

static
VALUE
rb_acore_parameterized_attribute(VALUE self, VALUE name, VALUE parameter)
{
  CFTypeRef       param = to_ax(parameter);
  CFTypeRef        attr = NULL;
  CFStringRef attr_name = unwrap_string(name);
  AXError          code = AXUIElementCopyParameterizedAttributeValue(
								     unwrap_ref(self),
								     attr_name,
								     param,
								     &attr
								     );
  CFRelease(param);
  CFRelease(attr_name);
  switch (code)
    {
    case kAXErrorSuccess:
      return to_ruby(attr);
    case kAXErrorNoValue:
    case kAXErrorInvalidUIElement:
      return Qnil;
    default:
      return handle_error(self, code);
    }
}

#parameterized_attributesArray<String>

Get the list of parameterized attributes for the element

Similar to #attributes, this method will also return an empty array if the element is dead.

Most elements do not have parameterized attributes, but the ones that do, have many.

Examples:


text_area.parameterized_attributes  # => ["AXStringForRange", ...]
app.parameterized_attributes        # => []

Returns:



407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
# File 'ext/accessibility/core/core.c', line 407

static
VALUE
rb_acore_parameterized_attributes(VALUE self)
{
  VALUE cached_attrs = rb_ivar_get(self, ivar_param_attrs);
  if (cached_attrs != Qnil)
    return cached_attrs;

  CFArrayRef attrs = NULL;
  AXError     code = AXUIElementCopyParameterizedAttributeNames(
                                                                unwrap_ref(self),
								&attrs
								);
  switch (code)
    {
    case kAXErrorSuccess:
      cached_attrs = wrap_array_strings(attrs);
      rb_ivar_set(self, ivar_param_attrs, cached_attrs);
      CFRelease(attrs);
      return cached_attrs;
    case kAXErrorInvalidUIElement:
      return rb_ary_new();
    default:
      return handle_error(self, code);
    }
}

#parentAccessibility::Element?

Shortcut for getting the "AXParent"

The "parent" attribute of an element is the general way in which you would navigate upwards through the hierarchy of the views in an app.

An element will be returned if the receiver has a parent, otherwise nil will be returned. Incorrectly implemented elements may also return nil. Usually only something that has a #role of "AXApplication" will return nil since it does not have a parent.

Examples:


window.parent # => app
app.parent # => nil

Returns:



309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
# File 'ext/accessibility/core/core.c', line 309

static
VALUE
rb_acore_parent(VALUE self)
{
  CFTypeRef value = NULL;
  AXError    code = AXUIElementCopyAttributeValue(
						  unwrap_ref(self),
						  kAXParentAttribute,
						  &value
						  );
  switch (code)
    {
    case kAXErrorSuccess:
      return wrap_ref(value);
    case kAXErrorFailure:
    case kAXErrorNoValue:
    case kAXErrorInvalidUIElement:
    case kAXErrorAttributeUnsupported:
      return Qnil;
    default:
      return handle_error(self, code);
    }
}

#perform(action) ⇒ Boolean

Ask the receiver to perform the given action

This method will always return true or raises an exception. Actions should never fail, but there are some extreme edge cases (e.g. out of memory, etc.).

Unlike when reading attributes, performing an action on a dead element will raise an exception.

Examples:


perform KAXPressAction  # => true

Parameters:

Returns:

  • (Boolean)


488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
# File 'ext/accessibility/core/core.c', line 488

static
VALUE
rb_acore_perform(VALUE self, VALUE name)
{
  CFStringRef action = unwrap_string(name);
  AXError       code = AXUIElementPerformAction(unwrap_ref(self), action);

  CFRelease(action);
  switch (code)
    {
    case kAXErrorSuccess:
      return Qtrue;
    case kAXErrorInvalidUIElement:
      return Qfalse;
    default:
      return handle_error(self, code);
    }
}

#pidFixnum

Get the process identifier (PID) of the application of the receiver

This method will return 0 if the element is dead or if the receiver is the the system wide element.

Examples:


window.pid               # => 12345
Element.system_wide.pid  # => 0

Returns:

  • (Fixnum)


377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
# File 'ext/accessibility/core/core.c', line 377

static
VALUE
rb_acore_pid(VALUE self)
{
  VALUE cached_pid = rb_ivar_get(self, ivar_pid);
  if (cached_pid != Qnil)
    return cached_pid;

  pid_t    pid = 0;
  AXError code = AXUIElementGetPid(unwrap_ref(self), &pid);

  switch (code)
    {
    case kAXErrorSuccess:
      break;
    case kAXErrorInvalidUIElement:
      if (IS_SYSTEM_WIDE(self)) {
	pid = 0;
	break;
      }
    default:
      handle_error(self, code);
    }

  cached_pid = PIDT2NUM(pid);
  rb_ivar_set(self, ivar_pid, cached_pid);
  return cached_pid;
}

#post(events) ⇒ self

Post the list of given keyboard events to the receiver

This only applies if the given element is an application object or the system wide object. The focused element will receive the events.

Events could be generated from a string using output from the accessibility_keyboard gem's Accessibility::String#keyboard_events_for method.

Events are number/boolean tuples, where the number is a keycode and the boolean is the keypress state (true is keydown, false is keyup).

You can learn more about keyboard events from the Keyboard Events documentation.

Examples:


include Accessibility::String
events = keyboard_events_for "Hello, world!\n"
app.post events

Parameters:

Returns:

  • (self)


508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
# File 'ext/accessibility/core/core.c', line 508

static
VALUE
rb_acore_post(VALUE self, VALUE events)
{
  events = rb_ary_to_ary(events);
  long length = RARRAY_LEN(events);
  useconds_t sleep_time = NUM2DBL(rb_ivar_get(rb_cElement, ivar_key_rate)) * 100000;

  // CGCharCode key_char = 0; // TODO this value seems to not matter?
  VALUE            pair;
  CGKeyCode virtual_key;
  int         key_state;
  AXError          code;


  for (long i = 0; i < length; i++) {
    pair        = rb_ary_entry(events, i);
    virtual_key = NUM2INT(rb_ary_entry(pair, 0));
    key_state   = rb_ary_entry(pair, 1) == Qtrue ? true : false;
    code        = AXUIElementPostKeyboardEvent(
					       unwrap_ref(self),
					       0,
					       virtual_key,
					       key_state
					       );
    switch (code)
      {
      case kAXErrorSuccess:
	break;
      default:
	handle_error(self, code);
      }

    usleep(sleep_time);
  }

  return self;
}

#roleString?

Shortcut for getting the "AXRole" attribute

The role of an element roughly translates to the class of the object; however this can be thought of as the superclass of the object if the object also has a "AXSubrole" attribute.

Remember that dead elements may return nil for their role.

Examples:


window.role  # => "AXWindow"

Returns:



261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'ext/accessibility/core/core.c', line 261

static
VALUE
rb_acore_role(VALUE self)
{
  CFTypeRef value = NULL;
  AXError    code = AXUIElementCopyAttributeValue(
						  unwrap_ref(self),
						  kAXRoleAttribute,
						  &value
						  );
  switch (code)
    {
    case kAXErrorSuccess:
      return wrap_string(value);
    case kAXErrorNoValue:
    case kAXErrorInvalidUIElement:
      return Qnil;
    default:
      return handle_error(self, code);
    }
}

#set(name, value) ⇒ Object

Set the given value for the given attribute on the receiver

You do not need to worry about wrapping objects first, Range objects will also be automatically converted into CFRange objects (unless they have a negative index) and then wrapped.

This method does not check writability of the attribute you are setting. If you need to check, use #writable? first to check.

Unlike when reading attributes, writing to a dead element, and other error conditions, will raise an exception.

Examples:


set 'AXValue',        "hi"       # => "hi"
set 'AXSize',         [250,250]  # => [250,250]
set 'AXVisibleRange', 0..3       # => 0..3
set 'AXVisibleRange', 1...4      # => 1..3

Parameters:



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'ext/accessibility/core/core.c', line 240

static
VALUE
rb_acore_set(VALUE self, VALUE name, VALUE value)
{
  CFTypeRef    ax_value = to_ax(value);
  CFStringRef attr_name = unwrap_string(name);
  AXError          code = AXUIElementSetAttributeValue(
						       unwrap_ref(self),
						       attr_name,
						       ax_value
						       );
  switch (code)
    {
    case kAXErrorSuccess:
      return value;
    default:
      return handle_error(self, code); // name, value
    }
}

#set_timeout_to(seconds) ⇒ Number

Change the timeout value for the element

The timeout value is mostly effective for apps that are slow to respond to accessibility queries, or if you intend to make a large query (such as thousands of rows in a table).

If you change the timeout on the system wide object, it affets all timeouts.

Setting the global timeout to 0 seconds will reset the timeout value to the system default. The system default timeout value is 6 seconds as of the writing of this documentation, but Apple has not publicly documented this (we had to ask in person at WWDC).

Parameters:

  • seconds (Number)

Returns:

  • (Number)


572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
# File 'ext/accessibility/core/core.c', line 572

static
VALUE
rb_acore_set_timeout_to(VALUE self, VALUE seconds)
{
  float timeout = NUM2DBL(seconds);
  AXError  code = AXUIElementSetMessagingTimeout(unwrap_ref(self), timeout);

  switch (code)
    {
    case kAXErrorSuccess:
      return seconds;
    default:
      return handle_error(self, code); // seconds
    }
}

#size_of(name) ⇒ Number

Note:

It has been observed that some elements may lie with this value. Bugs should be reported to the app developers in those cases. I'm looking at you, Safari!

Get the size of the array that would be returned by calling #attribute

When performance matters, this is much faster than getting the array and asking for the size.

If there is a failure or the backing element is no longer alive, this method will return 0.

Examples:


window.size_of 'AXChildren'  # => 19
table.size_of  'AXRows'      # => 100

Parameters:

Returns:

  • (Number)


188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'ext/accessibility/core/core.c', line 188

static
VALUE
rb_acore_size_of(VALUE self, VALUE name)
{
  CFIndex          size = 0;
  CFStringRef attr_name = unwrap_string(name);
  AXError          code = AXUIElementGetAttributeValueCount(
							    unwrap_ref(self),
							    attr_name,
							    &size
							    );
  CFRelease(attr_name);
  switch (code)
    {
    case kAXErrorSuccess:
      return INT2FIX(size);
    case kAXErrorFailure:
    case kAXErrorNoValue:
    case kAXErrorInvalidUIElement:
      return INT2FIX(0);
    default:
      return handle_error(self, code);
    }
}

#subroleString?

Note:

You might get nil back as the subrole even if the object claims to have a subrole attribute. AXWebArea objects are known to do this. You need to check. :(

Shortcut for getting the "AXSubrole"

The subrole of an element roughly translates to the class of the object, but only if the object has a subrole. If an object does not have a subrole then the class of the object would be the #role.

Examples:

window.subrole    # => "AXDialog"
web_area.subrole  # => nil

Returns:



284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# File 'ext/accessibility/core/core.c', line 284

static
VALUE
rb_acore_subrole(VALUE self)
{
  CFTypeRef value = NULL;
  AXError    code = AXUIElementCopyAttributeValue(
						  unwrap_ref(self),
						  kAXSubroleAttribute,
						  &value
						  );
  switch (code)
    {
    case kAXErrorSuccess:
      return wrap_string((CFStringRef)value);
    case kAXErrorFailure:
    case kAXErrorNoValue:
    case kAXErrorInvalidUIElement:
    case kAXErrorAttributeUnsupported:
      return Qnil;
    default:
      return handle_error(self, code);
    }
}

#valueObject

Shortcut for getting the "AXValue"

Examples:


label.value   # => "Mark Rada"
slider.value  # => 42


357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
# File 'ext/accessibility/core/core.c', line 357

static
VALUE
rb_acore_value(VALUE self)
{
  CFTypeRef value = NULL;
  AXError    code = AXUIElementCopyAttributeValue(
						  unwrap_ref(self),
						  kAXValueAttribute,
						  &value
						  );
  switch (code)
    {
    case kAXErrorSuccess:
      return to_ruby(value);
    default:
      return handle_error(self, code);
    }
}

#writable?(name) ⇒ Boolean

Returns whether or not the given attribute is writable on the reciver

Often, you will want/need to check writability of an attribute before trying to call #set for the attribute.

In case of internal error, or if the element dies, this method will return false.

Examples:


window.writable? 'AXSize'  # => true
window.writable? 'AXTitle' # => false

Parameters:

Returns:

  • (Boolean)


214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'ext/accessibility/core/core.c', line 214

static
VALUE
rb_acore_is_writable(VALUE self, VALUE name)
{
  Boolean        result;
  CFStringRef attr_name = unwrap_string(name);
  AXError          code = AXUIElementIsAttributeSettable(
							unwrap_ref(self),
							attr_name,
							&result
							);
  CFRelease(attr_name);
  switch (code)
    {
    case kAXErrorSuccess:
      return (result ? Qtrue : Qfalse);
    case kAXErrorFailure:
    case kAXErrorNoValue:
    case kAXErrorInvalidUIElement:
      return Qfalse;
    default:
      return handle_error(self, code);
    }
}