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:



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'ext/accessibility/core/core.c', line 151

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

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

  if (running_app) {
    VALUE app = wrap_ref(AXUIElementCreateApplication(the_pid));
    [running_app release];
      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:



718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
# File 'ext/accessibility/core/core.c', line 718

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)


187
188
189
190
191
192
# File 'ext/accessibility/core/core.c', line 187

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)


195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'ext/accessibility/core/core.c', line 195

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:



179
180
181
182
183
184
# File 'ext/accessibility/core/core.c', line 179

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

Instance Method Details

#==(other) ⇒ Object



750
751
752
753
754
755
756
757
758
# File 'ext/accessibility/core/core.c', line 750

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:



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

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



693
694
695
696
697
698
# File 'ext/accessibility/core/core.c', line 693

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:



264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'ext/accessibility/core/core.c', line 264

static
VALUE
rb_acore_attribute(VALUE self, VALUE name)
{
  VALUE             obj;
  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:
      obj = to_ruby(attr);
      if (TYPE(obj) != T_DATA)
        CFRelease(attr);
      return obj;
    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:



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'ext/accessibility/core/core.c', line 234

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:



451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
# File 'ext/accessibility/core/core.c', line 451

static
VALUE
rb_acore_children(VALUE self)
{
  VALUE       obj;
  CFTypeRef value = NULL;
  AXError    code = AXUIElementCopyAttributeValue(
						  unwrap_ref(self),
						  kAXChildrenAttribute,
						  &value
						  );
  switch (code)
    {
    case kAXErrorSuccess:
      obj = wrap_array_refs(value);
      CFRelease(value);
      return obj;
    case kAXErrorFailure:
    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:



718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
# File 'ext/accessibility/core/core.c', line 718

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)


677
678
679
680
681
682
683
684
685
686
687
688
689
690
# File 'ext/accessibility/core/core.c', line 677

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



187
188
189
190
191
192
# File 'ext/accessibility/core/core.c', line 187

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

#key_rate=(rate) ⇒ Object



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'ext/accessibility/core/core.c', line 195

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:



560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
# File 'ext/accessibility/core/core.c', line 560

static
VALUE
rb_acore_parameterized_attribute(VALUE self, VALUE name, VALUE parameter)
{
  VALUE             obj;
  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:
      obj = to_ruby(attr);
      if (TYPE(obj) != T_DATA)
        CFRelease(attr);
      return obj;
    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:



532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
# File 'ext/accessibility/core/core.c', line 532

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:



426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
# File 'ext/accessibility/core/core.c', line 426

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)


617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
# File 'ext/accessibility/core/core.c', line 617

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)


502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
# File 'ext/accessibility/core/core.c', line 502

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)


637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
# File 'ext/accessibility/core/core.c', line 637

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:



369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
# File 'ext/accessibility/core/core.c', line 369

static
VALUE
rb_acore_role(VALUE self)
{
  VALUE       obj;
  CFTypeRef value = NULL;
  AXError    code = AXUIElementCopyAttributeValue(
						  unwrap_ref(self),
						  kAXRoleAttribute,
						  &value
						  );
  switch (code)
    {
    case kAXErrorSuccess:
      obj = wrap_string(value);
      CFRelease(value);
      return obj;
    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:



347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'ext/accessibility/core/core.c', line 347

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
						       );
  CFRelease(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)


701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
# File 'ext/accessibility/core/core.c', line 701

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)


295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'ext/accessibility/core/core.c', line 295

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:



395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
# File 'ext/accessibility/core/core.c', line 395

static
VALUE
rb_acore_subrole(VALUE self)
{
  VALUE       obj;
  CFTypeRef value = NULL;
  AXError    code = AXUIElementCopyAttributeValue(
						  unwrap_ref(self),
						  kAXSubroleAttribute,
						  &value
						  );
  switch (code)
    {
    case kAXErrorSuccess:
      if (value) {
	obj = wrap_string(value);
	CFRelease(value);
	return obj;
      }
      return Qnil;
    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


478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
# File 'ext/accessibility/core/core.c', line 478

static
VALUE
rb_acore_value(VALUE self)
{
  VALUE       obj;
  CFTypeRef value = NULL;
  AXError    code = AXUIElementCopyAttributeValue(
						  unwrap_ref(self),
						  kAXValueAttribute,
						  &value
						  );
  switch (code)
    {
    case kAXErrorSuccess:
      obj = to_ruby(value);
      if (TYPE(obj) != T_DATA)
        CFRelease(value);
      return obj;
    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)


321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
# File 'ext/accessibility/core/core.c', line 321

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);
    }
}