Class Tree<T>
- All Implemented Interfaces:
Scrollable
The application keeps its nodes. A tree is handed a Tree.Model, which answers what
the roots are, what a node's children are, and what widget draws one. Nothing here is a node type
of this toolkit's: a tree over Path, over a record, or over a live domain object is the
same tree. Identity is the node's own equals, which gives value semantics to records and
reference semantics to mutable objects, and both are what the application that chose them wanted.
A node is unique within a tree: two equal nodes in two places would share one selection,
one expansion and one accessible identity, so the tree refuses them the moment both are visible,
with an IllegalStateException naming the node. A model whose values repeat under
different parents — a file named the same in two folders — gives its nodes path identity, the way
the guide's example does.
A row may promise children before it can name them. Tree.Model.children(T) answers
null for a node whose children are not known yet, and Tree.Model.load(T) hands back a
Work that fetches them: the row opens, shows that it is working, and fills in when the
job lands on the UI thread; its spinner steps four times a second instead of turning smoothly
while the user has asked the system for less motion. Collapsing a row that is still loading
cancels the job, because a result nobody is looking at is a result nobody should pay for. A
directory that has not been read is therefore not a leaf — it has a triangle, and pressing it is
what reads it — and one that turns out to hold nothing stays open, with a muted "Empty" line
where its children would be.
Rows are realized where the viewport reaches, by the anchor-and-walk this toolkit's
list and table already use: a row's height is measured when it is first needed, the mean seeds
the scroll estimate (never the tree's own preferred height, which is setVisibleRows(int) seed
rows under an unbounded parent), and two rows are kept mounted even when a scroll carries them
outside: the one holding the keyboard focus, and the cursor row while the tree itself holds the
keyboard, so a reader's cursor survives a wheel, a refresh and a reorder. The order rows are
walked in is a traversal of what is expanded, which is the one thing a tree does that a list
cannot.
What this is not: no columns — a TreeTable is a widget still to come — no
in-place editing, ever, no drag to reorder, and no tri-state checkbox cascade over a data model
the toolkit does not own.
The cursor is not the selection. The row the keyboard is on (cursorNode())
moves with the arrows in every mode and is what Enter activates; the selection
(selectedNodes(), led by leadNode()) follows it where the mode allows, and in
MULTI a row toggled off keeps the cursor. Table keeps the same pair. While the
tree holds the keyboard the cursor row's cell wears a thin focus ring, which is how the cursor
is seen where the selection wash is not on it.
To a screen reader this is a TREE of TREE_ITEMs, each carrying its expanded
state, its selection numbered among its siblings ("2 of 5"), and its depth and flat row index
through the hierarchy facet ("level 3"), the cursor row ACTIVE while the tree holds the
keyboard. All three platform bridges carry those numbers, and macOS also answers the
disclosure attributes VoiceOver reads an outline row by.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic interfaceSomething was dropped on this tree.static interfaceWhat the tree asks the application about its own data.static interfaceNodes of this tree were dragged to another place in it. -
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final DataFormat<List<?>> What a dragged node carries unlesssetDragContent(java.util.function.Function<? super java.util.List<T>, limn.transfer.Transfer>)says otherwise: the nodes themselves, for a widget of this same program to take. -
Constructor Summary
ConstructorsConstructorDescriptionTree(Tree.Model<T> model) A tree overmodel, which supplies the nodes and the widgets that draw them. -
Method Summary
Modifier and TypeMethodDescriptionvoidactivate()Announces that the cursor row was opened, asINVOKED/CODE: a caller's verb, which reaches a watcher and notonActivate, the way Enter does.Whether the scroll bars overlay the rows or reserve strips beside them.Takes the message shown with no rows away, assetEmptyMessage(null)would: that call does not compile, becausenullfits more than one of the overloads.Drops the selection, announced asSELECTION/CODE; nothing happens when nothing is selected.protected booleanWhetherWidget.paintChildren(limn.graphics.Canvas)clips its children to this widget's bounds (scroll views, list viewports, tab strips).Closesnode, cancelling a load still in flight for it.@Nullable T@Nullable DropAction@Nullable Widget<?> Opensnode, loading its children if the model has to fetch them.protected voidhandleUserChange(Change.Aspect aspect) Runs this widget's application handler for a change it has just announced.booleanbooleanbooleanisExpanded(T node) booleanbooleanboolean@Nullable TleadNode()protected voidThe tree itself.protected booleanonAccessibilityAction(Accessible.Action action, Accessible.Argument arg) The tree's one verb of its own:PRESSactivates the cursor row.protected voidonAccessibilityChild(Widget<?> child, Accessibility a) Adds what only this widget knows about one of its children: the role a mounted list cell takes, its position in the data, its selected state.protected booleanonAccessibilityChildAction(Widget<?> child, long key, Accessible.Action action, Accessible.Argument arg) A verb the tree claimed on a row's cell, performed by the rules of the ROWS shape (RowsAccessibility.performOnRow(limn.components.internal.a11y.RowsAccessibility.Host<R>, R, limn.accessibility.Accessible.Action)) overTree<T>.limn.components.tree.Tree.RowsHost, the tree's own mechanisms: each through the seam the equivalent gesture takes atUSER.protected voidonAccessibilityChildIdentity(Widget<?> child, Accessibility a) A row's identity is its node's stable identifier, answered before the cell describes itself so that a cell recycled to another node carries nothing of the old one.onActivate(@Nullable Consumer<T> handler) The application's response to the user opening a row: Enter, a double click, an assistive technology's press.protected voidCalled when this widget enters a scene (attached to the tree).protected booleanonCanEdit(EditCommand command) Whether this widget can do an editing command now: copy with something selected, paste with something to paste, undo with something to take back.onCollapse(@Nullable Consumer<T> handler) Runs when the user closes a row.protected voidCalled when this widget leaves the scene: detached from the tree, moved into another scene (told this before theWidget.onAttached()of the scene it joins), or still in it when the scene's window closes, which detaches every widget the scene has.protected voidonDragExit(DragEvent event) The drag this widget was last asked about inWidget.onDragOver(limn.scene.event.DragEvent)is no longer over it, whether it accepted or not: whatever it drew for the drag goes, the mark of the drop's place or the sign that it refuses.protected voidonDragOver(DragEvent event) A drag is over this widget, or has moved on it.protected @Nullable DragonDragStart(DragStartEvent event) Asked when a drag may begin on this widget: the pointer was pressed here and has moved a few points, or the drag key was pressed while this widget had the focus.onDrop(@Nullable Tree.DropHandler<T> handler) Lets things be dropped on this tree, into a node or between two: what carries one ofthe formats named, from another tree, a list, another program.protected voidWhat was dragged is dropped here: on the widget that accepted it last.protected booleanonEdit(EditCommand command) Does an editing command, as the user's own act.Runs when the user opens a row.protected voidCalled when this widget takes keyboard focus.protected voidCalled when this widget loses keyboard focus.protected @Nullable Widget<?> onHitTest(float localX, float localY) What this widget answers for a point that reached it: one of its children's answers, itself, ornullto let the point through to what is beneath it.protected voidonKeyEvent(KeyEvent event) Key events (focused widget first, then ancestors).protected voidonLayout()Containers position children here (measure +Widget.layoutBox(float, float, float, float)per child).protected SizeonMeasure(Constraints constraints) Reports the size this widget wants withinconstraints.protected voidonMouseEvent(MouseEvent event) Mouse events (bubbling); callevent.consume()when handled.Hears that nodes dragged out of this tree were moved to somewhere else, another widget or another program, and removes them.Lets the user move nodes within this tree.The application's response to the user changing the selection: a click, a key, an assistive technology's select.protected voidpaintChildren(Canvas canvas) Children pass; override to clip (e.g.voidrefresh()Re-reads the model from the roots down, keeping what is expanded and dropping every cached child list: the application changed its data and the tree owns none of it.voidrevealRect(float x, float y, float rectWidth, float rectHeight) Scrolls the minimum amount so the rectangle becomes visible.voidscrollBy(float dx, float dy) Scrolls by a delta in logical points: positivedytoward the last row, positivedxtoward the trailing edge.Selects every open row, inSelectionMode.MULTI: what is visible, so a node under a closed branch is not taken.setBarLayout(ScrollGutters.Layout layout) Sets whether the bars float over the rows or reserve strips of their own (defaultScrollGutters.Layout.OVERLAY), the table's setter under the table's name.setDragContent(@Nullable Function<? super List<T>, Transfer> content) Says what dragged nodes carry, in place ofNODESand their text.setDragEnabled(boolean enabled) Lets the user drag nodes out of this tree (default off): the selected nodes when the node pressed is one of them, and that node alone otherwise; a node goes with everything under it, so one under another that is dragged is not named twice.setDragWithinWindow(boolean within) Keeps a drag of this tree's nodes inside its window (default: not): no other program is offered them, and no other window.setDropAction(@Nullable DropAction action) Says what a drop from elsewhere does here, whatever the source would rather: a tree that only ever copies what it is given asks forDropAction.COPY, one that takes things over forDropAction.MOVE.setDropFormats(DataFormat<?>... formats) Names whatonDrop(DropHandler)takes: a drag carrying any of these is accepted.setEmptyContent(@Nullable Widget<?> content) Showscontentin the rows' viewport whenever the tree has no rows (a tree with no root rows), and takes it away when one arrives: the tree knows its count, so the application never says when.setEmptyContent(@Nullable Widget<?> content, @Nullable Label message) Showscontentwhile there are no rows, assetEmptyContent(Widget)does, and describes the tree bymessagewhile it shows, so a reader landing on it hears "tree, 0 rows" and then the message.setEmptyMessage(@Nullable String message) Shows one fixed line while there are no rows; seesetEmptyMessage(I18nString).setEmptyMessage(@Nullable I18nString message) Shows one muted, wrapped, centred line while there are no rows, which also describes the tree to a reader: the empty content for words alone.setLoading(boolean loading) Says the tree is loading its rows, or has finished.setLoadingContent(@Nullable Widget<?> content) Replaces what shows while the tree loads with no rows;nullbrings back the default, a moving bar over a muted "Loading…" in the tree's language.setLoadingContent(@Nullable Widget<?> content, @Nullable Label message) Replaces what shows while the tree loads with no rows, and describes the tree bymessagewhile it shows.setScrollbarPolicy(ScrollBar.Policy policy) Sets when the scroll bars are shown (defaultScrollBar.Policy.AUTO), the table's setter under the table's name.setSelected(@Nullable T node) Selects exactlynode, moving the cursor onto it and revealing it when it is a visible row.setSelectedNodes(Collection<? extends T> nodes) Selects exactlynodes, in that order, with the last as the lead and under the cursor: what an application restoring a saved selection calls.How many rows may be selected at once; the default isSelectionMode.SINGLE.setVisibleRows(int rows) Sets how many seed rows tall the tree prefers to be when its parent gives it no height (default 8, the table's number): inside a column or a scroll pane, that is its height.intintHow many seed rows tall this tree prefers to be under an unbounded height.Methods inherited from class limn.scene.Widget
acceptsTextInput, accessibleLabelAccessKey, accessibleLabelTarget, accessibleLabelText, add, add, announceChange, backdropReach, baselineOffset, baselineOffsetOf, caretRect, children, clipboard, clipHeight, clipWidth, clipX, clipY, controlSize, cursor, declaredControlSize, declaredLayoutDirection, declaredLocale, focusArrivedBackward, focusArrivedByTraversal, height, hitTest, hitTestChild, id, imageCursor, inheritanceHost, invalidate, invalidate, invalidateAccessible, isEnabled, isFocusable, isFocused, isRightToLeft, isSelfEnabled, isShowing, isVisible, isVisibleByItsOwnFlag, layoutBox, layoutDirection, locale, localToScene, localToSceneBounds, markNeedsContainedLayout, markNeedsLayout, markNeedsLayoutInPlace, maxHeight, maxWidth, measure, minHeight, minWidth, moveChild, moveChild, moveChild, neutralBase, notifyChange, notifyTextEdit, observeChanges, onCharTyped, onPaint, onPaintOverlay, onPreedit, onSyntheticAction, overlayPassesPointer, paintOutset, paintsDecoration, paintsFromBackdrop, paintWidget, parent, remove, remove, requestFocus, requestFocus, revealInView, revealInView, runHandler, scene, sceneNanos, sceneToLocal, self, setAccessibleDescribedBy, setAccessibleDescription, setAccessibleDescription, setAccessibleIgnored, setAccessibleLabelledBy, setAccessibleName, setAccessibleName, setAccessibleRole, setControlSize, setCursor, setEnabled, setFocusable, setId, setImageCursor, setInheritanceHost, setLayoutDirection, setLocale, setMaxHeight, setMaxWidth, setMinHeight, setMinWidth, setPresentedElsewhere, setTooltip, setTooltip, setVisible, shapeText, textRuler, tooltip, tooltipSource, width, x, y
-
Field Details
-
NODES
What a dragged node carries unlesssetDragContent(java.util.function.Function<? super java.util.List<T>, limn.transfer.Transfer>)says otherwise: the nodes themselves, for a widget of this same program to take. Beside it goes their text, one line a node, which is what another program is given.
-
-
Constructor Details
-
Tree
A tree overmodel, which supplies the nodes and the widgets that draw them.
-
-
Method Details
-
refresh
public void refresh()Re-reads the model from the roots down, keeping what is expanded and dropping every cached child list: the application changed its data and the tree owns none of it. An open row whose children the model has to fetch therefore fetches them again, cancelling a load still in flight for it. UI thread only.What the tree remembers about a node — that it is selected, that it is open, that the cursor stands on it — outlives the refresh as long as the model still has the node. A node that is a row afterwards is confirmed by that; one that is not (under a closed branch, or under an open row whose children are being fetched again) is verified along the path it was last seen at, and only that path: a step the model no longer has drops the node, a step whose children are still on their way keeps it until they land, when it is verified again and dropped one announcement later if gone, and a step whose children nobody has fetched keeps it, since confirming it would mean a load nobody asked for. The identifiers of what the model no longer has are released with it.
A row a popup hangs from (a context menu opened on its cell, a popover) is not rebuilt while its node is still a row: its cell stays as it is, at the node's row now, so the popup stays open through a refresh a timer makes, and is built afresh at the first layout after the popup closes. The popup acts on the node the cell was built for, as the model gave it: a model that hands out new node objects equal to the old ones leaves it acting on the old object, so a menu that can outlive a refresh acts through the node's key. When the node is no longer a row the cell is let go of and the popup closes with it, and so is a cell the keyboard is in or returns to when its popup closes, such as a field whose own menu is open: kept, it would stay bound to the node it was built for, and an edit made in it would go there.
-
isExpanded
- Returns:
- whether
nodeis open
-
expand
Opensnode, loading its children if the model has to fetch them. UI thread only. -
collapse
Closesnode, cancelling a load still in flight for it. UI thread only. -
visibleRowCount
public int visibleRowCount()- Returns:
- how many nodes are visible, which is a traversal of what is open. The "Loading…" line under a row whose children are on their way is not one of them.
-
setScrollbarPolicy
Sets when the scroll bars are shown (defaultScrollBar.Policy.AUTO), the table's setter under the table's name.- Parameters:
policy- the policy- Returns:
- this tree
-
setBarLayout
Sets whether the bars float over the rows or reserve strips of their own (defaultScrollGutters.Layout.OVERLAY), the table's setter under the table's name.Reserved is what a tree whose cells end in something usually wants: a count, a badge or a button against a row's trailing edge is exactly what a thumb covers. The strips key on overflow, so a deep tree takes the horizontal one only once it scrolls sideways.
- Parameters:
layout- the layout- Returns:
- this tree
-
barLayout
Whether the scroll bars overlay the rows or reserve strips beside them. -
setVisibleRows
Sets how many seed rows tall the tree prefers to be when its parent gives it no height (default 8, the table's number): inside a column or a scroll pane, that is its height. A count of the size step's seed row, never of the rows realized, so the preference stands whatever scrolls in. A bounded height from the parent wins. UI thread only.- Parameters:
rows- the count, at least one- Returns:
- this tree
- Throws:
IllegalArgumentException- ifrowsis below one
-
visibleRows
public int visibleRows()How many seed rows tall this tree prefers to be under an unbounded height. -
setSelectionMode
How many rows may be selected at once; the default isSelectionMode.SINGLE. The cursor stays where it is in every mode:NONEempties the selection, not the row the keyboard is on. -
selectionMode
- Returns:
- how many rows the user may select: none, one, or any number
-
selectedNodes
- Returns:
- the selected nodes, in the order they were selected
-
cursorNode
- Returns:
- the row the keyboard is on, or
nullbefore the keyboard was in the tree: what the arrows move, what Enter activates, and the row a reader's cursor stands on. It moves in every mode,SelectionMode.NONEincluded, and is not the selection; seeleadNode()for that.
-
leadNode
- Returns:
- the selection's lead — the node the user selected last that is still selected —
or
nullwhen nothing is selected. Until 2026-09-14 this was also the row the keyboard was on, whichcursorNode()answers now: the two part inMULTI, where a row toggled off keeps the cursor and loses the lead, and inNONE, where the cursor moves and this is alwaysnull.
-
setSelected
Selects exactlynode, moving the cursor onto it and revealing it when it is a visible row. A node under a closed branch is selected where it is — re-opening its parent finds it selected — and neither revealed nor made the cursor, which stays on a row the user can see.nullclears the selection and leaves the cursor where it is. -
setSelectedNodes
Selects exactlynodes, in that order, with the last as the lead and under the cursor: what an application restoring a saved selection calls. InSelectionMode.SINGLEonly one node may be named. AnnouncesSELECTION/CODEonce when the set changed. UI thread only.- Parameters:
nodes- the nodes; none clears the selection- Returns:
- this tree
- Throws:
IllegalStateException- if the mode isSelectionMode.NONE, or SINGLE and more than one node is named
-
clearSelection
Drops the selection, announced asSELECTION/CODE; nothing happens when nothing is selected. The cursor stays where it is. UI thread only.- Returns:
- this tree
-
selectAll
Selects every open row, inSelectionMode.MULTI: what is visible, so a node under a closed branch is not taken. The lead is kept where it is in the selection, or becomes the first row; the cursor does not move. A caller's write, announced asSELECTION/CODE; Ctrl+A or Cmd+A enters the same seam as the user's. UI thread only.- Returns:
- this tree
-
activate
public void activate()Announces that the cursor row was opened, asINVOKED/CODE: a caller's verb, which reaches a watcher and notonActivate, the way Enter does. Nothing without a cursor row. UI thread only. -
onSelect
The application's response to the user changing the selection: a click, a key, an assistive technology's select. The selection is read back fromselectedNodes(), and the lead fromleadNode(); the handler takes no node because a selection is a set, and the node a toggle removed is not one to hand anybody. Never forsetSelected(T), or arefresh()orsetSelectionMode(limn.components.SelectionMode)that moved it, which are the caller's or the tree's own; to hear every change whatever caused it, watch the tree.- Parameters:
handler- the handler, ornullto clear the slot- Returns:
- this tree
- Throws:
IllegalStateException- if a handler is already registered
-
onActivate
The application's response to the user opening a row: Enter, a double click, an assistive technology's press. Handed the row that was opened — the cursor row for Enter and a double click, which inNONEis a row that was never selected, and the addressed row for a reader'sPRESS, which need not be the cursor's. Never foractivate(), which is a caller's verb.- Parameters:
handler- the handler, ornullto clear the slot- Returns:
- this tree
- Throws:
IllegalStateException- if a handler is already registered
-
onExpand
Runs when the user opens a row.One handler at a time:
nullclears it, and setting a second while one is set throwsIllegalStateException. -
onCollapse
Runs when the user closes a row.One handler at a time:
nullclears it, and setting a second while one is set throwsIllegalStateException. -
handleUserChange
Description copied from class:WidgetRuns this widget's application handler for a change it has just announced. Called byWidget.runHandler(limn.scene.Change), and so byWidget.notifyChange(limn.scene.Change), only for aUSERorigin and after every watcher has run.A component overrides it once and switches on the aspect, reading its own accessors for the payload: a slider passes
value(), a combo boxselectedIndex(), a text fieldtext(). A component with no handler does not override it.An override that does not recognise the aspect must call
super, because a subclass of a component that has a handler inherits that component's dispatch through this method: aSearchFieldthat overrode this for its own aspect and did not chain would silently deleteTextField's. The ordering lives in the base class, this is the one link in it a subclass can break, and the contract test drives every subclass through its parent's gestures to catch one that does.- Overrides:
handleUserChangein classWidget<Tree<T>>- Parameters:
aspect- what changed; the origin isUSERor this is not called
-
onMeasure
Description copied from class:WidgetReports the size this widget wants withinconstraints. Called once per layout pass, and the result is cached against the constraints and the resolved axes (size step, layout direction, locale), so it must be a pure function of them and of this widget's own state.Resolve the
ControlSizeand theLayoutDirectiononce each here and thread them down; never read either in a constructor. The locale needs no threading: it is in scope, andI18n.locale()answers it wherever text is resolved. -
setEmptyContent
Showscontentin the rows' viewport whenever the tree has no rows (a tree with no root rows), and takes it away when one arrives: the tree knows its count, so the application never says when. It gives the words, "No orders" or "No orders match", which only it knows.The content is the tree's last child while it shows, so its own controls (a "Clear filter" button) follow the tree in the Tab order and a reader finds them after its (no) rows; they belong to no selection. It is this tree's own: one already in another container is refused, and one given to two widgets shows in one only. A content passed this way describes nothing to a reader: pass its message with
setEmptyContent(Widget, Label).- Parameters:
content- the content, ornullfor none- Returns:
- this tree
- Throws:
IllegalArgumentException- whencontentis already in another container (another widget showing it included), or holds this tree
-
setEmptyContent
Showscontentwhile there are no rows, assetEmptyContent(Widget)does, and describes the tree bymessagewhile it shows, so a reader landing on it hears "tree, 0 rows" and then the message.- Parameters:
content- the content, ornullfor nonemessage- the label inside it that says why there are no rows, ornull- Returns:
- this tree
- Throws:
IllegalArgumentException- whencontentis already in another container (another widget showing it included), or holds this tree
-
setEmptyMessage
Shows one muted, wrapped, centred line while there are no rows, which also describes the tree to a reader: the empty content for words alone.- Parameters:
message- the words, ornullfor no empty content- Returns:
- this tree
-
setEmptyMessage
Shows one fixed line while there are no rows; seesetEmptyMessage(I18nString).- Parameters:
message- the words, ornullfor no empty content- Returns:
- this tree
-
clearEmptyMessage
Takes the message shown with no rows away, assetEmptyMessage(null)would: that call does not compile, becausenullfits more than one of the overloads.- Returns:
- this tree
-
emptyContent
- Returns:
- the content shown while there are no rows, or
null
-
isShowingEmptyContent
public boolean isShowingEmptyContent()- Returns:
- whether the empty content shows now, as the last layout placed it: there is one, no rows and no load running, and no other widget holds it
-
setLoading
Says the tree is loading its rows, or has finished. While it loads with no rows, the loading content shows in the viewport in place of the empty one; over rows already shown, they stay. Either way the tree is published busy, and a reader is told both ends: "Loading Orders" at the start, with the name a reader hears the tree by (given bysetAccessibleName, or by a caption bound withLabel.setLabelFor(limn.scene.Widget<?>)), and at the end how many rows arrived, or the empty message when none did. A tree in a hidden container, on a tab page not chosen or on a face a transform does not draw is busy and says nothing of its load. Set the rows first, then say the load ended, so the count said is the count shown.- Parameters:
loading- whether a load is running- Returns:
- this tree
-
isLoading
public boolean isLoading()- Returns:
- whether a load is running, as
setLoading(boolean)last said
-
setLoadingContent
Replaces what shows while the tree loads with no rows;nullbrings back the default, a moving bar over a muted "Loading…" in the tree's language.- Parameters:
content- the content, ornullfor the default- Returns:
- this tree
- Throws:
IllegalArgumentException- whencontentis already in another container (another widget showing it included), or holds this tree
-
setLoadingContent
Replaces what shows while the tree loads with no rows, and describes the tree bymessagewhile it shows.- Parameters:
content- the content, ornullfor the defaultmessage- the label inside it that says what is loading, ornull- Returns:
- this tree
- Throws:
IllegalArgumentException- whencontentis already in another container (another widget showing it included), or holds this tree
-
isShowingLoadingContent
public boolean isShowingLoadingContent()- Returns:
- whether a loading content, the application's or the default, shows now, as the last layout placed it
-
onLayout
protected void onLayout()Description copied from class:WidgetContainers position children here (measure +Widget.layoutBox(float, float, float, float)per child). -
scrollBy
public void scrollBy(float dx, float dy) Scrolls by a delta in logical points: positivedytoward the last row, positivedxtoward the trailing edge. A tree whose content fits its box has nothing to do sideways. UI thread only.- Parameters:
dx- how far sidewaysdy- how far along the rows
-
revealRect
public void revealRect(float x, float y, float rectWidth, float rectHeight) Description copied from interface:ScrollableScrolls the minimum amount so the rectangle becomes visible. The rectangle is given in this widget's local (viewport) coordinates, i.e. the same space a child'sx()/y()resolve to. Rectangles larger than the viewport align their near edge. Implementations clamp to their content bounds; already-visible rects are a no-op. UI thread only.- Specified by:
revealRectin interfaceScrollable
-
setDragEnabled
Lets the user drag nodes out of this tree (default off): the selected nodes when the node pressed is one of them, and that node alone otherwise; a node goes with everything under it, so one under another that is dragged is not named twice. From the keyboard, the scene's drag key picks them up. What they carry isNODESand their text, or whatsetDragContent(java.util.function.Function<? super java.util.List<T>, limn.transfer.Transfer>)makes. A target is offered a copy; a move too onceonReorderoronMoveOutis set, and a move first with the latter. A row's own controls and its triangle keep their gestures. UI thread only.- Parameters:
enabled- whether nodes can be dragged- Returns:
- this tree
-
isDragEnabled
public boolean isDragEnabled()- Returns:
- whether nodes can be dragged
-
setDragContent
Says what dragged nodes carry, in place ofNODESand their text. UI thread only.- Parameters:
content- makes the transfer for the nodes dragged, ornullfor the default- Returns:
- this tree
-
onReorder
Lets the user move nodes within this tree. Dragged, whendragging is on: dropped on a node they go into it, near its upper or lower edge before or after it, never into themselves, and a closed node the pointer rests on opens. And by Alt+Shift with an arrow: Up and Down among its siblings, Left out one level, Right into the sibling before it. The handler moves the data; the tree then shows it, opens the node they went into and says where they are. UI thread only.- Parameters:
handler- moves the nodes, ornullfor a tree that does not rearrange- Returns:
- this tree
-
onMoveOut
Hears that nodes dragged out of this tree were moved to somewhere else, another widget or another program, and removes them. With it a drag proposes a move unless the user's modifier key asks for a copy; without it, a copy. UI thread only.- Parameters:
handler- removes the nodes, ornull- Returns:
- this tree
-
onDrop
Lets things be dropped on this tree, into a node or between two: what carries one ofthe formats named, from another tree, a list, another program. UI thread only.- Parameters:
handler- takes a drop, ornullfor a tree that takes none- Returns:
- this tree
-
setDropFormats
Names whatonDrop(DropHandler)takes: a drag carrying any of these is accepted. UI thread only.- Parameters:
formats- the formats taken; none takes nothing- Returns:
- this tree
-
setDropAction
Says what a drop from elsewhere does here, whatever the source would rather: a tree that only ever copies what it is given asks forDropAction.COPY, one that takes things over forDropAction.MOVE. It is what the drop does where the source allows it; where it does not, and by default, the drop does what the user is asking for, the action the modifier keys name or the source's own preference. Nodes moved within this tree are always a move. UI thread only.- Parameters:
action- the action asked for, ornullfor the one proposed- Returns:
- this tree
-
dropAction
- Returns:
- the action a drop from elsewhere is asked to be, or null for the one proposed
-
setDragWithinWindow
Keeps a drag of this tree's nodes inside its window (default: not): no other program is offered them, and no other window. For nodes that mean nothing anywhere else. UI thread only.- Parameters:
within- whether a drag stays in the window it began in- Returns:
- this tree
-
isDragWithinWindow
public boolean isDragWithinWindow()- Returns:
- whether a drag of this tree's nodes stays in its window
-
onDragStart
Description copied from class:WidgetAsked when a drag may begin on this widget: the pointer was pressed here and has moved a few points, or the drag key was pressed while this widget had the focus. The question bubbles from the widget under the pointer, and the firstDraganswered starts the drag; the press is then over for the widget that had it. Default:null, which leaves the gesture to theDRAGevents it has always been.- Overrides:
onDragStartin classWidget<Tree<T>>- Parameters:
event- where the press began, and whether the keyboard asked- Returns:
- what to drag, or null
-
onDragOver
Description copied from class:WidgetA drag is over this widget, or has moved on it. A widget that would take what is offered callsevent.accept()and marks where the drop would land; one that says nothing passes the question to its parent, and may show that it refuses. The question goes to the widget under the pointer and then to each enabled ancestor in turn, up to the first that accepts. It is asked again on every move, so that the answer can depend on the place, and again when the content under a still pointer scrolls: a pointer drag resting near an edge of a scrolling view it is over, or past one, scrolls that view. In a keyboard drag only the widget the drag key, Tab or an arrow brings the drag to is asked. Files dragged in from the system arrive the same way, their offer holdingDataFormat.FILES. Whatever this draws, accepting or not,Widget.onDragExit(limn.scene.event.DragEvent)takes away. Default: says nothing.- Overrides:
onDragOverin classWidget<Tree<T>>- Parameters:
event- what is offered and where
-
onDragExit
Description copied from class:WidgetThe drag this widget was last asked about inWidget.onDragOver(limn.scene.event.DragEvent)is no longer over it, whether it accepted or not: whatever it drew for the drag goes, the mark of the drop's place or the sign that it refuses. It comes once, at the first of these: a move whose question no longer reaches this widget (the pointer left it, or a widget under the pointer accepted before it was asked); the drag called off, by Escape, another button, the window losing the focus or the source leaving; the drag leaving the window; the drop, on another widget or over this one with no widget taking it; this widget taken out of the scene, hidden or disabled while the drag is over it. In a keyboard drag a widget that does not accept hears it at once, the drag going on to the next, and the target when the drag moves on or ends. A drag from another window or program is told the same. Never afterWidget.onDrop(limn.scene.event.DropEvent): the widget handed the drop hears no exit. Default: nothing.- Overrides:
onDragExitin classWidget<Tree<T>>- Parameters:
event- the drag as it leaves
-
onDrop
Description copied from class:WidgetWhat was dragged is dropped here: on the widget that accepted it last. Read what is wanted fromevent.offer()and carry outevent.action(); whatever marked the place goes now, since noWidget.onDragExit(limn.scene.event.DragEvent)follows. Default: nothing. -
onCanEdit
Description copied from class:WidgetWhether this widget can do an editing command now: copy with something selected, paste with something to paste, undo with something to take back.Scene.canEdit(limn.scene.EditCommand)asks the widget that has the keyboard and then each of its ancestors, and a menu's item of an editing role is enabled by the answer.A widget answers for the commands it takes from the keyboard, and does for the command what it does for the key.
-
onEdit
Description copied from class:WidgetDoes an editing command, as the user's own act. Called on the first widget, from the one with the keyboard upward, whoseWidget.onCanEdit(limn.scene.EditCommand)answeredtrue. -
onKeyEvent
Description copied from class:WidgetKey events (focused widget first, then ancestors).- Overrides:
onKeyEventin classWidget<Tree<T>>
-
onMouseEvent
Description copied from class:WidgetMouse events (bubbling); callevent.consume()when handled.- Overrides:
onMouseEventin classWidget<Tree<T>>
-
onHitTest
Description copied from class:WidgetWhat this widget answers for a point that reached it: one of its children's answers, itself, ornullto let the point through to what is beneath it. Called byWidget.hitTest(float, float)only for a widget that is visible, enabled and in reach, with the point inside its box or where it draws a descendant moved past it, so an override checks none of that again.The default asks each child through
Widget.hitTestChild(limn.scene.Widget<?>, float, float), the last first because it is painted on top, and answers the first that is hit, or this widget when none is. A widget overrides it to ask one child before the others (a scroll bar over the content it scrolls), to keep a part of itself from its children, or to be transparent to the pointer. -
clipsChildren
protected boolean clipsChildren()Description copied from class:WidgetWhetherWidget.paintChildren(limn.graphics.Canvas)clips its children to this widget's bounds (scroll views, list viewports, tab strips). Partial rendering uses it to clamp a descendant'sWidget.invalidate()damage to the visible region; a widget scrolled out of view damages nothing. Any override that clips inpaintChildrenshould also override this, and one whose clip is smaller than its box for some child should override the four clip accessors below too.- Overrides:
clipsChildrenin classWidget<Tree<T>>
-
paintChildren
Description copied from class:WidgetChildren pass; override to clip (e.g. scroll views).Indexed rather than an enhanced
for, for the reasonScene's own overlay loop already carries: an iterator here is an allocation per widget per frame, and an idle window is the one place in this toolkit that is not allowed to allocate at all. A subtree of two hundred widgets was paying two hundred short-lived objects a frame to walk a list it can index.- Overrides:
paintChildrenin classWidget<Tree<T>>
-
onAttached
protected void onAttached()Description copied from class:WidgetCalled when this widget enters a scene (attached to the tree).Widget.scene()is the scene it just joined. Fires top-down, so a parent runs before its children. Default no-op.- Overrides:
onAttachedin classWidget<Tree<T>>
-
onFocusGained
protected void onFocusGained()Description copied from class:WidgetCalled when this widget takes keyboard focus. Default: nothing.- Overrides:
onFocusGainedin classWidget<Tree<T>>
-
onFocusLost
protected void onFocusLost()Description copied from class:WidgetCalled when this widget loses keyboard focus. Default: nothing.- Overrides:
onFocusLostin classWidget<Tree<T>>
-
onDetached
protected void onDetached()Description copied from class:WidgetCalled when this widget leaves the scene: detached from the tree, moved into another scene (told this before theWidget.onAttached()of the scene it joins), or still in it when the scene's window closes, which detaches every widget the scene has. What was taken inWidget.onAttached()from something that outlives the window (a listener's place in a list of the process's, a resource of the system's) is given back here.Widget.scene()still answers the scene being left, and becomesnullonce this returns, so releasing something the scene owns needs no field of your own to remember it by. Fires bottom-up, so children run before their parent, and every one of them can still reach the scene.Release resources here, but GPU resources must be handed to
Scene.disposeLater(limn.graphics.GpuSurface)(disposal needs the owning GL context, which is only current inside a frame). Not called onWidget.setVisible(boolean). Default no-op.- Overrides:
onDetachedin classWidget<Tree<T>>
-
onAccessibility
The tree itself.TREE, since 2026-09-13, when the AT-SPI number the role was waiting on came off the Fedora guest. Until then it publishedLIST, because a role with no number announces as "invalid" on Linux and a list of items that can open was the truthful answer on all three platforms.- Overrides:
onAccessibilityin classWidget<Tree<T>>- Parameters:
a- the node being described; write into it, and never keep it
-
onAccessibilityChildIdentity
A row's identity is its node's stable identifier, answered before the cell describes itself so that a cell recycled to another node carries nothing of the old one.- Overrides:
onAccessibilityChildIdentityin classWidget<Tree<T>>- Parameters:
child- the child about to be describeda- the builder, open only for the two identity calls
-
onAccessibilityChild
Description copied from class:WidgetAdds what only this widget knows about one of its children: the role a mounted list cell takes, its position in the data, its selected state.Called on the UI thread with the child's node current and this widget's locale in scope, after the child has described itself, so what is written here wins. The child's identity is not decided here but in
Widget.onAccessibilityChildIdentity(limn.scene.Widget<?>, limn.accessibility.Accessibility), which runs first;Accessibility.key(long)refuses to be called from here.This is the only place
Accessibility.delegate(limn.accessibility.Accessible.Action)may be called: a verb this widget claims on the child — a list'sSELECTon a row — is published on the child's node and routed toWidget.onAccessibilityChildAction(limn.scene.Widget<?>, long, limn.accessibility.Accessible.Action, limn.accessibility.Accessible.Argument). The builder refuses a delegation from anywhere else, and refuses one here for a verb the child declared for itself, for a verb that takes an argument, and forFOCUSorSCROLL_INTO_VIEWon a focusable child, which the walk performs on its own. A verb written here withAccessibility.action(limn.accessibility.Accessible.Action)would be dispatched to the child's ownWidget.onAccessibilityAction(limn.accessibility.Accessible.Action, limn.accessibility.Accessible.Argument), which is why a child hook writes facts and delegates verbs.- Overrides:
onAccessibilityChildin classWidget<Tree<T>>- Parameters:
child- the child being describeda- the child's node
-
onAccessibilityChildAction
protected boolean onAccessibilityChildAction(Widget<?> child, long key, Accessible.Action action, Accessible.Argument arg) A verb the tree claimed on a row's cell, performed by the rules of the ROWS shape (RowsAccessibility.performOnRow(limn.components.internal.a11y.RowsAccessibility.Host<R>, R, limn.accessibility.Accessible.Action)) overTree<T>.limn.components.tree.Tree.RowsHost, the tree's own mechanisms: each through the seam the equivalent gesture takes atUSER. A verb the row did not publish is refused, which the platform never learns of (the published list is the only refusal it sees).- Overrides:
onAccessibilityChildActionin classWidget<Tree<T>>- Parameters:
child- the child the verb was addressed tokey- the identity key this widget gave that child, or0when it gave noneaction- what was askedarg- the argument, orAccessible.Argument.NONE- Returns:
- whether this widget did it
-
onAccessibilityAction
The tree's one verb of its own:PRESSactivates the cursor row.- Overrides:
onAccessibilityActionin classWidget<Tree<T>>- Parameters:
action- what was asked; one of the seventeen, including the four that carry an argumentarg- the argument, orAccessible.Argument.NONE- Returns:
- whether this widget did it.
falseis the honest answer for a verb it does not offer.
-