Class AccessibleTree

java.lang.Object
limn.accessibility.AccessibleTree

public final class AccessibleTree extends Object
One window's accessible tree, as it stood at the instant the user-interface thread published it.

Immutable, and read from any thread with no lock. That is the whole shape of this design and it is what the three platforms decide between them: one of them calls a provider from several of its own threads at once while the user-interface thread sleeps, and another never calls at all but wants the entire tree in a single message. A snapshot answers both, and the staleness argument is short — a snapshot goes stale only while the user-interface thread is running, and the user-interface thread publishes before it goes back to sleep.

The node at index 0 is the root. Every other node is reachable from it through the link indices on AccessibleNode, in tree order, which is paint order, which is the order the Tab key walks: reading order is defined to be that order and never a sort by geometry, which would be right in a left-to-right interface and backwards in a right-to-left one.

The window stamp on this type is the half a reader needs and cannot ask for. A window's screen origin and its scale are user-interface-thread-confined, so a reader on a platform thread that tried to ask would throw; they are captured here, once, by the thread that may.

  • Field Details

    • EMPTY

      public static final AccessibleTree EMPTY
      The tree of a window that has nothing to say: no nodes, no stamp, no identifiers.
  • Method Details

    • restamp

      public AccessibleTree restamp(int newScreenX, int newScreenY, float newFactor, boolean positioning)
      The same tree with a new window stamp and every node untouched.

      A window drag changes every screen rectangle this tree publishes and changes nothing in it: the boxes are scene-local, and a move does not touch one of them. So a move re-stamps and walks nothing, which is the difference between a drag costing four numbers and a drag costing a whole tree walk per callback the compositor sends.

      Parameters:
      newScreenX - the window's content origin in native screen coordinates
      newScreenY - the same, vertically
      newFactor - the multiplier from logical points to native screen coordinates
      positioning - whether the platform lets this window know where it is at all
      Returns:
      a tree with the new stamp, or this one when nothing in the stamp moved
    • sceneTag

      public long sceneTag()
      Returns:
      the tag every identifier minted for this window carries above its serial, which is what makes an identifier process-wide; 0 for the empty tree
    • holds

      public boolean holds(long id)
      Whether an identifier was minted for this window's scene, which is the question a bridge holding several windows' trees asks before find(long): a relation may name a node in another window — a native popup's root names the field that opened it — and the number alone says which tree to look in. Answered from the identifier's high bits and nothing else, so it costs no lookup and no registry; it does not say whether the node is in this snapshot, which find(long) does.
      Parameters:
      id - a node identifier
      Returns:
      whether it belongs to this window's scene
    • nodeCount

      public int nodeCount()
      Returns:
      how many nodes this tree holds; 0 for a window with nothing to say
    • node

      public AccessibleNode node(int index)
      Parameters:
      index - the node's index, as carried by the link accessors on AccessibleNode
      Returns:
      the node at that index
      Throws:
      IndexOutOfBoundsException - if the index is not one of this tree's
    • root

      public @Nullable AccessibleNode root()
      Returns:
      the root node, or null when this tree is empty
    • indexOf

      public int indexOf(long id)
      Parameters:
      id - the identifier to look for
      Returns:
      the index of the node carrying it, or AccessibleNode.NONE. Linear in the node count: a bridge that resolves identifiers on every platform call keeps a map of its own rather than asking this repeatedly.
    • find

      public @Nullable AccessibleNode find(long id)
      Parameters:
      id - the identifier to look for
      Returns:
      the node carrying it, or null
    • children

      public List<AccessibleNode> children(AccessibleNode node)
      The children of a node, in order, walked along the sibling links. Resolved through the node's identifier rather than its links directly, so a node held from an earlier snapshot answers with its children in this one, or with none when it is gone.
      Parameters:
      node - a node of this tree or of an earlier one
      Returns:
      its children in this tree, first to last; empty when it has none or is not here
    • indexInParent

      public int indexInParent(AccessibleNode node)
      Where a node sits among its siblings.
      Parameters:
      node - a node of this tree or of an earlier one
      Returns:
      its position under its parent, from zero; -1 for the root, which has no siblings, and for a node that is not in this tree
    • focused

      public long focused()
      Returns:
      the identifier of the node holding the keyboard focus, or 0 when nothing in this window does
    • activeDescendant

      public long activeDescendant()
      The node the keyboard cursor is on inside the focused node: the first node published Accessible.State.ACTIVE strictly below focused() in reading order, whichever containers lie between them. Never the window node's own ACTIVE, and never resolved through a SelectionFacet: a container that is not the focused node, or below it, has no cursor to publish. When the focused node's own subtree holds no ACTIVE node and it opened a popup that is a window of its own, the cursor is read across the Accessible.Relation.CONTROLLER_FOR relation into that window's tree, so the identifier here may belong to another window — holds(long) says which.
      Returns:
      the identifier of the active descendant, or 0 when the focused node has none or nothing is focused
    • effectiveFocus

      public long effectiveFocus()
      Where the user is, for a platform that puts its focus on the item rather than on the widget: the active descendant when there is one, and the focused node otherwise.
      Returns:
      the identifier of the node a reader should be standing on, or 0 when nothing in this window holds the focus
    • firstActiveBelow

      public long firstActiveBelow(int index)
      The first node published Accessible.State.ACTIVE strictly below a node, in reading order: what activeDescendant() is resolved from, exposed so that the walk of a window whose focused node opened a popup can read the popup's cursor off the popup's own tree, and so that a bridge can ask the same of any subtree. Allocates nothing.
      Parameters:
      index - the index of the node whose subtree is searched; its own bit is never counted
      Returns:
      the identifier of the first active node below it, or 0 when there is none
      Throws:
      IndexOutOfBoundsException - if the index is not one of this tree's
    • screenX

      public int screenX()
      Returns:
      the window's content origin in native screen coordinates, captured on the user-interface thread at publish time. A placeholder when supportsAbsolutePositioning() is false.
    • screenY

      public int screenY()
      Returns:
      the same, vertically
    • logicalToScreenFactor

      public float logicalToScreenFactor()
      Returns:
      the multiplier from this scene's logical points to native screen coordinates: one on a platform that measures in points, the monitor scale on one that measures in pixels. It already folds in an application's content-scale override, so one multiplier gives all three platforms the answer each of them wants.
    • supportsAbsolutePositioning

      public boolean supportsAbsolutePositioning()
      Returns:
      whether this window can know where it is on the screen at all. Where it cannot, the origin above is a placeholder and a bridge reports window-relative extents truthfully rather than dressing zeros up as a position.
    • sceneWidth

      public float sceneWidth()
      Returns:
      the scene's width in logical points at publish time
    • sceneHeight

      public float sceneHeight()
      Returns:
      the scene's height in logical points at publish time. This is the content view's height, and it is what a bridge flips against on the one platform whose accessibility coordinates run from the bottom.
    • locale

      public Locale locale()
      Returns:
      the scene's own language, which a node inherits when it declares none
    • generation

      public long generation()
      Returns:
      a counter that moves on every publish and every re-stamp of this window. Meaningless except compared with itself: it is what lets a bridge holding a derived form of a tree — a pre-marshalled message, a cached element list — know that the form is still current without comparing anything.