io.github.palexdev.virtualizedfx.grid.VFXGrid Maven / Gradle / Ivy
Show all versions of virtualizedfx Show documentation
/*
* Copyright (C) 2024 Parisi Alessandro - [email protected]
* This file is part of VirtualizedFX (https://github.com/palexdev/VirtualizedFX)
*
* VirtualizedFX is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of the License,
* or (at your option) any later version.
*
* VirtualizedFX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with VirtualizedFX. If not, see .
*/
package io.github.palexdev.virtualizedfx.grid;
import io.github.palexdev.mfxcore.base.beans.Size;
import io.github.palexdev.mfxcore.base.beans.range.IntegerRange;
import io.github.palexdev.mfxcore.base.properties.functional.FunctionProperty;
import io.github.palexdev.mfxcore.base.properties.functional.SupplierProperty;
import io.github.palexdev.mfxcore.base.properties.styleable.StyleableDoubleProperty;
import io.github.palexdev.mfxcore.base.properties.styleable.StyleableIntegerProperty;
import io.github.palexdev.mfxcore.base.properties.styleable.StyleableObjectProperty;
import io.github.palexdev.mfxcore.base.properties.styleable.StyleableSizeProperty;
import io.github.palexdev.mfxcore.base.properties.styleable.StyleableSizeProperty.SizeConverter;
import io.github.palexdev.mfxcore.controls.Control;
import io.github.palexdev.mfxcore.controls.SkinBase;
import io.github.palexdev.mfxcore.utils.PositionUtils;
import io.github.palexdev.mfxcore.utils.fx.PropUtils;
import io.github.palexdev.mfxcore.utils.fx.StyleUtils;
import io.github.palexdev.virtualizedfx.base.VFXContainer;
import io.github.palexdev.virtualizedfx.base.VFXScrollable;
import io.github.palexdev.virtualizedfx.base.VFXStyleable;
import io.github.palexdev.virtualizedfx.cells.base.VFXCell;
import io.github.palexdev.virtualizedfx.controls.VFXScrollPane;
import io.github.palexdev.virtualizedfx.enums.BufferSize;
import io.github.palexdev.virtualizedfx.events.VFXContainerEvent;
import io.github.palexdev.virtualizedfx.properties.VFXGridStateProperty;
import io.github.palexdev.virtualizedfx.utils.VFXCellsCache;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.css.CssMetaData;
import javafx.css.Styleable;
import javafx.css.StyleableProperty;
import javafx.css.StyleablePropertyFactory;
import javafx.geometry.Pos;
import javafx.scene.shape.Rectangle;
import java.util.List;
import java.util.Map;
import java.util.SequencedMap;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* Implementation of a virtualized container to show a list of items in a "2D" perspective.
* The default style class is: '.vfx-grid'.
*
* Extends {@link Control}, implements {@link VFXContainer}, has its own skin implementation {@link VFXGridSkin}
* and behavior {@link VFXGridManager}. Uses cells of type {@link VFXCell}.
*
* This is a stateful component, meaning that every meaningful variable (position, size, cell size, etc.) will produce a new
* {@link VFXGridState} when changing. The state determines how and which items are displayed in the container.
*
* Features {@literal &} Implementation Details
* - First and foremost, it's important to describe how the grid works and why it's made as it is. The grid arranges
* the contents of a simple 1D data structure (a list) in a 2D way. (History time) The previous implementation used a 2D data structure
* instead which indeed made some algorithms easier to implement, but made its usage very inconvenient for one simple reason:
* the data structure was not flexible enough. To add a row/column, they needed to have the same size of the data structure,
* so in practice, if you had, for example, a half-full row/column to add, you had to fill it with {@code null} elements.
* The same issue occurred for the data structure creation, the source list/array had to be exactly the size given by
* 'nRows * nColumns'. (End of history time) So the question is, if we now use a simple 1D structure now
* (which is more flexible and easier to use for the end-user), how can the grid arrange the contents in a 2D way? Well,
* the answer is pretty straightforward; we need a value that the big dumb-dumb me of the past didn't think about:
* the {@link #columnsNumProperty()}. Given the desired number of columns, we can easily get the number of rows as follows:
* {@code Math.ceil(nItems / nColumns)}. However, note that for performance reason, the property acts as a 'maximum number of columns',
* which means that the actual number of columns in the viewport depends on these other factors: the container width,
* the cell size, the horizontal spacing and the buffer size.
*
- The default behavior implementation, {@link VFXGridManager}, can be considered as the name suggests more like
* a 'manager' than an actual behavior. It is responsible for reacting to core changes in the functionalities defined here
* to produce a new state.
* The state can be considered like a 'picture' of the container at a certain time. Each combination of the variables
* that influence the way items are shown (how many, start, end, changes in the list, etc.) will produce a specific state.
* This is an important concept as some of the features I'm going to mention below are due to the combination of default
* skin + default behavior. You are allowed to change/customize the skin and behavior as you please. BUT, beware, VFX
* components are no joke, they are complex, make sure to read the documentation before!
*
- The {@link #alignmentProperty()} is a unique feature of the grid that allows to set the position of the viewport,
* more information can be found in the skin, {@link VFXGridSkin}.
*
- The items list is managed automatically (permutations, insertions, removals, updates). Compared to previous
* algorithms, the {@link VFXGridManager} adopts a much simpler strategy while still trying to keep the cell updates count
* as low as possible to improve performance. See {@link VFXGridManager#onItemsChanged()}.
*
- The function used to generate the cells, called "cellFactory", can be changed anytime, even at runtime, see
* {@link VFXGridManager#onCellFactoryChanged()}.
*
- The core aspect for virtualization is to have a fixed cell size for all cells, this parameter can be controlled through
* the {@link #cellSizeProperty()}, and can also be changed anytime, see {@link VFXGridManager#onCellSizeChanged()}.
*
- Similar to the JavaFX's {@code GridPane}, this container allows you to evenly space the cells in the viewport by
* setting the properties {@link #hSpacingProperty()} and {@link #vSpacingProperty()}. See {@link VFXGridManager#onSpacingChanged()}.
*
- Even though the grid doesn't have the orientation property (compared to the VFXList), core computations such as
* the range of rows, the range of columns, the estimated size, the layout of nodes etc., are delegated to separate 'helper'
* class which is the {@link VFXGridHelper}. You are allowed to change the helper through the {@link #helperFactoryProperty()}.
*
- The vertical and horizontal positions are available through the properties {@link #hPosProperty()} and {@link #vPosProperty()}.
* It could indeed be possible to use a single property for the position, but they are split for performance reasons.
*
- The virtual bounds of the container are given by two properties:
*
a) the {@link #virtualMaxXProperty()} which specifies the total number of pixels on the x-axis
*
b) the {@link #virtualMaxYProperty()} which specifies the total number of pixels on the y-axis
*
- You can access the current state through the {@link #stateProperty()}. The state gives crucial information about
* the container such as the rows range, the columns range and the visible cells (by index and by item). If you'd like to observe
* for changes in the displayed items, then you want to add a listener on this property. Make sure to also read the
* {@link VFXGridState} documentation, as it also contains important information on the grid's mechanics.
*
- It is possible to force the viewport to update the layout by invoking {@link #requestViewportLayout()},
* although this should never be necessary as it is automatically handled by "system".
*
- Additionally, this container makes use of a simple cache implementation, {@link VFXCellsCache}, which
* avoids creating new cells when needed if some are already present in it. The most crucial aspect for this kind of
* virtualization is to avoid creating nodes, as this is the most expensive operation. Not only nodes need
* to be created but also added to the container and then laid out.
* Instead, it's much more likely that the {@link VFXCell#updateItem(Object)} will be simple and thus faster.
* Note 1: to make the cache more generic, thus allowing its usage in more cases, a recent refactor,
* removed the dependency on the container itself and replaced it with the cell factory. Since the cache can also populate
* itself with "empty" cells, it must know how to create them. The cache's cell factory is automatically synchronized with
* the container's one.
* Note 2: by default, the capacity is set to 10 cells. However, for the grid's nature, such number is likely to be
* too small, but it also depends from case to case. You can play around with the values and see if there's any benefit to performance.
*
*
* @param the type of items in the grid
* @param the type of cells used by the container to visualize the items
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public class VFXGrid> extends Control> implements VFXContainer, VFXStyleable, VFXScrollable {
//================================================================================
// Properties
//================================================================================
private final VFXCellsCache cache;
private final ListProperty items = new SimpleListProperty<>(FXCollections.observableArrayList()) {
@Override
public void set(ObservableList newValue) {
if (newValue == null) newValue = FXCollections.observableArrayList();
super.set(newValue);
}
};
private final FunctionProperty cellFactory = new FunctionProperty<>() {
@Override
protected void invalidated() {
cache.setCellFactory(get());
}
};
private final ReadOnlyObjectWrapper> helper = new ReadOnlyObjectWrapper<>() {
@Override
public void set(VFXGridHelper newValue) {
if (newValue == null)
throw new NullPointerException("Grid helper cannot be null!");
VFXGridHelper oldValue = get();
if (oldValue != null) oldValue.dispose();
super.set(newValue);
}
};
private final SupplierProperty> helperFactory = new SupplierProperty<>(defaultHelperFactory()) {
@Override
public void set(Supplier> newValue) {
if (newValue == null)
throw new NullPointerException("Grid helper factory cannot be null!");
super.set(newValue);
}
@Override
protected void invalidated() {
VFXGridHelper helper = get().get();
setHelper(helper);
}
};
private final DoubleProperty vPos = PropUtils.clampedDoubleProperty(
() -> 0.0,
this::getMaxVScroll
);
private final DoubleProperty hPos = PropUtils.clampedDoubleProperty(
() -> 0.0,
this::getMaxHScroll
);
private final VFXGridStateProperty state = new VFXGridStateProperty<>(VFXGridState.INVALID);
private final ReadOnlyBooleanWrapper needsViewportLayout = new ReadOnlyBooleanWrapper(false);
//================================================================================
// Constructors
//================================================================================
public VFXGrid() {
cache = createCache();
initialize();
}
public VFXGrid(ObservableList items, Function cellFactory) {
this();
setItems(items);
setCellFactory(cellFactory);
}
//================================================================================
// Methods
//================================================================================
private void initialize() {
getStyleClass().addAll(defaultStyleClasses());
setDefaultBehaviorProvider();
setHelper(getHelperFactory().get());
}
/**
* Calls {@link #autoArrange(int)} with 0 as parameter.
*/
public void autoArrange() {
autoArrange(0);
}
/**
* This method will compute the maximum number of columns that can fit in the grid. The computation depends on the
* following values: the container's width, the cell width, and the horizontal spacing. The expression is the following:
* {@code Math.max(Math.max(0, min), Math.floor(getWidth() / (cellWidth + hSpacing)))}.
*
* One good example of this would be a grid that automatically adapts to the size of its parent or window.
* In combination with the {@link #alignmentProperty()} this can reveal to be very powerful.
*
* Needless to say, the computed number is automatically set as the {@link #columnsNumProperty()}.
*
* @param min the minimum number of columns
*/
public void autoArrange(int min) {
double cellWidth = getCellSize().getWidth();
double hSpacing = getHSpacing();
int nColumns = (int) Math.max(Math.max(0, min), Math.floor(getWidth() / (cellWidth + hSpacing)));
setColumnsNum(nColumns);
}
/**
* Responsible for creating the cache instance used by this container.
*
* @see VFXCellsCache
* @see #cacheCapacityProperty()
*/
protected VFXCellsCache createCache() {
return new VFXCellsCache<>(null, getCacheCapacity());
}
/**
* Setter for the {@link #stateProperty()}.
*/
protected void update(VFXGridState state) {
setState(state);
}
/**
* @return the default function used to build a {@link VFXGridHelper}.
*/
protected Supplier> defaultHelperFactory() {
return () -> new VFXGridHelper.DefaultHelper<>(this);
}
/**
* Setter for the {@link #needsViewportLayoutProperty()}.
* This sets the property to true, causing the default skin to recompute the cells' layout.
*/
public void requestViewportLayout() {
setNeedsViewportLayout(true);
}
//================================================================================
// Overridden Methods
//================================================================================
@Override
public void update(int... indexes) {
VFXGridState state = getState();
if (state.isEmpty()) return;
if (indexes.length == 0) {
state.getCellsByIndex().values().forEach(VFXContainerEvent::update);
return;
}
for (int index : indexes) {
C c = state.getCellsByIndex().get(index);
if (c == null) continue;
VFXContainerEvent.update(c);
}
}
@Override
public List defaultStyleClasses() {
return List.of("vfx-grid");
}
@Override
protected SkinBase, ?> buildSkin() {
return new VFXGridSkin<>(this);
}
@Override
public Supplier> defaultBehaviorProvider() {
return () -> new VFXGridManager<>(this);
}
@Override
public VFXScrollPane makeScrollable() {
return new VFXScrollPane(this).bindTo(this);
}
//================================================================================
// Delegate Methods
//================================================================================
/**
* Delegate for {@link VFXCellsCache#populate()}.
*/
public VFXGrid populateCache() {
cache.populate();
return this;
}
/**
* Delegate for {@link VFXGridState#getRowsRange()}
*/
public IntegerRange getRowsRange() {return getState().getRowsRange();}
/**
* Delegate for {@link VFXGridState#getColumnsRange()}
*/
public IntegerRange getColumnsRange() {return getState().getColumnsRange();}
/**
* Delegate for {@link VFXGridState#getCellsByIndexUnmodifiable()}
*/
public SequencedMap getCellsByIndexUnmodifiable() {return getState().getCellsByIndexUnmodifiable();}
/**
* Delegate for {@link VFXGridState#getCellsByItemUnmodifiable()}
*/
public List> getCellsByItemUnmodifiable() {
return getState().getCellsByItemUnmodifiable();
}
/**
* Delegate for {@link VFXGridHelper#virtualMaxXProperty()}.
*/
@Override
public ReadOnlyDoubleProperty virtualMaxXProperty() {
return getHelper().virtualMaxXProperty();
}
/**
* Delegate for {@link VFXGridHelper#virtualMaxYProperty()}.
*/
@Override
public ReadOnlyDoubleProperty virtualMaxYProperty() {
return getHelper().virtualMaxYProperty();
}
/**
* Delegate for {@link VFXGridHelper#maxVScrollProperty()}.
*/
@Override
public ReadOnlyDoubleProperty maxVScrollProperty() {
return getHelper().maxVScrollProperty();
}
/**
* Delegate for {@link VFXGridHelper#maxHScrollProperty()}.
*/
@Override
public ReadOnlyDoubleProperty maxHScrollProperty() {
return getHelper().maxHScrollProperty();
}
/**
* Delegate for {@link VFXGridHelper#scrollToRow(int)}.
*/
public void scrollToRow(int row) {
getHelper().scrollToRow(row);
}
/**
* Delegate for {@link VFXGridHelper#scrollToColumn(int)}.
*/
public void scrollToColumn(int column) {
getHelper().scrollToColumn(column);
}
/**
* Delegate for {@link VFXGridHelper#scrollToRow(int)}, with parameter 0.
*/
public void scrollToFirstRow() {
scrollToRow(0);
}
/**
* Delegate for {@link VFXGridHelper#scrollToRow(int)}, with parameter {@link Integer#MAX_VALUE}.
*/
public void scrollToLastRow() {
scrollToRow(Integer.MAX_VALUE);
}
/**
* Delegate for {@link VFXGridHelper#scrollToColumn(int)}, with parameter 0.
*/
public void scrollToFirstColumn() {
scrollToColumn(0);
}
/**
* Delegate for {@link VFXGridHelper#scrollToColumn(int)}, with parameter {@link Integer#MAX_VALUE}.
*/
public void scrollToLastColumn() {
scrollToColumn(Integer.MAX_VALUE);
}
//================================================================================
// Styleable Properties
//================================================================================
private final StyleableSizeProperty cellSize = new StyleableSizeProperty(
StyleableProperties.CELL_SIZE,
this,
"cellSize",
Size.of(100, 100)
);
private final StyleableIntegerProperty columnsNum = new StyleableIntegerProperty(
StyleableProperties.COLUMNS_NUM,
this,
"columnsNum",
5
);
private final StyleableObjectProperty alignment = new StyleableObjectProperty<>(
StyleableProperties.ALIGNMENT,
this,
"alignment",
Pos.TOP_LEFT
) {
@Override
public void set(Pos v) {
if (PositionUtils.isBaseline(v)) v = Pos.TOP_LEFT;
super.set(v);
}
};
private final StyleableDoubleProperty hSpacing = new StyleableDoubleProperty(
StyleableProperties.H_SPACING,
this,
"hSpacing",
0.0
);
private final StyleableDoubleProperty vSpacing = new StyleableDoubleProperty(
StyleableProperties.V_SPACING,
this,
"vSpacing",
0.0
);
private final StyleableObjectProperty bufferSize = new StyleableObjectProperty<>(
StyleableProperties.BUFFER_SIZE,
this,
"bufferSize",
BufferSize.standard()
);
private final StyleableDoubleProperty clipBorderRadius = new StyleableDoubleProperty(
StyleableProperties.CLIP_BORDER_RADIUS,
this,
"clipBorderRadius",
0.0
);
private final StyleableIntegerProperty cacheCapacity = new StyleableIntegerProperty(
StyleableProperties.CACHE_CAPACITY,
this,
"cacheCapacity",
10
) {
@Override
protected void invalidated() {
cache.setCapacity(get());
}
};
public Size getCellSize() {
return cellSize.get();
}
/**
* Specifies the cells' width and height as a {@link Size} object.
*
* Can be set in CSS via the property: '-vfx-cell-size'.
*
* Note that this is a special styleable property, in order to set it in CSS see the docs here
* {@link SizeConverter}.
*/
public StyleableSizeProperty cellSizeProperty() {
return cellSize;
}
public void setCellSize(Size cellSize) {
this.cellSize.set(cellSize);
}
public void setCellSize(double w, double h) {
setCellSize(Size.of(w, h));
}
public void setCellSize(double size) {
setCellSize(Size.of(size, size));
}
public int getColumnsNum() {
return columnsNum.get();
}
/**
* Specifies the maximum number of columns the grid can have. This number is crucial to also compute the nuber of rows.
* By default, the latter is computed as follows: {@code Math.ceil(nItems / nColumns)}.
*
* Can be set in CSS via the property: '-vfx-columns-num'.
*/
public StyleableIntegerProperty columnsNumProperty() {
return columnsNum;
}
public void setColumnsNum(int columnsNum) {
this.columnsNum.set(columnsNum);
}
public Pos getAlignment() {
return alignment.get();
}
/**
* Specifies the position of the viewport node inside the grid as a {@link Pos} object.
* Note that 'baseline' positions are not supported and will default to {@link Pos#TOP_LEFT} instead.
*
* Can be set in CSS via the property: '-vfx-alignment'.
*/
public StyleableObjectProperty alignmentProperty() {
return alignment;
}
public void setAlignment(Pos alignment) {
this.alignment.set(alignment);
}
public double getHSpacing() {
return hSpacing.get();
}
/**
* Specifies the horizontal number of pixels between each cell.
*
* Can be set in CSS via the property: '-vfx-h-spacing'.
*/
public StyleableDoubleProperty hSpacingProperty() {
return hSpacing;
}
public void setHSpacing(double spacing) {
this.hSpacing.set(spacing);
}
public double getVSpacing() {
return vSpacing.get();
}
/**
* Specifies the vertical number of pixels between each cell.
*
* Can be set in CSS via the property: '-vfx-v-spacing'.
*/
public StyleableDoubleProperty vSpacingProperty() {
return vSpacing;
}
public void setVSpacing(double spacing) {
this.vSpacing.set(spacing);
}
/**
* Convenience method to set both vertical and horizontal spacing to the given value.
*
* @see #hSpacingProperty()
* @see #vSpacingProperty()
*/
public void setSpacing(double spacing) {
setHSpacing(spacing);
setVSpacing(spacing);
}
/**
* Convenience method to set both vertical and horizontal spacing to the given values.
*
* @see #hSpacingProperty()
* @see #vSpacingProperty()
*/
public void setSpacing(double hSpacing, double vSpacing) {
setHSpacing(hSpacing);
setVSpacing(vSpacing);
}
/**
* {@inheritDoc}
*
* To be more precise, for the grid this determines the number of extra rows and columns to add in the viewport.
* Since this is a "2D" container, by its nature, a considerable amount of cells is displayed (but it also depends on
* other factors such as the container's size, the cells size, etc.). The buffer increases such number, so if you have
* performance issues you may try to lower the buffer value, although I would consider it as a last resort.
*
* Can be set in CSS via the property: '-vfx-buffer-size'.
*/
public StyleableObjectProperty bufferSizeProperty() {
return bufferSize;
}
public double getClipBorderRadius() {
return clipBorderRadius.get();
}
/**
* Used by the viewport's clip to set its border radius.
* This is useful when you want to make a rounded container, this prevents the content from going outside the view.
*
* Side note: the clip is a {@link Rectangle}, now for some fucking reason, the rectangle's arcWidth and arcHeight
* values used to make it round do not act like the border-radius or background-radius properties,
* instead their value is usually 2 / 2.5 times the latter.
* So, for a border radius of 5, you want this value to be at least 10/13.
*
* Can be set in CSS via the property: '-vfx-clip-border-radius'.
*/
public StyleableDoubleProperty clipBorderRadiusProperty() {
return clipBorderRadius;
}
public void setClipBorderRadius(double clipBorderRadius) {
this.clipBorderRadius.set(clipBorderRadius);
}
public int getCacheCapacity() {
return cacheCapacity.get();
}
/**
* Specifies the maximum number of cells the cache can contain at any time. Excess will not be added to the queue and
* disposed immediately.
*
* Can be set in CSS via the property: '-vfx-cache-capacity'.
*/
public StyleableIntegerProperty cacheCapacityProperty() {
return cacheCapacity;
}
public void setCacheCapacity(int cacheCapacity) {
this.cacheCapacity.set(cacheCapacity);
}
//================================================================================
// CssMetaData
//================================================================================
private static class StyleableProperties {
private static final StyleablePropertyFactory> FACTORY = new StyleablePropertyFactory<>(Control.getClassCssMetaData());
private static final List> cssMetaDataList;
private static final CssMetaData, Size> CELL_SIZE =
new CssMetaData<>("-vfx-cell-size", SizeConverter.getInstance(), Size.of(100, 100)) {
@Override
public boolean isSettable(VFXGrid, ?> styleable) {
return !styleable.cellSizeProperty().isBound();
}
@Override
public StyleableProperty getStyleableProperty(VFXGrid, ?> styleable) {
return styleable.cellSizeProperty();
}
};
private static final CssMetaData, Number> COLUMNS_NUM =
FACTORY.createSizeCssMetaData(
"-vfx-columns-num",
VFXGrid::columnsNumProperty,
5
);
private static final CssMetaData, Pos> ALIGNMENT =
FACTORY.createEnumCssMetaData(
Pos.class,
"-vfx-alignment",
VFXGrid::alignmentProperty,
Pos.TOP_LEFT
);
private static final CssMetaData, Number> H_SPACING =
FACTORY.createSizeCssMetaData(
"-vfx-h-spacing",
VFXGrid::hSpacingProperty,
0.0
);
private static final CssMetaData, Number> V_SPACING =
FACTORY.createSizeCssMetaData(
"-vfx-v-spacing",
VFXGrid::vSpacingProperty,
0.0
);
private static final CssMetaData, Number> CLIP_BORDER_RADIUS =
FACTORY.createSizeCssMetaData(
"-vfx-clip-border-radius",
VFXGrid::clipBorderRadiusProperty,
0.0
);
private static final CssMetaData, Number> CACHE_CAPACITY =
FACTORY.createSizeCssMetaData(
"-vfx-cache-capacity",
VFXGrid::cacheCapacityProperty,
10
);
private static final CssMetaData, BufferSize> BUFFER_SIZE =
FACTORY.createEnumCssMetaData(
BufferSize.class,
"-vfx-buffer-size",
VFXGrid::bufferSizeProperty,
BufferSize.standard()
);
static {
cssMetaDataList = StyleUtils.cssMetaDataList(
Control.getClassCssMetaData(),
CELL_SIZE, COLUMNS_NUM, ALIGNMENT, H_SPACING, V_SPACING,
BUFFER_SIZE, CACHE_CAPACITY, CLIP_BORDER_RADIUS
);
}
}
public static List> getClassCssMetaData() {
return StyleableProperties.cssMetaDataList;
}
@Override
protected List> getControlCssMetaData() {
return getClassCssMetaData();
}
//================================================================================
// Getters/Setters
//================================================================================
/**
* @return the cache instance used by this container
*/
protected VFXCellsCache getCache() {
return cache;
}
/**
* Delegate for {@link VFXCellsCache#size()}.
*/
public int cacheSize() {
return cache.size();
}
/**
* {@inheritDoc}
*
* Also, despite the grid being a 2D container, we still use a 1D collection because it's much more easy and convenient
* to use. Knowing the number of columns we want to divide the items by, it's enough to make the list act as a 2D collection.
*/
public ListProperty itemsProperty() {
return items;
}
public Function getCellFactory() {
return cellFactory.get();
}
/**
* Specifies the function used to build the cells.
*/
public FunctionProperty cellFactoryProperty() {
return cellFactory;
}
public void setCellFactory(Function cellFactory) {
this.cellFactory.set(cellFactory);
}
public VFXGridHelper getHelper() {
return helper.get();
}
/**
* Specifies the instance of the {@link VFXGridHelper} built by the {@link #helperFactoryProperty()}.
*/
public ReadOnlyObjectProperty> helperProperty() {
return helper.getReadOnlyProperty();
}
protected void setHelper(VFXGridHelper helper) {
this.helper.set(helper);
}
public Supplier> getHelperFactory() {
return helperFactory.get();
}
/**
* Specifies the function used to build a {@link VFXGridHelper} instance.
*/
public SupplierProperty> helperFactoryProperty() {
return helperFactory;
}
public void setHelperFactory(Supplier> helperFactory) {
this.helperFactory.set(helperFactory);
}
public DoubleProperty vPosProperty() {
return vPos;
}
public DoubleProperty hPosProperty() {
return hPos;
}
public VFXGridState getState() {
return state.get();
}
/**
* Specifies the container's current state. The state carries useful information such as the range of rows and columns
* and the cells ordered by index, or by item (not ordered).
*/
public ReadOnlyObjectProperty> stateProperty() {
return state.getReadOnlyProperty();
}
protected void setState(VFXGridState state) {
this.state.set(state);
}
public boolean isNeedsViewportLayout() {
return needsViewportLayout.get();
}
/**
* Specifies whether the viewport needs to compute the layout of its content.
*
* Since this is read-only, layout requests must be sent by using {@link #requestViewportLayout()}.
*/
public ReadOnlyBooleanProperty needsViewportLayoutProperty() {
return needsViewportLayout.getReadOnlyProperty();
}
protected void setNeedsViewportLayout(boolean needsViewportLayout) {
this.needsViewportLayout.set(needsViewportLayout);
}
}