TableState
A hoistable state holder for what a Table has selected, the order it sorts its rows in and the layout of its columns, carrying the gesture that brings one of its rows into view.
Each row is named by its index into the table's rows - the model's own row space, never the position the row is drawn at - so a sort order and a row filter move where a row is shown and leave the index that names it alone.
selectedRowIndices, sortKeys and columnLayout are two-way: assigning one applies it to the table, and the user's own change - selecting rows by click, drag or keyboard, sorting by a header click, dragging a column header or divider - is written back here. All three are snapshot-observable, so reading one inside a composable (or a snapshotFlow collector) subscribes to the user's later changes as well.
The rows this state names are the composition's own and are re-applied on every pass, so a table driven by a state never stands on a selection the state does not hold, and a row the table's rows do not reach is left out of the selection while it goes on being named here - rows that reach it again show it selected.
The sort order and the column layout are applied when they are assigned, and this state is the only place they are held. A table built again over this state starts on them, and so does a table that turns sorting on. The rows and columns a table is declared with, a new model and a new column model keep them. A column the table no longer has goes on being named here, so columns that bring it back put it in its place again. A structure change the caller makes to their own model in place starts the order and the columns over, as it does on a JTable, and this state is written back with the result.
revealRow brings one row into view when the application decides to - a row just added, a search hit:
val state = rememberTableState()
Button("Add", onClick = { people = people + Person() })
LaunchedEffect(people) { state.revealRow(people.lastIndex) }
ScrollPane {
Viewport {
Table(rows = people, columns = { column("Name") { it.name } }, state = state)
}
}rowCount and shownSelectedRowIndices answer for the table instead of for what this state holds. Each reads the bound table where it is called, so what it reports is what the table stands on - which is not always what was declared, since a selection mode narrower than the declaration keeps only part of it and a row filter takes the rows it hides out of the selection. They are not snapshot state, so reading one subscribes to nothing; a composable that has to follow the user reads selectedRowIndices. An unbound state has no table to answer for and reports no rows and nothing selected.
A state drives at most one table: passing it to a second one moves it there and leaves the first unbound.
Parameters
the rows selected until the caller or the user moves the selection.
the order the rows are sorted in until the caller or the user sorts them again.
the layout the columns start in; null holds none, leaving them in their declared layout.
See also
Constructors
Properties
The order and the widths of the columns; null holds no layout, leaving the columns in the layout they are already in, which starts as their declared layout.
The selected rows as indices into the table's rows, expressed as the general multi-select shape so one state covers every one of org.jetbrains.compose.swing.annotations.SelectionMode's modes.
The rows the table has selected, as indices into its rows. Empty while no table is bound.
The order the rows are sorted in while the table is sortable; an empty list leaves them unsorted, in the order the table's rows hold them.