Accessibility/SoftFocus

From MozillaWiki
< Accessibility
Revision as of 09:45, 17 November 2011 by TREVOR (talk | contribs)
Jump to navigation Jump to search

Background

It is useful to have a focus-like state that is exclusive to accessibility and is independant of the DOM state. From the user's perspective this allows reviewing a document or web application without directly affecting its state, like moving the caret or cycling through focusable elements. This is useful in mobile, for example a user could navigate to a text entry without having it capture input. Only if the user chooses to enter text they would "activate" the text entry which would bring up an on screen keyboard, etc. This feature would also allow easier navigation of content.

Note: From what I gathered from Alex's input in Toronto, he thinks it would be useful to not limit this to a single node at a time, but to allow multiple focal points. It might pay off in the long term as this would be more future proof, and allow other use cases such as multi-region magnifier or interesting features in cognitive disablity ATs. This introduces a layer of complexity (pivots); nonetheles, the usage should not get complicated, we would just always used a pivot index of 0.

Pivots

A pivot (working title) is a focal point. Typically you would only have one pivot that would change depending on where the user is exploring. But you could have multiple pivots each pointing to something else and changing independantly.

nsIAccessible

interface nsIAccessible : nsISupports
{
  ...
  /***
   * Take the given pivot's soft focus.
   *
   * @param pivotIndex  The pivot of which to take focus. If the given index is -1 or out
   *                    of range (pivotIndex >= number of pivots), create a new pivot.
   * @return            The index of the affected pivot.
   */
  long takeSoftFocus(in long pivotIndex);
}

State

An accessible that is soft focused would have a state of EXT_STATE_SOFT_FOCUSED.

Event

When an accessible gets or loses focus, an EVENT_STATE_CHANGED is fired.

it seems like it is important for event consumer to know which pivit changed, but I'm not really sure how to comunicate this best.

nsIAccessibleDocument

interface nsIAccessibleDocument : nsISupports
{
  ...
  /***
   * The collection of tracked soft focuses. The index in the array matches the pivot's
   * index.
   */
  readonly attribute nsIArray softFocusPivots;

  /***
   * Remove soft focus state from accessible on the given pivot. No-op if no
   * accessible is focused on that pivot. On a successful call the
   * nsIAccessibleDocument.softFocusPivots element at the given pivot index will
   * be set to null.
   *
   * @param pivotIndex  The pivot of which to blur focus.
   */
  void blurSoftFocus(in PRInt32 pivotIndex);

does a negative index make sense?

}

nsIAccessibleText

In addition to soft focus, an accessible that supports nsIAccessibleText could have a soft text selection that is independant of any actual selection in the document.

interface nsIAccessibleText : nsISupports
{
  ...
  /***
   * Set the soft selection to the given range.
   *
   * @param startOffset  Start index of text to soft select.
   * @param endOffset    End index of text to soft select.
   */
  void setSoftSelection (in long startOffset, in long endOffset);

  /***
   * Clear the soft selection in this accessible.
   */
  void clearSoftSelection ();

  /***
   * Get the soft selection range in this accessible.
   *
   * @param startOffset  [out] Start index of soft selected text.
   * @param endOffset    [out] End index of soft selected text.
   * @return             false if no range is selected.
   */
  boolean getSoftSelection (out long startOffset, out long endOffset);
}

Event

When the soft selection changes an EVENT_SOFT_TEXT_SELECTION_CHANGED event is fired. This event is only emitted if the accessible already has EXT_STATE_SOFT_FOCUSED.

Example Usage

Setting Soft Focus

An input event handler that changes the soft focus.

function inputEventHandler(event) {
  ...
  let currNode = docAcc.softFocusPivots[0];
  if (currNode.nextSibling)
    currNode.nextSibling.takeSoftFocus(0);
}

Presenting Soft Focus

An event listener that highlights the bounds of a soft focus. drawRect() is left to your imagination.

function handleEvent(aEvent) {
  if (aEvent.type == EVENT_STATE_CHANGED) {
    let event = aEvent.QueryInterface(nsIAccessibleStateChangeEvent);
    if (event.state == EXT_STATE_SOFT_FOCUSED && event.isExtraState()) {
      if (event.isEnabled())
        showSoftFocus(aEvent.accessible);
      else
        hideSoftFocus();
    }
  } else if (aEvent.type == EVENT_SOFT_TEXT_SELECTION_CHANGED) {
        showSoftFocus(aEvent.accessible);
  }
}

function showSoftFocus (aAccessible) {
  // Only interested in pivot index 0
  if (accDoc.softFocusPivots[0] != aAccessible)
    return;

  let x = {};
  let y = {};
  let w = {};
  let h = {};

  try {
    let textAcc = aAccessible.QueryInterface(nsIAccessibleText);
    let start = {};
    let end = {};
    if (textAcc.getSoftSelection(start, end)) {
      textAcc.getRangeExtents(start.value, end.value, x, y, w, h,
                              COORDTYPE_SCREEN_RELATIVE);
      drawRect(x.value, y.value, w.value, h.value);
      return;
    }
  catch (e) {
  }

  aAccessible.getBounds(x, y, w, h);
  drawRect(x.value, y.value, w.value, h.value);
}