Class ListView<T>
- All Implemented Interfaces:
Scrollable
JTable/RecyclerView: only the rows in the viewport get a
widget, so a list of a million rows costs the same as one of twenty (layout
and paint are O(visible)).
Rows may have different heights: e.g. small grouping headers among normal cards. There is no global height cache and no binary search: the list keeps an anchor (which row sits where) and walks a handful of rows from it each frame, measuring each on demand. Scroll-thumb size/position are estimated from the average measured height (good enough for a scroll indicator).
The items are yours: a List<T> the application owns, handed over with
setItems(java.util.List<T>) and re-read after a change with refresh(), as a Table's
rows are. A row's widget comes from a cell function, made fresh for each row that comes into
view (ListView(Function)), from a pool that binds a recycled widget to its item
(pooled(java.util.function.Supplier<? extends W>, java.util.function.BiConsumer<? super W, ? super T>)), or from a cell function the application pools behind itself, handed each
widget back as it scrolls out (ListView(Function, Consumer)). The list owns only the
tree and the positioning.
Selection is a SelectionMode: SINGLE by default, NONE, or
MULTI with the gestures Table and Tree share. The keyboard cursor is
not the selection in NONE and MULTI; in SINGLE they are one.
Interaction: the wheel scrolls (with the shared ScrollBar); the
list is focusable and, while focused, Up/Down/Home/End/PageUp/PageDown move a
highlighted selection (auto-scrolling to reveal it) and Enter activates it;
clicking a row selects it (clicks on a row's own buttons reach those buttons).
Widget.children() is the bar and then the realized rows in data order, whichever
direction the scroll realized them in — which is what makes a Tab through those row buttons, and
the reading order an assistive technology is given, agree with the list on screen.
The row holding the keyboard focus is realized for as long as it holds it, wherever a scroll has taken the viewport: it stays mounted in data order, laid out outside the viewport, unpainted and unreachable by the pointer, and published to an assistive technology as the list item it is, not showing. A screen reader whose cursor follows the focus onto a row is otherwise left standing on a node a page scroll deleted. Every other row outside the viewport goes back back to its cell source, and so does that one the moment the focus leaves it or the data is refreshed. And so is the selected row while the list itself holds the keyboard: the selection is the reader's cursor here, and a wheel or a bar drag that scrolled it away used to recycle it, leaving the reader's cursor on nothing until the next arrow key. It is kept the same way — mounted, outside the viewport, published not showing and still the cursor — across a refresh too, and released by the first pass after the keyboard leaves.
Rows can be dragged, once setDragEnabled(boolean) says so: the selected rows, to
another place in this list (onReorder), to another widget or to another program. And
things can be dropped between its rows (onDrop(DropHandler)). The items stay the
application's: the list never rearranges them, it asks, and shows what it is handed back.
Size steps propagate rather than being imposed. Rows are application-supplied
widgets in this list's subtree, so they resolve the ControlSize
themselves and list.setControlSize(SMALL) shortens them because they
re-measure. Only three metrics are the list's own: the row-height seed, used for every
scroll estimate before anything has been measured and for the intrinsic height under an
unbounded constraint always (setVisibleRows(int) rows of it), the intrinsic width under
an unbounded constraint, and the selection ring's corner radius.
Inside a scroller the list scrolls itself first and hands the wheel on at either end: a detent that finds this list already at the top or the bottom is left unconsumed and reaches the scroll pane that holds it.
The scroll bar does not take part in the size axis (ScrollBar.thickness()
is 15 pt at every step), and it overlays the rows rather than insetting them, so at a
compact step it covers a larger fraction of a shorter row. An accepted cost of one
scrollbar geometry process-wide.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic interfaceSomething was dropped between two rows of this list.static interfaceRows of this list were dragged to another place in it. -
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final DataFormat<List<?>> What a dragged row carries unlesssetDragContent(java.util.function.Function<? super java.util.List<T>, limn.transfer.Transfer>)says otherwise: the items themselves, for a widget of this same program to take. -
Constructor Summary
ConstructorsConstructorDescriptionA list whose rows are made bycellFor, a fresh widget each time a row comes into view; nothing is recycled.A list whose rows are made bycellForand handed back torecyclewhen they scroll out, so the application can keep them and hand them out again: the form for rows of several kinds, each pooled on its own, whichpooled(java.util.function.Supplier<? extends W>, java.util.function.BiConsumer<? super W, ? super T>)does not cover. -
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 scrollbar overlays the rows or reserves a gutter.Takes the message shown with no rows away, assetEmptyMessage(null)would: that call does not compile, becausenullfits more than one of the overloads.protected booleanWhetherWidget.paintChildren(limn.graphics.Canvas)clips its children to this widget's bounds (scroll views, list viewports, tab strips).intThe row the keyboard is on, or-1: what the arrows move, what Enter opens, and what wears the focus ring while the list holds the keyboard.@Nullable DropAction@Nullable Widget<?> intprotected voidhandleUserChange(Change.Aspect aspect) Runs this widget's application handler for a change it has just announced.booleanbooleanbooleanbooleanisSelected(int index) booleanbooleanitems()protected voidOne list node over the rows it has realized, carrying where the viewport sits and which row the cursor is on.protected booleanonAccessibilityAction(Accessible.Action action, Accessible.Argument arg) Opens the selected row, through the sameUSERseam Enter reaches -- and not through the publicactivate(), which is a caller's verb and reaches no handler.protected voidonAccessibilityChild(Widget<?> child, Accessibility a) What only the list knows about a mounted row: which row it is, that it is one, where it sits in the data, and whether the cursor is on it.protected booleanonAccessibilityChildAction(Widget<?> child, long key, Accessible.Action action, Accessible.Argument arg) A verb the list 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)) overListView<T>.limn.components.ListView.RowsHost:SELECTmakes that row the selection, as a click on it does, through the sameUSERseam and with the same reveal;SCROLL_INTO_VIEWreveals the row where it is, as the walk's free verb reveals a focusable one, and moves neither the selection nor the cursor.protected voidonAccessibilityChildIdentity(Widget<?> child, Accessibility a) A mounted cell's identity is its data index, answered before the cell describes itself so that its name is carried over under that identity and whatever the cell holds inside it follows the row when the cell is recycled.onActivate(@Nullable IntConsumer handler) The application's response to the user opening the cursor row: Enter, a double click, or an assistive technology's press.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.protected voidLeaving the window, the empty or loading content shown goes back to being nobody's child.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 ListView.DropHandler handler) Lets things be dropped between the rows of this list: what carries one ofthe formats named, from another list, another widget or 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.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 rows dragged out of this list were moved to somewhere else, another widget or another program, and removes them.onReorder(@Nullable ListView.Reorder<T> handler) Lets the user move rows within this list: dragged, whendragging is on, and with Alt+Shift+Up and Alt+Shift+Down, which move the selected rows one place.The application's response to the user changing the selection: a click, a key, or an assistive technology's select.protected voidpaintChildren(Canvas canvas) Children pass; override to clip (e.g.pooled(Supplier<? extends W> create, BiConsumer<? super W, ? super T> bind) A list whose row widgets are pooled: a widget that scrolls out is kept, and bound to the next item that comes into view instead of a new one being made.voidrefresh()Re-reads the items and re-lays out (call after changing the list in place).voidrevealRect(float x, float y, float rectWidth, float rectHeight) Scrolls the minimum so the rect (in viewport coordinates) becomes visible.introwCount()Row count: the size of the items.voidscrollBy(float dy) Scrolls by a delta in logical points (positive = toward the end).Selects every row, inSelectionMode.MULTI; nothing in the other modes.intThe lead of the selection: the row selected most recently that is still selected, or-1when nothing is.int[]@Nullable TsetBarLayout(ScrollGutters.Layout layout) Sets whether the bar floats over the rows or reserves a strip of its own (defaultScrollGutters.Layout.OVERLAY).setDragContent(@Nullable Function<? super List<T>, Transfer> content) Says what dragged rows carry, in place ofITEMSand their text: the application's own format, a file, a picture.setDragEnabled(boolean enabled) Lets the user drag rows out of this list (default off): the selected rows when the row pressed is one of them, and that row alone otherwise.setDragWithinWindow(boolean within) Keeps a drag of this list's rows 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 list 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 list has no rows (a list with no items), and takes it away when one arrives: the list 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 list bymessagewhile it shows, so a reader landing on it hears "list, 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 list to a reader: the empty content for words alone.setItemName(@Nullable Function<? super T, ? extends I18nString> name) What to call a row for an assistive technology, when its own cell says nothing about itself — the common case, because a cell that paints its own text usually declares nothing, and theLIST_ITEMrole the list writes onto it suppresses the warning that would have been the application's only notice.ShowsnewItems, which the application keeps: the list reads it where it is and never copies or changes it.setLoading(boolean loading) Says the list is loading its rows, or has finished.setLoadingContent(@Nullable Widget<?> content) Replaces what shows while the list loads with no rows;nullbrings back the default, a moving bar over a muted "Loading…" in the list's language.setLoadingContent(@Nullable Widget<?> content, @Nullable Label message) Replaces what shows while the list loads with no rows, and describes the list bymessagewhile it shows.setScrollbarPolicy(ScrollBar.Policy policy) Sets when the vertical scrollbar is shown (defaultScrollBar.Policy.AUTO).setSelectedIndex(int index) Selects one row, alone, and scrolls it into view: a caller's write, so it announcesSELECTION/CODEand reaches no handler.Sets how many rows the user may select (defaultSelectionMode.SINGLE).setVisibleRows(int rows) Sets how many rows tall this list prefers to be when its parent gives it no height — a list inside aScrollViewor an unconstrained column — as a count of the step's seed rows (default 6).intHow many seed rows tall this list 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, onAttached, 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
-
ITEMS
What a dragged row carries unlesssetDragContent(java.util.function.Function<? super java.util.List<T>, limn.transfer.Transfer>)says otherwise: the items themselves, for a widget of this same program to take. Beside it goes their text, one line an item, which is what another program is given.
-
-
Constructor Details
-
ListView
A list whose rows are made bycellFor, a fresh widget each time a row comes into view; nothing is recycled. The items start empty: seesetItems(java.util.List<T>).- Parameters:
cellFor- the widget for an item, populated and ready to show; nevernull
-
ListView
public ListView(Function<? super T, ? extends Widget<?>> cellFor, Consumer<? super Widget<?>> recycle) A list whose rows are made bycellForand handed back torecyclewhen they scroll out, so the application can keep them and hand them out again: the form for rows of several kinds, each pooled on its own, whichpooled(java.util.function.Supplier<? extends W>, java.util.function.BiConsumer<? super W, ? super T>)does not cover.- Parameters:
cellFor- the widget for an item, populated and ready to show; nevernull. It may be onerecyclewas given earlier.recycle- receives a row widget that scrolled out
-
-
Method Details
-
pooled
public static <T,W extends Widget> ListView<T> pooled(Supplier<? extends W> create, BiConsumer<? super W, ? super T> bind) A list whose row widgets are pooled: a widget that scrolls out is kept, and bound to the next item that comes into view instead of a new one being made. What a long list of the same kind of row wants.- Type Parameters:
T- the item typeW- the row widget type- Parameters:
create- makes a row widget when the pool is emptybind- fills a row widget with an item, a recycled one included- Returns:
- the list, with no items yet
-
setBarLayout
Sets whether the bar floats over the rows or reserves a strip of its own (defaultScrollGutters.Layout.OVERLAY). Reserved is what a list of records usually wants: the trailing edge of a row is where a count, a date or a status chip goes, and a thumb over it is a defect. Trailing and not right: the bar and the row's last column are both on the left of a list that reads right to left, and they move there together. -
barLayout
Whether the scrollbar overlays the rows or reserves a gutter. -
setScrollbarPolicy
Sets when the vertical scrollbar is shown (defaultScrollBar.Policy.AUTO). -
setVisibleRows
Sets how many rows tall this list prefers to be when its parent gives it no height — a list inside aScrollViewor an unconstrained column — as a count of the step's seed rows (default 6). A bounded height from the parent always wins; this is the free-axis fallback only. The preference is the seed's and not the realized rows' on purpose: a preference that followed the measured average moved every time a row of another height scrolled in, and re-laid out the parent with it. UI thread only.- Parameters:
rows- a row count of at least one- Returns:
- this list
- Throws:
IllegalArgumentException- ifrowsis below one
-
visibleRows
public int visibleRows()How many seed rows tall this list prefers to be under an unbounded height. -
onSelect
The application's response to the user changing the selection: a click, a key, or an assistive technology's select. Never forsetSelectedIndex(int),clearSelection(),selectAll(),setItems(java.util.List<T>)or arefresh()that collapsed the selection, which are the caller's or the list's own; to hear every change whatever caused it, watch the list instead. The handler reads what it needs:selectedIndex(),selectedIndices(),selectedItem().- Parameters:
handler- the handler, ornullto clear the slot- Returns:
- this list
- Throws:
IllegalStateException- if a handler is already registered
-
onActivate
The application's response to the user opening the cursor row: Enter, a double click, or an assistive technology's press. Handed the row's index, asTable's is. Never foractivate(), which is a caller's verb.- Parameters:
handler- the handler, ornullto clear the slot- Returns:
- this list
- Throws:
IllegalStateException- if a handler is already registered
-
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<ListView<T>>- Parameters:
aspect- what changed; the origin isUSERor this is not called
-
setDragEnabled
Lets the user drag rows out of this list (default off): the selected rows when the row pressed is one of them, and that row alone otherwise. From the keyboard, the scene's drag key picks up the selection. What they carry isITEMSand 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 (a button, a field) keep their gestures. UI thread only.- Parameters:
enabled- whether rows can be dragged- Returns:
- this list
-
isDragEnabled
public boolean isDragEnabled()- Returns:
- whether rows can be dragged
-
setDragContent
Says what dragged rows carry, in place ofITEMSand their text: the application's own format, a file, a picture. UI thread only.- Parameters:
content- makes the transfer for the rows dragged, ornullfor the default- Returns:
- this list
-
onReorder
Lets the user move rows within this list: dragged, whendragging is on, and with Alt+Shift+Up and Alt+Shift+Down, which move the selected rows one place. The handler moves the items; the list then shows them, keeps them selected and says where they went. UI thread only.- Parameters:
handler- moves the items, ornullfor a list that does not reorder- Returns:
- this list
-
onMoveOut
Hears that rows dragged out of this list 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 items, ornull- Returns:
- this list
-
onDrop
Lets things be dropped between the rows of this list: what carries one ofthe formats named, from another list, another widget or another program. A line shows where it would land. UI thread only.- Parameters:
handler- takes a drop, ornullfor a list that takes none- Returns:
- this list
-
setDropFormats
Names whatonDrop(DropHandler)takes: a drag carrying any of these is accepted.ITEMStakes another list's rows,DataFormat.FILESfiles. UI thread only.- Parameters:
formats- the formats taken; none takes nothing- Returns:
- this list
-
setDropAction
Says what a drop from elsewhere does here, whatever the source would rather: a list 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. Rows moved within this list are always a move. UI thread only.- Parameters:
action- the action asked for, ornullfor the one proposed- Returns:
- this list
-
dropAction
- Returns:
- the action a drop from elsewhere is asked to be, or null for the one proposed
-
setDragWithinWindow
Keeps a drag of this list's rows inside its window (default: not): no other program is offered them, and no other window. For rows that mean nothing anywhere else. UI thread only.- Parameters:
within- whether a drag stays in the window it began in- Returns:
- this list
-
isDragWithinWindow
public boolean isDragWithinWindow()- Returns:
- whether a drag of this list's rows 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<ListView<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<ListView<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<ListView<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. -
setEmptyContent
Showscontentin the rows' viewport whenever the list has no rows (a list with no items), and takes it away when one arrives: the list 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 list's last child while it shows, so its own controls (a "Clear filter" button) follow the list in the Tab order and a reader finds them after its (no) rows; they belong to no selection. It is this list'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 list
- Throws:
IllegalArgumentException- whencontentis already in another container (another widget showing it included), or holds this list
-
setEmptyContent
Showscontentwhile there are no rows, assetEmptyContent(Widget)does, and describes the list bymessagewhile it shows, so a reader landing on it hears "list, 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 list
- Throws:
IllegalArgumentException- whencontentis already in another container (another widget showing it included), or holds this list
-
setEmptyMessage
Shows one muted, wrapped, centred line while there are no rows, which also describes the list to a reader: the empty content for words alone.- Parameters:
message- the words, ornullfor no empty content- Returns:
- this list
-
setEmptyMessage
Shows one fixed line while there are no rows; seesetEmptyMessage(I18nString).- Parameters:
message- the words, ornullfor no empty content- Returns:
- this list
-
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 list
-
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 list 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 list is published busy, and a reader is told both ends: "Loading Orders" at the start, with the name a reader hears the list 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 list 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 list
-
isLoading
public boolean isLoading()- Returns:
- whether a load is running, as
setLoading(boolean)last said
-
setLoadingContent
Replaces what shows while the list loads with no rows;nullbrings back the default, a moving bar over a muted "Loading…" in the list's language.- Parameters:
content- the content, ornullfor the default- Returns:
- this list
- Throws:
IllegalArgumentException- whencontentis already in another container (another widget showing it included), or holds this list
-
setLoadingContent
Replaces what shows while the list loads with no rows, and describes the list bymessagewhile it shows.- Parameters:
content- the content, ornullfor the defaultmessage- the label inside it that says what is loading, ornull- Returns:
- this list
- Throws:
IllegalArgumentException- whencontentis already in another container (another widget showing it included), or holds this list
-
isShowingLoadingContent
public boolean isShowingLoadingContent()- Returns:
- whether a loading content, the application's or the default, shows now, as the last layout placed it
-
onDetached
protected void onDetached()Leaving the window, the empty or loading content shown goes back to being nobody's child.- Overrides:
onDetachedin classWidget<ListView<T>>
-
setItems
ShowsnewItems, which the application keeps: the list reads it where it is and never copies or changes it. A new list is a new world: the selection and the cursor go (announced asSELECTION/ADJUSTMENTwhen there was one), the view returns to the top, andCHILDREN/CODEis announced. After changing the same list in place, callrefresh()instead. A row a popup hangs from (its context menu, a popover) whose item the new list holds is kept, asrefresh()keeps it, but not one the keyboard is in or returns to, such as a field whose own menu is open. The popup acts on the item its row was built for, as the cell factory was given it: when the new list holds new objects equal to the old, that is the old object and not the list's, so a menu that can outlive a push acts through the item's key, or finds the item initems(), rather than changing the object it captured. UI thread only.- Parameters:
newItems- the items, in the order shown- Returns:
- this list
-
items
- Returns:
- the items, as handed to
setItems(java.util.List<T>)
-
setItemName
What to call a row for an assistive technology, when its own cell says nothing about itself — the common case, because a cell that paints its own text usually declares nothing, and theLIST_ITEMrole the list writes onto it suppresses the warning that would have been the application's only notice. Asked for each realized row whose cell has no name, and for a selected row that is not realized, which is announced from the list's own node.Hand back a string the item holds. A name is carried over from the previous walk when it is the same object under the same locale and translation epoch, at no cost; one built inside this call is never the same object, so it is allocated and resolved again for every realized row on every damaged frame. A field of the item, or a constant, is what belongs here. UI thread only.
- Parameters:
name- the row's name for an item, ornullfor none- Returns:
- this list
-
setSelectionMode
Sets how many rows the user may select (defaultSelectionMode.SINGLE). Narrowing keeps what the new mode can hold:NONEdrops the selection andSINGLEkeeps the lead, each announced asSELECTION/ADJUSTMENT. UI thread only.- Parameters:
mode- the mode- Returns:
- this list
-
selectionMode
- Returns:
- how many rows the user may select
-
selectedIndex
public int selectedIndex()The lead of the selection: the row selected most recently that is still selected, or-1when nothing is. InSINGLEit is the one selected row and also the cursor. A list is one of the widgets in this set that genuinely has no-selection as a state:clearSelection()reaches it, and a fresh list is in it. -
selectedIndices
public int[] selectedIndices()- Returns:
- every selected row, ascending; empty when nothing is selected
-
isSelected
public boolean isSelected(int index) - Parameters:
index- a row- Returns:
- whether it is selected
-
selectedItem
- Returns:
- the item at
selectedIndex(), ornullwhen nothing is selected
-
selectedItems
- Returns:
- the selected items, in the order shown; empty when nothing is selected
-
cursorIndex
public int cursorIndex()The row the keyboard is on, or-1: what the arrows move, what Enter opens, and what wears the focus ring while the list holds the keyboard. InSINGLEit is the selection; inMULTIa row toggled off keeps it; inNONEit moves with nothing selected. -
rowCount
public int rowCount()Row count: the size of the items. -
firstVisibleIndex
public int firstVisibleIndex()- Returns:
- the first fully-or-partly visible row index (tests/inspection)
-
refresh
public void refresh()Re-reads the items and re-lays out (call after changing the list in place). AnnouncesCHILDREN/CODEafter the rows are unmounted. Rows past the new end leave the selection; inSINGLEa selection past the end moves to the last row (or goes, when the list is now empty), and either is announced asSELECTION/ADJUSTMENTfirst: a watcher showing the selected record hears the list move by itself, with an origin that says so, where a handler -- the user's response -- is not run. A row is its index: an item that moved in the list leaves its selection where it was.A row a popup hangs from (a context menu opened on it, a popover) is not let go of while its item is still in the list, found again by
equalsat the occurrence nearest where it stood: it stays as it is, at its item's index now, so the popup stays open through a refresh a timer or a server push makes, and it is realized afresh, from the item as it is then, at the first layout after the popup closes. The popup acts on the item the row was built for, as the cell factory was given it, which is the list's own object unless the list now holds another equal to it (seesetItems(java.util.List<T>)). When the item is gone, or the row would show another one, the row is let go of and the popup closes with it. So is a row the keyboard is in or returns to when its popup closes, such as a field whose own menu is open, as a refresh lets go of the row holding the keyboard, which falls back to the list: kept, the field would go on showing the item as it was, and stay bound to the object it was built for, which after asetItems(java.util.List<T>)of new objects is no longer the list's, so an edit made in it would go there. UI thread only. -
setSelectedIndex
Selects one row, alone, and scrolls it into view: a caller's write, so it announcesSELECTION/CODEand reaches no handler. The cursor goes there too. Selecting the row that is already the whole selection changes nothing, reveals nothing and announces nothing. InNONEit moves the cursor and selects nothing. UI thread only.- Parameters:
index- a row in[0, rowCount()).-1is not an argument even though it is whatselectedIndex()reports for an empty selection:clearSelection()is how that state is reached.- Returns:
- this list
- Throws:
IndexOutOfBoundsException- ifindexis outside that range, an empty list included, where every index is. An index that came from a search which found nothing, or from state saved against longer data, is a caller's bug here, exactly as it is forList.get.
-
clearSelection
Drops the selection:selectedIndex()becomes-1, announced asSELECTION/CODE. InSINGLEthe cursor goes with it; in the other modes it stays where the keyboard left it. No-op when nothing is selected. UI thread only.- Returns:
- this list
-
selectAll
Selects every row, inSelectionMode.MULTI; nothing in the other modes. Announced asSELECTION/CODEwhen anything changed. UI thread only.- Returns:
- this list
-
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. An application that wants its own open-the-row code run calls that code. Nothing without a cursor. UI thread only. -
scrollBy
public void scrollBy(float dy) Scrolls by a delta in logical points (positive = toward the end). UI thread only. -
revealRect
public void revealRect(float x, float y, float rectWidth, float rectHeight) Scrolls the minimum so the rect (in viewport coordinates) becomes visible.- Specified by:
revealRectin interfaceScrollable
-
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. -
onLayout
protected void onLayout()Description copied from class:WidgetContainers position children here (measure +Widget.layoutBox(float, float, float, float)per child). -
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<ListView<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<ListView<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. -
onMouseEvent
Description copied from class:WidgetMouse events (bubbling); callevent.consume()when handled.- Overrides:
onMouseEventin classWidget<ListView<T>>
-
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<ListView<T>>
-
onAccessibility
One list node over the rows it has realized, carrying where the viewport sits and which row the cursor is on.Three facts decide the shape. The selection is not required: this class documents no-selection as a genuine resting state — a fresh list is in it,
clearSelection()reaches it, andrefresh()returns to it on an emptied list — unlike the combo, which refuses an empty item list, and unlike the tabbed pane, which is always selected while it has tabs. The active descendant is not declared here and cannot be: it is resolved in the copy from the first node in this subtree publishedAccessible.State.ACTIVE, which is the selected row. And the scroll facet is published unconditionally, as the scroll pane's and the tab strip's are, so that resizing past the fitting point moves two booleans rather than making a facet appear and disappear — but with no half-point slop, unlike the tab strip's: this widget's wheel gate andscrollBy(float)'s clamp both use the bare subtraction and will move a list that overflows by a third of a point, so a slop-gated boolean would advertise a refusal the widget does not make.The verb is on this node and not on a row: the walk records a published node's owner as the widget it came from, and the scene dispatches strictly to that owner, so a verb written onto a row would be sent to the application's own cell widget and refused there. It is offered only while something is selected, because
activate()on an empty selection is a no-op and a verb that can only fail is worse than an absent one.Nothing is formatted here. The one string this hook can hand over is an
I18nStringthe item holds, compared by reference, so a frame that damaged the list and moved nothing costs no memory at all.- Overrides:
onAccessibilityin classWidget<ListView<T>>- Parameters:
a- the node being described
-
onAccessibilityChildIdentity
A mounted cell's identity is its data index, answered before the cell describes itself so that its name is carried over under that identity and whatever the cell holds inside it follows the row when the cell is recycled.- Overrides:
onAccessibilityChildIdentityin classWidget<ListView<T>>- Parameters:
child- the child about to be describeda- the builder, open only for the two identity calls
-
onAccessibilityChild
What only the list knows about a mounted row: which row it is, that it is one, where it sits in the data, and whether the cursor is on it.The identity key is the line the whole recycling story rests on, and it is answered by
onAccessibilityChildIdentity(limn.scene.Widget<?>, limn.accessibility.Accessibility)before this hook runs. A cell is pooled and rebound, so the widget object is the wrong key the moment it is reused: keyed by the data index, a cell that carried row three and is recycled onto row nine is minted row nine's identifier, and a cell coming back to row three gets row three's identifier back out of the intern table. A screen reader holding row three therefore still holds row three.The role and the name are conditional and the reason is the same for both: a cell is an application's widget and may already have said what it is. A
Row, aColumnor a padded box says nothing and takesLIST_ITEM, which is also what keeps it from being deleted as scaffolding; a cell that is a button or a scroll pane keeps what it declared, because eliding a scroll pane would take its scroll facet with it. The name is the widening this step made tosetItemName(java.util.function.Function<? super T, ? extends limn.i18n.I18nString>): rows commonly paint their own text and declare nothing, and the role written here suppresses the paints-and-says-nothing warning that would have been the application's only notice, so without asking for a name the ordinary case publishes thousands of nameless list items and nothing anywhere says so. An application's ownsetAccessibleNamestill wins, because it is applied after this.The position and the size of the set are the model's numbers and never the published tree's: a list that realizes twenty of five thousand rows still tells a reader which of five thousand it is on. And the selected row is marked active as well as selected, because here the selection is the cursor — there is no second highlight, unlike the combo's — and the keyboard stays on the list while it moves, which is exactly what an active descendant is for: without the bit a reader can enumerate the rows and never learn which one the user is on. Only while this list holds the keyboard: the cursor is resolved from the focused node down, so an unfocused list that marked its selected row active would hand the widget the user is actually in a cursor it does not have — a list nested inside another list's row cell would have handed the outer list its selected row as the outer list's own cursor, which is the cost the earlier text of this paragraph accepted and this gate removes.
The verbs are delegated rather than written: a verb written onto a row would be dispatched to the application's own cell widget, whose hook answers false, and the verb would never be performed. A delegated verb is published on the row and routed to
onAccessibilityChildAction(limn.scene.Widget<?>, long, limn.accessibility.Accessible.Action, limn.accessibility.Accessible.Argument), so a reader's "select this row" lands on the row it addressed and the list performs it. The row verb set, read against this widget:SELECTalways, because a list has no selection mode and is neverNONE;SCROLL_INTO_VIEWon a cell that cannot take the keyboard, because on one that can the walk already grants it free and a second performer is refused; neverADD_TO_SELECTIONorDESELECT, because this list selects one row and has no multi-select to add to; and neverFOCUS, because the selection is the cursor here and a focus that selected would beSELECTunder another name.PRESSstays on the list — seeonAccessibility(limn.accessibility.Accessibility).- Overrides:
onAccessibilityChildin classWidget<ListView<T>>- Parameters:
child- the child being described, which is the bar or one mounted cella- the child's node
-
onAccessibilityChildAction
protected boolean onAccessibilityChildAction(Widget<?> child, long key, Accessible.Action action, Accessible.Argument arg) A verb the list 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)) overListView<T>.limn.components.ListView.RowsHost:SELECTmakes that row the selection, as a click on it does, through the sameUSERseam and with the same reveal;SCROLL_INTO_VIEWreveals the row where it is, as the walk's free verb reveals a focusable one, and moves neither the selection nor the cursor.- Overrides:
onAccessibilityChildActionin classWidget<ListView<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
Opens the selected row, through the sameUSERseam Enter reaches -- and not through the publicactivate(), which is a caller's verb and reaches no handler.No enabled guard of its own: the node acted on here is this widget, so the scene's gate has already walked this list and every ancestor for
isEnabled(), checked that it is showing, that the window is not modal-blocked and that it is inside the layer that owns input. The seam re-checks the selection for itself, so the application is told exactly as Enter tells it and nothing here is a second entry point.- Overrides:
onAccessibilityActionin classWidget<ListView<T>>- Parameters:
action- what was askedarg- unused; the one verb offered here is parameterless- Returns:
- whether this list did it
-
onFocusGained
protected void onFocusGained()Description copied from class:WidgetCalled when this widget takes keyboard focus. Default: nothing.- Overrides:
onFocusGainedin classWidget<ListView<T>>
-
onFocusLost
protected void onFocusLost()Description copied from class:WidgetCalled when this widget loses keyboard focus. Default: nothing.- Overrides:
onFocusLostin classWidget<ListView<T>>
-