Class Accessibility

java.lang.Object
limn.accessibility.Accessibility

public final class Accessibility extends Object
What a widget fills in to say what it is, and the scratch the publish step fills it into.

One object per scene, reused down every walk and across every frame. A widget's describe hook is handed this and writes into whichever node the walk is currently on; nothing it writes constructs a facet, a string or a set. That is the whole reason the setters take a facet's fields rather than a facet: a record per node per walk would allocate on exactly the frames this design promises allocate nothing, and a blinking caret in a window of two hundred nodes would then be two hundred short-lived objects a second. The records are built once, in the copy, and only for nodes that carry them.

Two rules a widget author has to know, and they are the same rule twice.

First: a name is an I18nString and is compared by source. Hand over the string object the widget holds, not a resolved string. The comparison is then a reference, a locale and an epoch, and a repaint that changed nothing re-resolves nothing. A subtree that changes language moves the locale, so every name under it is re-resolved exactly once and diffed, with no subscription anywhere.

Second: a derived string is never built here. A formatted value, a masked line, a text field's contents have no source to compare, so they are handed over from the widget's own cache together with the counter that cache was filled against — name(String, long, Accessible.NameFrom), valueText(String, long), text(java.lang.String, long, int, limn.graphics.ShapedText.Affinity, int, int, int, limn.graphics.Rect, boolean). The comparison is two longs and never two strings. A widget that formats a string inside its describe hook stays correct and stops being free: it allocates one string per damaged frame to conclude that nothing changed, which is the defect the publish-cost test exists to catch. The general form, for whoever adds the next one: if a field's comparison allocates, the field needs a cheaper witness, and a monotonic counter on the model is usually it.

The publish step drives the rest of this class — the methods that begin and end nodes, apply the inherited state bits and produce the tree. Those are public because the scene is in another package and for no other reason; a widget calls none of them.

  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static interface 
    What resolveRelations(limn.accessibility.Accessibility.RelationResolver) turns a relation's target into a node identifier with.
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final int
    How many differing nodes a single publish reports individually before the difference stops being a list of events and becomes "everything changed".
  • Constructor Summary

    Constructors
    Constructor
    Description
    A builder with no nodes and nothing published.
  • Method Summary

    Modifier and Type
    Method
    Description
    void
    accessKey(@Nullable String display)
    Gives this node its access letter's chord, as a user would read it ("Alt+N"): the letter a menu title, a button or a field's label marks.
    void
    Offers a verb on this node.
    void
    Offers several verbs on this node.
    void
    Offers two verbs on this node.
    void
    bounds(float x, float y, float width, float height)
    Places a synthetic child's box, in the coordinates of the widget that declared it.
    void
    cell(int row, int column)
    Declares that this node is one cell of a table, at a row and a column as shown.
    void
    cell(int row, int column, CellFacet.Sort sort)
    Declares that this node is one cell of a table, at a row and a column as shown, and which way its column's rows are running.
    boolean
    Whether what the walk just produced differs, anywhere, from what was published last.
    void
    child(long key)
    Begins a synthetic child of the node being described: something the widget draws but never instantiated as a widget — a menu column, a menu row, a combo option, a chart series.
    void
    containerlessSelectionItem(boolean selected, int positionInSet, int sizeOfSet)
    Declares that this node is one member of a selection that no node holds: a radio button, whose group is not a widget and has no box.
    void
    Offers a verb on this widget child that this widget's parent performs: a row's SELECT, published on the row's cell where the platform addresses it and routed by the scene to the container's onAccessibilityChildAction hook with the child's key.
    void
    description(@Nullable String cached, long witness)
    Describes this node from a string the widget derived and is holding.
    void
    description(@Nullable I18nString source)
    Describes this node at more length than its name does: what a tooltip says when the tooltip is not already the name.
    void
    Says that the synthetic child being described cannot be operated even though its owner can: a calendar day the bounds or the filter refuse, a scroll chevron with nothing left to scroll.
    void
    Discards the node begin(long, int, java.util.Locale, float, float, float, float) began, as though it had never been started: what the publish step does to a container that turned out to be scaffolding.
    void
    emptyValue(double min, double max, double step, boolean readOnly)
    Declares that this node's state is a number in a range and that it holds no number right now: a date segment nobody has typed into.
    void
    end()
    Ends the node begin(long, int, java.util.Locale, float, float, float, float) began, making its parent current again.
    void
    Closes the synthetic child child(long) opened.
    void
    expand(boolean expanded)
    Declares that this node opens and closes.
    boolean
    Whether the node being described has a name yet, so that the publish step knows whether one of its free defaults still has somewhere to go.
    boolean
    Whether anything has said what the node being described is.
    void
    hierarchy(int level, int row, int rowCount)
    Declares where this node stands in an outline: a tree row's depth and its flat index among the rows the outline shows open, the two numbers HierarchyFacet explains.
    void
    Removes this node and its children from the tree: a spacer, a decorative rule, a mark a widget draws that carries no information.
    void
    key(long key)
    Gives the child about to be described the identity key its parent chose for it, from inside the parent's identity hook and nowhere else.
    void
    keyBinding(@Nullable String display)
    Gives this node's primary action the keystroke that performs it, as a user would read it.
    void
    name(@Nullable String cached, long witness, Accessible.NameFrom from)
    Names this node from a string the widget derived and is already holding, together with the counter its cache was filled against.
    void
    name(@Nullable I18nString source)
    Names this node from a localizable string, with the provenance Accessible.NameFrom.CONTENT: the widget's own painted text.
    void
    name(@Nullable I18nString source, Accessible.NameFrom from)
    Names this node from a localizable string the widget holds, and says where the name came from.
    int
     
    void
    Says that the synthetic child being described is scrolled out of its owner's viewport, so that it publishes without SHOWING while its owner still shows.
    publish(long focusedId, int screenX, int screenY, float factor, boolean positioning)
    Turns the walk into an immutable tree, and the difference from the last one into events.
    void
    relation(Accessible.Relation kind, @Nullable Object target)
    Links this node to another one.
    void
    Declares what this node is.
    void
    scroll(double horizontalPercent, double verticalPercent, double horizontalViewSize, double verticalViewSize, boolean horizontallyScrollable, boolean verticallyScrollable)
    Declares that this node scrolls, in the fractions a ScrollFacet carries; scrollFrom(double, double, double, double, double, double, double, double) computes them from offsets and extents.
    void
    scrollFrom(double offsetX, double maxX, double viewportWidth, double contentWidth, double offsetY, double maxY, double viewportHeight, double contentHeight)
    The scroll facet folded from the geometry every scrolling widget holds: per axis, how far it has scrolled, how far it can, and how big the viewport and the content are.
    void
    selection(boolean multiSelectable, boolean required)
    Declares that this node holds a selection among its descendants.
    void
    selectionItem(boolean selected, int positionInSet, int sizeOfSet)
    Declares that this node is one member of a container's selection.
    void
    slider(Accessible.State orientation, double value, double min, double max, double step)
    A slider in one call: the role, the axis its value runs along, the value and the two step verbs.
    void
    Sets a state on this node; the same as state(Accessible.State, boolean) with true.
    void
    state(Accessible.State state, boolean on)
    Sets a state on this node.
    void
    table(int rowCount, int columnCount)
    Declares that this node is a grid of rows and columns over data.
    void
    text(@Nullable String cached, long witness, int caretOffset, ShapedText.Affinity caretAffinity, int selectionStart, int selectionEnd, int lineCount, @Nullable Rect caretRect, boolean readOnly)
    Declares that this node holds text, handing over the string the widget already has together with the counter its editing model bumps.
    void
    toggle(@Nullable ToggleFacet.State state)
    Declares that this node carries a checked state.
    void
    under(long syntheticKey)
    Hangs the child about to be described under one of this widget's own synthetic children rather than under this widget's node: a Table's widget cell under the synthetic ROW it sits in, so a reader walking the row finds the control among its cells.
    void
    value(double value, double min, double max, double step)
    Declares that this node's state is a number in a range.
    void
    value(double value, double min, double max, double step, boolean readOnly)
    value(double, double, double, double), for a number the user may be shown and not set: a read-only value accepts no SET_VALUE and publishes no step verbs.
    void
    valueText(@Nullable String cached, long witness)
    Gives the value a display form: what the widget shows instead of the number, handed over from the widget's own cache with the counter that cache was filled against.
    void
    window(boolean modal, boolean canMaximize, boolean canMinimize, WindowFacet.State state)
    Declares that this node is a real top-level window.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • EVENT_BUDGET

      public static final int EVENT_BUDGET
      How many differing nodes a single publish reports individually before the difference stops being a list of events and becomes "everything changed".

      The bound is here rather than in each bridge so that all three get the same guarantee from one implementation, and so that it can be asserted without a platform. Past it the publish emits one AccessibleEvent.Type.INVALIDATED, which a bridge answers by reconciling whatever it holds against the tree it was just handed — a full sweep, which is stronger than replaying the events that were dropped and is the operation it needs on a rebind anyway. Outside the budget, and handed over after the collapse as after any publish, is the reserved tail: the per-parent structure changes, the final focus change, the single cursor change, the per-container selection changes and the window activation events — the events a reader is directed by, which the demo's dialog opening was three events short of losing.

      See Also:
  • Constructor Details

    • Accessibility

      public Accessibility()
      A builder with no nodes and nothing published. One per scene.
  • Method Details

    • role

      public void role(Accessible.Role role)
      Declares what this node is. A node that declares nothing else is scaffolding and is removed from the tree with its children hoisted in its place, so declaring a role is how a widget stays in the tree at all.
      Parameters:
      role - the role; never null
      Throws:
      NullPointerException - if role is null
    • hasRole

      public boolean hasRole()
      Whether anything has said what the node being described is.

      The publish step asks, because a widget the keyboard can land on and nobody named is a defect it has to report rather than a group box it can quietly publish.

      Returns:
      whether role(Accessible.Role) was called on this node
    • name

      public void name(@Nullable I18nString source, Accessible.NameFrom from)
      Names this node from a localizable string the widget holds, and says where the name came from.

      Resolved under the node's own language, once, and carried over unresolved on every later frame where the string, the language and the translation epoch are all unchanged.

      A name that resolves to nothing carries no provenance. A widget hands over the string it holds whether or not the application filled it — a text field's placeholder is I18nString.EMPTY until one is set — and the empty string names nothing, which is what lets the walk's own defaults still land. The provenance has to follow the same rule, or the node says it was named by a placeholder it does not have: a fact about where nothing came from, which one platform would then act on by choosing an attribute for it. So an empty name is published with the default, Accessible.NameFrom.CONTENT, whatever the caller said, and the caller's provenance is kept only for a name that exists.

      Parameters:
      source - the widget's own string; null clears the name
      from - where the name came from, which decides which attribute one platform publishes it in
      Throws:
      NullPointerException - if from is null
    • name

      public void name(@Nullable I18nString source)
      Names this node from a localizable string, with the provenance Accessible.NameFrom.CONTENT: the widget's own painted text.
      Parameters:
      source - the widget's own string; null clears the name
    • name

      public void name(@Nullable String cached, long witness, Accessible.NameFrom from)
      Names this node from a string the widget derived and is already holding, together with the counter its cache was filled against.

      This is the form for a formatted name — anything built with a format call, which is never cached and therefore has no source to compare. Building the string inside the describe hook instead leaves the node correct and makes every damaged frame allocate.

      An empty string carries no provenance, for the reason name(I18nString, Accessible.NameFrom) gives.

      Parameters:
      cached - the string the widget is holding; null clears the name
      witness - a value that changes whenever cached does, and is otherwise meaningless
      from - where the name came from
      Throws:
      NullPointerException - if from is null
    • description

      public void description(@Nullable I18nString source)
      Describes this node at more length than its name does: what a tooltip says when the tooltip is not already the name.
      Parameters:
      source - the widget's own string; null clears the description
    • description

      public void description(@Nullable String cached, long witness)
      Describes this node from a string the widget derived and is holding.
      Parameters:
      cached - the string; null clears the description
      witness - a value that changes whenever cached does
    • state

      public void state(Accessible.State state, boolean on)
      Sets a state on this node.

      Two groups of states are refused here rather than stored, and refused loudly, the way action(Accessible.Action) refuses a verb that takes an argument. The states a facet expresses — checked, mixed, expanded, expandable, selected, selectable, read-only — are derived from that facet, so one fact keeps one home: declare the facet. The five the publish step owns — enabled, visible, showing, focusable and focused — belong to the walk, because the tree has to agree with a keyboard whose traversal stops at the first ancestor that is hidden or disabled and at the layer that owns input, which a widget describing itself cannot see; a synthetic child narrows two of them through offScreen() and disabled(). Until 2026-09-14 such a call was dropped in silence, and a widget carried a dead line for months that no reader ever heard.

      Parameters:
      state - what to set; never null
      on - whether it holds
      Throws:
      NullPointerException - if state is null
      IllegalArgumentException - if the state is a facet's or the publish step's to derive
    • state

      public void state(Accessible.State state)
      Sets a state on this node; the same as state(Accessible.State, boolean) with true.
      Parameters:
      state - what to set
    • toggle

      public void toggle(@Nullable ToggleFacet.State state)
      Declares that this node carries a checked state.
      Parameters:
      state - whether it is off, on, or neither; null removes the facet
    • value

      public void value(double value, double min, double max, double step)
      Declares that this node's state is a number in a range.

      A value that advances on its own is rounded here, by the widget, to the resolution a user can act on: a playing video writes whole seconds. The comparison sees what it is given, so an unrounded position copies the whole tree once per frame for the length of the film with nobody touching anything.

      Parameters:
      value - the current value
      min - the smallest the node accepts
      max - the largest
      step - one increment, or 0 for none
    • slider

      public void slider(Accessible.State orientation, double value, double min, double max, double step)
      A slider in one call: the role, the axis its value runs along, the value and the two step verbs. Six widgets describe themselves this way and each used to write the four lines; the pair of verbs is the two-argument form, which allocates nothing.
      Parameters:
      orientation - Accessible.State.HORIZONTAL or Accessible.State.VERTICAL
      value - the current value
      min - the least value
      max - the greatest value
      step - what one increment moves by
    • value

      public void value(double value, double min, double max, double step, boolean readOnly)
      value(double, double, double, double), for a number the user may be shown and not set: a read-only value accepts no SET_VALUE and publishes no step verbs.
      Parameters:
      value - the current value
      min - the smallest the node accepts
      max - the largest
      step - one increment, or 0 for none
      readOnly - whether the number is shown and never set
    • emptyValue

      public void emptyValue(double min, double max, double step, boolean readOnly)
      Declares that this node's state is a number in a range and that it holds no number right now: a date segment nobody has typed into. The range stands, the text may say what is shown, and the number published is the minimum, for the platforms that must have one; ValueFacet.empty() is what says empty. Filling the number later is a AccessibleEvent.Type.VALUE_CHANGED, even when the number filled in is the minimum.
      Parameters:
      min - the smallest the node accepts
      max - the largest
      step - one increment, or 0 for none
      readOnly - whether the value may be read and not set
    • valueText

      public void valueText(@Nullable String cached, long witness)
      Gives the value a display form: what the widget shows instead of the number, handed over from the widget's own cache with the counter that cache was filled against.
      Parameters:
      cached - the displayed string; null means the number is the whole of it
      witness - a value that changes whenever cached does
    • selection

      public void selection(boolean multiSelectable, boolean required)
      Declares that this node holds a selection among its descendants.

      Which of them are selected is on the descendants, in their own facet. Which of them the keyboard cursor is on is not this container's fact at all: it is the tree's, resolved by the publish step from the first descendant of the focused node published with Accessible.State.ACTIVE (AccessibleTree.activeDescendant()), which a widget publishes only while it holds the keyboard.

      Parameters:
      multiSelectable - whether more than one may be selected at once
      required - whether at least one always is
    • selectionItem

      public void selectionItem(boolean selected, int positionInSet, int sizeOfSet)
      Declares that this node is one member of a container's selection.

      The position and the size are the model's numbers, not the tree's: a list that publishes only its realized rows still reports the true row count, so a user hears where they are in the data.

      The facet's presence is what makes the node Accessible.State.SELECTABLE on every node that declares it, exactly as the expand facet's presence is what makes a node EXPANDABLE: declaring that you are one member of a selection is saying you can be selected, and neither bit is a widget's to set. Until 2026-09-16 nothing in either module published it — an uncut grep of both mains found two hits in the whole tree, the enum constant and the AT-SPI bit it maps to — so the state never left the model. The live cost was measured on Fedora 44 and Ubuntu 24.04 on 2026-09-16: after Ctrl+A, Orca resolved all five selected rows and discarded every one of them, "believed to be layout only: … is not focusable, selectable, or expandable and lacks explicit name", and said nothing at all. Linux maps the bit (AtspiStates, bit 22); Windows carries the same fact through the SelectionItem pattern and macOS through AXSelected's settability, so neither bridge changes.

      Parameters:
      selected - whether this member is selected
      positionInSet - its one-based position, or 0 when it has none
      sizeOfSet - how many members the set holds, or 0 when that is unknown
    • containerlessSelectionItem

      public void containerlessSelectionItem(boolean selected, int positionInSet, int sizeOfSet)
      Declares that this node is one member of a selection that no node holds: a radio button, whose group is not a widget and has no box. The numbers are as for selectionItem(boolean, int, int); what differs is that no selection change is raised on the node's published parent, which would be whatever layout ancestor survived transparency, and a bridge asked for the member's container answers none.
      Parameters:
      selected - whether this member is selected
      positionInSet - its one-based position among the members it counts, or 0
      sizeOfSet - how many members there are, or 0 when there is no set
    • expand

      public void expand(boolean expanded)
      Declares that this node opens and closes.

      The facet's presence is what makes the node Accessible.State.EXPANDABLE, on every node that declares it (menu titles and submenu rows included), and its value is what makes it Accessible.State.EXPANDED; the Linux bridge derives its own COLLAPSED from the two. Neither bit is a widget's to set.

      Parameters:
      expanded - whether it is open now
    • text

      public void text(@Nullable String cached, long witness, int caretOffset, ShapedText.Affinity caretAffinity, int selectionStart, int selectionEnd, int lineCount, @Nullable Rect caretRect, boolean readOnly)
      Declares that this node holds text, handing over the string the widget already has together with the counter its editing model bumps.

      Every offset is a UTF-16 code unit. A masked field passes its mask here and offsets into that mask, never its own text and never the secret's offsets.

      Parameters:
      cached - the text; null removes the facet
      witness - a value that changes whenever cached does
      caretOffset - where the caret sits
      caretAffinity - which side of it the caret is on; never null
      selectionStart - where the selection begins, equal to its end when there is none
      selectionEnd - where it ends
      lineCount - how many lines the text has
      caretRect - the caret's box in this node's own coordinates, or null; the publish step carries it into the window's, as it does the node's box
      readOnly - whether the text may be read and not written
      Throws:
      NullPointerException - if caretAffinity is null
    • scrollFrom

      public void scrollFrom(double offsetX, double maxX, double viewportWidth, double contentWidth, double offsetY, double maxY, double viewportHeight, double contentHeight)
      The scroll facet folded from the geometry every scrolling widget holds: per axis, how far it has scrolled, how far it can, and how big the viewport and the content are.

      Seven widgets used to fold these into the facet's percentages themselves, and three of them forgot the one guard that matters: a viewport larger than its content is a fraction over one, which a platform reads as more than all of it. Here the view size is held to one, an axis with nothing to scroll answers zero and one, and a content of zero is "all of it" rather than a division by zero — which is also what keeps a NaN out of a field the tree's difference compares with !=.

      Parameters:
      offsetX - how far the content has scrolled along x, from zero
      maxX - how far it can; zero or less when the axis does not scroll
      viewportWidth - the width shown
      contentWidth - the width there is; zero or less when unknown
      offsetY - how far the content has scrolled along y, from zero
      maxY - how far it can; zero or less when the axis does not scroll
      viewportHeight - the height shown
      contentHeight - the height there is; zero or less when unknown
    • scroll

      public void scroll(double horizontalPercent, double verticalPercent, double horizontalViewSize, double verticalViewSize, boolean horizontallyScrollable, boolean verticallyScrollable)
      Declares that this node scrolls, in the fractions a ScrollFacet carries; scrollFrom(double, double, double, double, double, double, double, double) computes them from offsets and extents.
      Parameters:
      horizontalPercent - where the viewport sits along the content's width, in 0..1
      verticalPercent - where it sits along the content's height, in 0..1
      horizontalViewSize - the fraction of the content's width the viewport shows
      verticalViewSize - the fraction of its height the viewport shows
      horizontallyScrollable - whether the content is wider than the viewport
      verticallyScrollable - whether it is taller
    • table

      public void table(int rowCount, int columnCount)
      Declares that this node is a grid of rows and columns over data.

      Both counts are the model's, as TableFacet says: a table publishes only the rows it has realized and still reports how many there are.

      Parameters:
      rowCount - how many data rows the model holds
      columnCount - how many columns are shown
    • cell

      public void cell(int row, int column)
      Declares that this node is one cell of a table, at a row and a column as shown.
      Parameters:
      row - the row as shown, from zero, or -1 for a cell of the header row
      column - the column as shown, from zero
    • cell

      public void cell(int row, int column, CellFacet.Sort sort)
      Declares that this node is one cell of a table, at a row and a column as shown, and which way its column's rows are running.

      The direction is a fact about a header cell and is meaningless anywhere else: it is the column's sort, and the header is the control a reader presses to change it. Every other cell declares CellFacet.Sort.NONE, and so does a header whose column is not the one the table is sorted by. The three bridges each carry it in their platform's own way; the localized phrase a header also puts in its description is not this, and neither replaces the other.

      Parameters:
      row - the row as shown, from zero, or -1 for a cell of the header row
      column - the column as shown, from zero
      sort - which way this column's rows run; never null
    • hierarchy

      public void hierarchy(int level, int row, int rowCount)
      Declares where this node stands in an outline: a tree row's depth and its flat index among the rows the outline shows open, the two numbers HierarchyFacet explains. Its place among its siblings is selectionItem(boolean, int, int)'s.
      Parameters:
      level - how deep the row is, from one at a root, or 0 when unknown
      row - which row of the outline this is, from one at the top, or 0
      rowCount - how many rows the outline shows open, or 0 when unknown
    • window

      public void window(boolean modal, boolean canMaximize, boolean canMinimize, WindowFacet.State state)
      Declares that this node is a real top-level window. A dialog drawn inside another window's scene is not one and must not call this.
      Parameters:
      modal - whether it blocks the windows it owns
      canMaximize - whether the desktop offers a maximize action
      canMinimize - whether it offers a minimize action
      state - normal, minimized or maximized; never null
      Throws:
      NullPointerException - if state is null
    • action

      public void action(Accessible.Action action)
      Offers a verb on this node.

      Only the parameterless verbs are publishable. A parameterised setter is advertised by the presence of the facet it sets, and offering it here would make one platform's action list unanswerable.

      Parameters:
      action - the verb to offer
      Throws:
      NullPointerException - if action is null
      IllegalArgumentException - if it takes an argument
    • action

      public void action(Accessible.Action first, Accessible.Action second)
      Offers two verbs on this node.

      A pair rather than the variable-argument form below, because that one allocates an array per call and the publish step offers the same two on every focusable widget in the window, on every frame that walks. The general form is for a widget describing itself, where a handful of arrays a frame is not the same kind of cost.

      Parameters:
      first - one verb
      second - the other
      Throws:
      NullPointerException - if either is null
      IllegalArgumentException - if either takes an argument
    • action

      public void action(Accessible.Action... actions)
      Offers several verbs on this node.
      Parameters:
      actions - the verbs to offer
      Throws:
      NullPointerException - if actions or any of them is null
      IllegalArgumentException - if one of them takes an argument
    • delegate

      public void delegate(Accessible.Action action)
      Offers a verb on this widget child that this widget's parent performs: a row's SELECT, published on the row's cell where the platform addresses it and routed by the scene to the container's onAccessibilityChildAction hook with the child's key.

      From onAccessibilityChild only, because that is where a container speaks about a child with the child's node open; and never for a verb the child already claimed in its own hook — a cell that is a Button keeps its PRESS, and a container that claimed the same verb would have stolen it in silence, so the walk is refused rather than publish one verb with two performers. The child keeps every other verb it declared, and its identity is untouched: the node is still the child's, owned by the child for every verb but the delegated ones.

      Parameters:
      action - the verb the parent claims
      Throws:
      NullPointerException - if action is null
      IllegalArgumentException - if it takes an argument
      IllegalStateException - if called outside onAccessibilityChild, or if the child already offers the verb itself
    • keyBinding

      public void keyBinding(@Nullable String display)
      Gives this node's primary action the keystroke that performs it, as a user would read it.

      Kept only on a node that offers a verb: it is published with the node's ActionFacet, which a node with no verb does not have.

      Parameters:
      display - the accelerator's display form, or null for none
    • accessKey

      public void accessKey(@Nullable String display)
      Gives this node its access letter's chord, as a user would read it ("Alt+N"): the letter a menu title, a button or a field's label marks. Not the accelerator; see keyBinding(java.lang.String).

      Kept only on a node that offers a verb, as the key binding is: it is published with the node's ActionFacet. A caption that names a field gives its key to the field, which is where the keyboard goes, and not to its own static text.

      Parameters:
      display - the chord's display form, or null for none
    • relation

      public void relation(Accessible.Relation kind, @Nullable Object target)
      Links this node to another one.

      The target is resolved by the publish step to the nearest node it actually published, and the relation is dropped when there is none: naming a node that was never published is worse than naming nothing, because every platform answers it with an element that does not resolve.

      Parameters:
      kind - what the link means; never null
      target - the Widget at the other end. Typed as an object because this package sits below the widget tree and names nothing in it; anything else resolves to no node and the relation is dropped.
      Throws:
      NullPointerException - if kind is null
    • ignore

      public void ignore()
      Removes this node and its children from the tree: a spacer, a decorative rule, a mark a widget draws that carries no information.

      Not for a control that can be operated. Removing one of those is not a deferral, it is hiding it from the only user who cannot see it.

    • offScreen

      public void offScreen()
      Says that the synthetic child being described is scrolled out of its owner's viewport, so that it publishes without SHOWING while its owner still shows.

      A widget cannot set that state itself — state(Accessible.State, boolean) refuses the five the publish step owns, because the walk works them out across the ancestry — and the publish step cannot work it out either, since a row clipped away inside a scrolled list is a fact only the owner's own row formula knows. So this is the one bit of the inherited set a widget contributes to, and it contributes it the way it can be checked: by handing over the answer its paint loop already computes, rather than by naming a rectangle twice.

      Clear by default and cleared for every child, so a scrolled-away row does not leave the next one marked. It says nothing about a widget's own node, which takes its showing bit from the widget tree.

      Throws:
      IllegalStateException - if no synthetic child is open
    • disabled

      public void disabled()
      Says that the synthetic child being described cannot be operated even though its owner can: a calendar day the bounds or the filter refuse, a scroll chevron with nothing left to scroll. It publishes without ENABLED while its owner keeps it.

      offScreen()'s shape, for offScreen()'s reason: enabled is one of the states the publish step owns and a widget cannot set, and the step inherits it from the owner down onto every synthetic child, which is right for a menu row inside a disabled menu and wrong for a refused day inside an enabled calendar. So this is narrowing only: a child may be less enabled than its owner and never more, because the owner's bit is the keyboard's and a child published operable inside a disabled owner would be refused by the scene's action gate with nothing said about why. Clear by default and cleared for every child. It narrows through the nesting: a synthetic child declared inside a disabled one publishes without ENABLED too, whether or not it called this itself.

      It does not reach a widget, and the walk refuses the combination. A widget child a container hangs under a disabled synthetic node with under(long) would take its enabled bit from the widget tree, as the scene's action gate does, and publish ENABLED and its verbs beneath its disabled row. No widget combines the two — Table is the only caller of under and never disables a row, and this method's callers (a calendar's refused day and month, a segmented control's dead chevron, a combo's options and a menu's rows) hang no widget under the node — so the walk throws when an enabled container does, and the previous tree stands. A widget that needs it owes the walk and the gate the same narrowing in the same change.

      A disabled child carries no verb: the publish step withdraws every verb from a node that is not ENABLED (inoperableAt(int)), so a refused day may declare its SELECT and still publishes none, and a bridge reads the absence; a setter its facets would imply is implied only on a node that is ENABLED and VISIBLE (AccessibleNode.accepts(limn.accessibility.Accessible.Action)).

      Throws:
      IllegalStateException - if no synthetic child is open
    • key

      public void key(long key)
      Gives the child about to be described the identity key its parent chose for it, from inside the parent's identity hook and nowhere else.

      This is what keeps a recycled list cell from carrying row three's identifier to row nine. A container that pools its children owns their identity, and nothing else can: the widget object is the wrong key the moment it is reused, and the data index is the right one.

      It is answered before the child describes itself, and that ordering is the whole point. The child's node takes its identifier from the key before its own hook runs, so a name it hands over is looked up under the identifier it was published with last frame and carried over unresolved; and everything the child declares inside itself — a synthetic child of its own, a widget it holds — is scoped under that identifier too, so a composite cell recycled to another row carries none of the old row's inner elements with it. A key given from the describe-a-child hook would arrive after both, which is why that hook refuses it.

      Parameters:
      key - a value unique among the children of the node this child hangs under, of both kinds — a widget cell keyed 2 and a synthetic cell keyed 2 under one row would be one element — and stable for as long as the child stands for the same thing
      Throws:
      IllegalStateException - if called outside the identity hook
    • under

      public void under(long syntheticKey)
      Hangs the child about to be described under one of this widget's own synthetic children rather than under this widget's node: a Table's widget cell under the synthetic ROW it sits in, so a reader walking the row finds the control among its cells.

      From the identity hook only, like key(long), because where a node hangs is decided before it is begun. The synthetic child is named by the key its owner gave it in child(long) during this same walk, and must exist: a parent naming a row it did not declare is a defect the walk reports rather than a child it quietly publishes elsewhere. The child's identity is then scoped under that synthetic node, not under this widget, so key(long) need only be unique among the row's children. Among them it takes its place by column when it and they carry a CellFacet, and last otherwise. Its verbs stay its own: the walk still routes an action on it to the widget, never to the owner's synthetic hook.

      Parameters:
      syntheticKey - the key of the synthetic child of this widget the child hangs under
      Throws:
      IllegalStateException - if called outside the identity hook
    • child

      public void child(long key)
      Begins a synthetic child of the node being described: something the widget draws but never instantiated as a widget — a menu column, a menu row, a combo option, a chart series.

      Synthetic children nest, so a menu row that opens a submenu owns that column. Every one of them must be closed with endChild(), and each supplies its own bounds, because nothing else has them.

      Parameters:
      key - a value unique among this owner's synthetic children and stable for as long as the child stands for the same thing
    • endChild

      public void endChild()
      Closes the synthetic child child(long) opened.
      Throws:
      IllegalStateException - if no synthetic child is open
    • bounds

      public void bounds(float x, float y, float width, float height)
      Places a synthetic child's box, in the coordinates of the widget that declared it.

      A widget's own node takes its box from its layout and never calls this; a synthetic child has no layout and must.

      A negative width or height is published as zero, and the first one is logged: a box computed as a difference before the layout it depends on has run.

      Parameters:
      x - the left edge, in the declaring widget's own coordinates
      y - the top edge
      width - the width
      height - the height
    • end

      public void end()
      Ends the node begin(long, int, java.util.Locale, float, float, float, float) began, making its parent current again. The publish step calls this.
    • drop

      public void drop()
      Discards the node begin(long, int, java.util.Locale, float, float, float, float) began, as though it had never been started: what the publish step does to a container that turned out to be scaffolding.
      Throws:
      IllegalStateException - if the node has already had children
    • nodeCount

      public int nodeCount()
      Returns:
      how many nodes the walk has produced so far
    • hasName

      public boolean hasName()
      Whether the node being described has a name yet, so that the publish step knows whether one of its free defaults still has somewhere to go.
      Returns:
      whether anything named this node
    • changed

      public boolean changed()
      Whether what the walk just produced differs, anywhere, from what was published last.

      This is the question a damaged frame asks and the answer is usually no: damage is a coarse trigger, and a caret blink, a hover ripple and a chart tween all damage something and change no accessible fact. Answering it costs a comparison per field and allocates nothing.

      Returns:
      whether anything a reader could see has moved
    • publish

      public AccessibleTree publish(long focusedId, int screenX, int screenY, float factor, boolean positioning)
      Turns the walk into an immutable tree, and the difference from the last one into events.

      This is the only step that allocates: the facet records are built here, and only for nodes that carry them.

      Parameters:
      focusedId - the identifier of the node holding the keyboard focus, or 0
      screenX - the window's content origin in native screen coordinates
      screenY - the same, vertically
      factor - the multiplier from logical points to native screen coordinates
      positioning - whether the platform lets this window know where it is
      Returns:
      the tree, ready to publish