Class VFXGridState<T, C extends VFXCell<T>>

java.lang.Object
io.github.palexdev.virtualizedfx.grid.VFXGridState<T,C>

public class VFXGridState<T, C extends VFXCell<T>> extends Object
Immutable object to represent the state of a VFXGrid in a specific moment in time. In other words, each and every state is given by a unique combination of the grid's properties (in terms of values).

The state carries four important pieces of information:

1) The range of rows to display

2) The range of columns to display

3) The cells that are currently in the viewport

4) A flag that indicates whether cells have changed since the last state

Note: the combination of 1 and 2 determines the point 3, so the items to display from VFXGrid.itemsProperty()

Indexes and loops in a 2D structure When implementing the VFXGrid's "infrastructure", the state was one of the first classes to be written since it is crucial to display the needed items. When you already have a good and stable example to look at (in my case VFXList) it's only natural to try and follow it as much as possible. Other than adding another IntegerRange variable (rows and columns), there's nothing new here compared to VFXListState. However, because we have two ranges, an issue arises on how to store the cells; we can't map indexes to cells right, we would have to store them by [r, c] coordinates right?

Actually, we can, and the concept I'm going to explain here is crucial and used pretty much everywhere in the grid's "infrastructure". To be clear, yes, we could create a wrapper class to represent a coordinate, but that would mean that the specialized data structure IndexBiMap.StateMap could not be used. We would need to adapt it to use our Coordinate class, which inevitably would lead to code duplication.

To begin with, the grid's underlying data structure is one dimensional, a plain and simple ObservableList. This means that item 0 is in the cell at coordinates [0, 0], item 1 would be at coordinates [0, 1] and so on. Of course, it also depends on the number of columns and rows.

The important thing to notice here is that essentially what the grid implicitly does is converting a position from the list to a pair of coordinates. Well, we can also do the opposite, starting from the two ranges, we can get the corresponding position in the list for each pair of coordinates.

Linear Index: A linear index refers to a single integer value used to access an element in a one-dimensional array or a multidimensional array that has been flattened into a one-dimensional sequence. In other words, it's a way to map the element's position in the array to a single integer. For example, in a 1D array, linear indexing is straightforward: each element has a unique linear index corresponding to its position in the array. However, in a multidimensional array, elements can be indexed using linear indexing by treating the array as if it were one long sequence of elements, often following a row-major or column-major order.

Subscripts: Subscripts are used to refer to the indices of individual dimensions in a multidimensional array. Each dimension of the array has its own subscript. For example, in a 2D array, you might have subscripts (i, j), where 'i' refers to the row index and 'j' refers to the column index.

The conversion from a pair of coordinates to a linear index is done by the utility GridUtils.subToInd(int, int, int).

This strategy brings two main consequences:

1) the cells are still stored in a IndexBiMap.StateMap, but the index used in the mapping is the linear index. Which means that if you want to get the coordinates, you'll have to convert them back using GridUtils.indToSub(int, int).

2) this is the first time in my Java developer career I use labels to break out of loops. You see when you use two nested for loops to iterate over the rows and columns ranges, it may happen that the resulting linear index is non-existing in the list. In other words, the linear index is greater than itemsNum - 1. In such cases, it's useless to continue the iterations. By placing the label above the external loop, we are able to immediately break out of the nested loop, and as you may guess, this is indeed a nice performance optimization. An example of when this may happen is in case of incomplete rows/columns.

 
 outer_loop:
 for (rIdx : rowsRange) {
     for (cIdx : columnsRange) {
         ...
         ...
         if (...) break outer_loop;
     }
 }
 
 
See Also:
  • Field Details

    • INVALID

      public static final VFXGridState INVALID
      Special instance of VFXGridState used to indicate that no cells can be present in the viewport at a certain time. The reasons can be many, for example, no cell factory, invalid range, width/height <= 0, etc...

      This and isEmpty() are two total different things!!

  • Constructor Details

    • VFXGridState

      public VFXGridState(VFXGrid<T,C> grid, io.github.palexdev.mfxcore.base.beans.range.IntegerRange rowsRange, io.github.palexdev.mfxcore.base.beans.range.IntegerRange columnsRange)
  • Method Details

    • addCell

      protected void addCell(int rIndex, int cIndex, C cell)
      Converts the given row and column indexes to a linear index and delegates to addCell(int, Object, VFXCell).
    • addCell

      protected void addCell(int index, C cell)
    • addCell

      protected void addCell(int index, T item, C cell)
      Adds the given cell to the IndexBiMap.StateMap of this state object.
    • removeCell

      protected C removeCell(int rIndex, int cIndex)
      Delegates to removeCell(int) by first converting the given row and column indexes to a linear index.
    • removeCell

      protected C removeCell(int index)
      Removes a cell from the IndexBiMap.StateMap for the given linear index. If the cell is not found, the next attempt is to remove it by the item at the given linear index in the VFXGrid.itemsProperty().
    • removeCell

      protected C removeCell(T item)
      Removes a cell from the IndexBiMap.StateMap for the given item.
    • dispose

      protected void dispose()
      Disposes this state object by: caching all the cells (VFXCellsCache.cache(Collection)), and then clearing the IndexBiMap.StateMap by calling IndexBiMap.clear().
      See Also:
    • getGrid

      public VFXGrid<T,C> getGrid()
      Returns:
      the VFXGrid instance this state is associated to
    • getRowsRange

      public io.github.palexdev.mfxcore.base.beans.range.IntegerRange getRowsRange()
      Returns:
      the range of rows to display
    • getColumnsRange

      public io.github.palexdev.mfxcore.base.beans.range.IntegerRange getColumnsRange()
      Returns:
      the range of columns to display
    • getCells

      protected IndexBiMap.StateMap<T,C> getCells()
      Returns:
      the map containing the cells
      See Also:
    • getCellsByIndex

      protected SequencedMap<Integer,C> getCellsByIndex()
      Returns:
      the map containing the cells by their index
    • getCellsByItem

      protected List<Map.Entry<T,C>> getCellsByItem()
      Returns:
      the list containing the cells by their item, as entries because of possible duplicates
      See Also:
    • getCellsByIndexUnmodifiable

      public SequencedMap<Integer,C> getCellsByIndexUnmodifiable()
      Returns:
      the map containing the cells by their index, unmodifiable
    • getCellsByItemUnmodifiable

      public List<Map.Entry<T,C>> getCellsByItemUnmodifiable()
      Returns:
      the list containing the cells by their item, as entries because of possible duplicates, unmodifiable
      See Also:
    • getNodes

      public List<Node> getNodes()
      Returns:
      converts the cells' map to a list of nodes by calling VFXCell.toNode() on each cell
    • size

      public int size()
      Returns:
      the number of cells in the IndexBiMap.StateMap
    • isEmpty

      public boolean isEmpty()
      Returns:
      whether the IndexBiMap.StateMap is empty
      See Also:
    • haveCellsChanged

      public boolean haveCellsChanged()
      Returns:
      whether the cells have changed since the last state. This is used to indicate if more or less cells are present in this state compared to the old one. Used by the default skin to check whether the viewport has to update its children or not.
      See Also:
    • setCellsChanged

      protected void setCellsChanged(boolean cellsChanged)
      See Also: