All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.palexdev.virtualizedfx.table.VirtualTable Maven / Gradle / Ivy

/*
 * Copyright (C) 2022 Parisi Alessandro
 * 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.table;

import io.github.palexdev.mfxcore.base.beans.Position;
import io.github.palexdev.mfxcore.base.beans.Size;
import io.github.palexdev.mfxcore.base.beans.range.IntegerRange;
import io.github.palexdev.mfxcore.base.beans.range.NumberRange;
import io.github.palexdev.mfxcore.base.properties.PositionProperty;
import io.github.palexdev.mfxcore.base.properties.SizeProperty;
import io.github.palexdev.mfxcore.base.properties.functional.BiFunctionProperty;
import io.github.palexdev.mfxcore.base.properties.functional.SupplierProperty;
import io.github.palexdev.mfxcore.base.properties.styleable.StyleableBooleanProperty;
import io.github.palexdev.mfxcore.base.properties.styleable.StyleableDoubleProperty;
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.utils.NumberUtils;
import io.github.palexdev.mfxcore.utils.fx.PropUtils;
import io.github.palexdev.mfxcore.utils.fx.StyleUtils;
import io.github.palexdev.virtualizedfx.beans.TableStateProperty;
import io.github.palexdev.virtualizedfx.cell.MappingTableCell;
import io.github.palexdev.virtualizedfx.cell.TableCell;
import io.github.palexdev.virtualizedfx.controls.VirtualScrollPane;
import io.github.palexdev.virtualizedfx.enums.ColumnsLayoutMode;
import io.github.palexdev.virtualizedfx.grid.VirtualGrid;
import io.github.palexdev.virtualizedfx.table.TableHelper.FixedTableHelper;
import io.github.palexdev.virtualizedfx.table.TableHelper.VariableTableHelper;
import io.github.palexdev.virtualizedfx.table.defaults.DefaultTableRow;
import io.github.palexdev.virtualizedfx.utils.VSPUtils;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.css.CssMetaData;
import javafx.css.Styleable;
import javafx.css.StyleableProperty;
import javafx.css.StyleablePropertyFactory;
import javafx.geometry.Orientation;
import javafx.scene.control.Control;
import javafx.scene.control.Skin;
import javafx.scene.shape.Rectangle;

import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Supplier;

/**
 * Implementation of a virtual table to virtualize the display of tabular data.
 * 

* The conceptual key difference from {@link VirtualGrid} is that the table is much more specific. * Columns and rows are not just a concept, or a way to track state, they are real nodes, each with its own "duties" * that will be explored later below. *

* Extends {@link Control}, has its own skin implementation {@link VirtualTableSkin} and its own cell system, * {@link TableCell} and {@link MappingTableCell}. *

* These are all the features: *

- The items are managed automatically (additions, removals, replacements, sorting) *

- The columns list is also managed automatically, plus there's a supplementary {@code Map} which allows * to get the index of a column really fast, {@link #getIndexedColumns()} *

- The rows can be customized and changed at any time through the apposite {@link #rowFactoryProperty()} *

- The control manages the height of all the cells through the {@link #cellHeightProperty()}, which is also * settable via CSS *

- There's also a property to specify the size of the columns, {@link #columnSizeProperty()}, which is also * settable via CSS, see {@link StyleableSizeProperty} *

- You can programmatically set the position of the viewport through a series of public methods *

- It is possible to retrieve the current shown/built cells as well as other information regarding the state of * the viewport through the {@link #stateProperty()} *

- It is possible to observe for changes to the estimated size of the viewport through the {@link #estimatedSizeProperty()} *

- It is possible to programmatically tell the viewport to update its layout with {@link #requestViewportLayout()} *

* Last but not least the {@link #columnsLayoutModeProperty()}. This particular property allows you to make the table * work in two completely different modes. *

- In the {@link ColumnsLayoutMode#FIXED} mode, columns will have a fixed size just like the cell, these * sizes are specified by the {@link #columnSizeProperty()}. As you can probably guess, this is the most efficient mode *

- In the {@link ColumnsLayoutMode#VARIABLE} mode, columns can have a variable width, allowing for example to resize * columns at runtime with the mouse, or use methods like {@link TableHelper#autosizeColumn(TableColumn)}. * In this mode the {@link #columnSizeProperty()} is still used though: the specified height will be fixed and equal for all * the columns, the width instead specifies the minimum width a column must have at any time. *

* This virtualized control also makes use of a cache. {@link TableCache} helps the table with horizontal scrolling, * the cells of columns that are not visible anymore in the viewport are cached. When the column becomes visible again, * instead of creating new cells (which is a costly operation in any case since Nodes are created), they are retrieved * from the cache. It is also automatically managed in case factories change or columns are added/removed/replaced. *

* Note on a strange bug: if for whatever reason you need to swap two columns it is highly recommended to follow * this example: *
 * {@code
 *      // Instead of this...
 *      Collections.swap(table.getColumns(), firstIndex, secondIndex);
 *
 *      // Use this...
 * 	    List>> tmp = new ArrayList<>(table.getColumns());
 * 	    Collections.swap(tmp, firstIndex, secondIndex);
 * 	    table.getColumns().setAll(tmp);
 * }
 * 
*

* More notes on the internals... *

* As you can see {@code VirtualTable} doesn't need/offer any info about what type of columns * or cells will be used. This is because of two main reasons: *

1) This simplifies the structure, way less battles with Java generics which can be super dumb at times *

2) Separation of roles *

* As stated above, {@code VirtualTable} has a specific structure because the view is organized in concrete * rows and columns. *

* Columns are dumb nodes. They do not contain the cell, but know enough about them to allow their creation, * each column has its own cell factory. *

* Rows on the other hand are more complex and useful. These are the actual containers for the cells, and are also * responsible for managing their state (like: which columns range is visualized, which is the index of the row, as well * as the needed behavior to update them) *

* The layout strategy is controlled by the set {@link TableHelper}, this way it can be deeply customized * just by implementing your own helper (before doing so it's highly recommended to check the source code of the * tow default ones) *

* If the table is being used with a model that doesn't make use of JavaFX properties, to signal the table that * items have changed you can use {@link #updateTable(boolean)}. * * @param the type of objects to represent */ public class VirtualTable extends Control implements VirtualScrollPane.Wrappable { //================================================================================ // Properties //================================================================================ private final String STYLE_CLASS = "virtual-table"; private final TableManager manager = new TableManager<>(this); private final TableCache cache = new TableCache<>(this); private final ObjectProperty> items = PropUtils.mappedObjectProperty(val -> val != null ? val : FXCollections.observableArrayList() ); private final ObservableList>> columns = FXCollections.observableArrayList(); private final BiFunctionProperty> rowFactory = new BiFunctionProperty<>() { @Override protected void invalidated() { onRowFactoryChanged(); } }; private final PositionProperty position = new PositionProperty(Position.of(0, 0)) { @Override public void set(Position newValue) { if (newValue == null) { super.set(Position.of(0, 0)); return; } TableHelper helper = getTableHelper(); if (helper == null) { super.set(newValue); return; } double x = NumberUtils.clamp(newValue.getX(), 0, helper.maxHScroll()); double y = NumberUtils.clamp(newValue.getY(), 0, helper.maxVScroll()); newValue.setX(x); newValue.setY(y); super.set(newValue); } }; private final ReadOnlyObjectWrapper tableHelper = new ReadOnlyObjectWrapper<>() { @Override public void set(TableHelper newValue) { TableHelper oldValue = get(); if (oldValue != null) oldValue.dispose(); super.set(newValue); } }; private final SupplierProperty tableHelperSupplier = new SupplierProperty<>() { @Override protected void invalidated() { TableHelper helper = get().get(); setTableHelper(helper); } }; private final SizeProperty estimatedSize = new SizeProperty(Size.of(0, 0)); private final ReadOnlyBooleanWrapper needsViewportLayout = new ReadOnlyBooleanWrapper(false); private final Map>, Integer> idxColumns = new HashMap<>(); private boolean updateRequested = false; //================================================================================ // Constructors //================================================================================ public VirtualTable() { this(FXCollections.observableArrayList(), FXCollections.observableArrayList()); } public VirtualTable(ObservableList items, TableColumn>... columns) { this(items, FXCollections.observableArrayList(columns)); } public VirtualTable(ObservableList items, ObservableList>> columns) { setItems(items); this.columns.setAll(columns); initialize(); } //================================================================================ // Methods //================================================================================ private void initialize() { getStyleClass().add(STYLE_CLASS); setTableHelperSupplier(() -> (getColumnsLayoutMode() == ColumnsLayoutMode.FIXED) ? new FixedTableHelper(this) : new VariableTableHelper(this) ); setTableHelper(getTableHelperSupplier().get()); defaultRowFactory(); columns.addListener(this::onColumnsChanged); } /** * Basically a setter for {@link #needsViewportLayoutProperty()}. *

* This sets the property to true causing the virtual table skin to catch the change through a listener * which then tells the viewport to recompute the layout. */ public void requestViewportLayout() { setNeedsViewportLayout(true); } /** * Sets the {@link #rowFactoryProperty()} to a default function which produces {@link DefaultTableRow}s. */ public void defaultRowFactory() { setRowFactory((index, columns) -> new DefaultTableRow<>(this, index, columns)); } /** * This method is called every time the {@link #rowFactoryProperty()} changes and is responsible for * updating the rContainer appropriately. *

* Adopts a permissive approach, meaning that if the new factory is null, the columns will remain * and only the rows will be removed. *

* If the new factory is not null than the viewport is reset. */ protected void onRowFactoryChanged() { BiFunction> factory = getRowFactory(); if (factory == null) { TableState state = getState(); state.clear(); TableState newState = new TableState<>(this, IntegerRange.of(-1), state.getColumnsRange()); newState.rowsChanged(); ((TableStateProperty) stateProperty()).set(newState); return; } manager.reset(); } /** * This method is called every time the {@link #cellHeightProperty()} changes, and is responsible * for updating the viewport. Different implementations may take different approaches as how to * update it. */ protected void onCellHeightChanged() { TableHelper helper = getTableHelper(); helper.computeEstimatedSize(); if (getWidth() != 0 && getHeight() != 0.0) { if (!manager.init()) { requestViewportLayout(); } else { setPosition(0, 0); } } } /** * This method is called every time the {@link #columnSizeProperty()} changes, and is responsible * for resetting the viewport with {@link TableManager#reset()} *

* The default behavior uses a "better safe than sorry" strategy by resetting the viewport rather than * trying to transition to a valid state. Different implementations may take different approaches as how to do this. */ protected void onColumnSizeChanged() { TableHelper helper = getTableHelper(); helper.computeEstimatedSize(); helper.computePositions(getState(), true, false); manager.reset(); } /** * This method is called every time the columns list changed and is responsible for building or updating the * {@link #getIndexedColumns()} map when needed, as well as resetting the viewport if the control's skin * is not null. */ protected void onColumnsChanged(ListChangeListener.Change>> c) { boolean rebuildMap = false; while (c.next()) { if (c.wasPermutated()) { rebuildMap = true; } if (c.wasRemoved()) { c.getRemoved().forEach(idxColumns::remove); rebuildMap = true; } if (c.wasAdded()) { List>> added = c.getAddedSubList(); int from = c.getFrom(); int to = from + added.size(); int j = 0; for (int i = from; i < to; i++) { TableColumn> column = added.get(j); idxColumns.put(column, i); j++; } } } if (rebuildMap) { for (int i = 0; i < columns.size(); i++) { TableColumn> column = columns.get(i); idxColumns.put(column, i); } } Skin skin = getSkin(); if (skin != null) manager.reset(); } /** * Columns that change their cell factory at runtime should call this method to transition to a * new valid state. * * @see TableManager#onColumnChangedFactory(TableColumn) * @see TableState#columnChangedFactory(TableColumn) */ public void onColumnChangedFactory(TableColumn> column) { manager.onColumnChangedFactory(column); } /** * This can be used to forcefully update all the currently visualized cells in the table by calling * {@link TableCell#updateItem(Object)}. *

* Optionally with the "reset" flag set to true, the table's viewport can also be reset, {@link TableManager#reset()}, * this will work only if the table has already been laid out at least once and its skin is not null. */ public void updateTable(boolean reset) { try { updateRequested = true; if (reset) { if (getSkin() == null) return; manager.reset(); } else { TableState state = getState(); if (state == TableState.EMPTY || state.isEmpty()) return; state.getRowsUnmodifiable().values().forEach(TableRow::updateItem); } } finally { updateRequested = false; } } //================================================================================ // Delegate Methods //================================================================================ public Size getEstimatedSize() { return estimatedSize.get(); } /** * Specifies the total virtual size of the viewport as a {@link Size} object. */ public ReadOnlyObjectProperty estimatedSizeProperty() { return estimatedSize; } public TableState getState() { return manager.getState(); } /** * Carries the {@link TableState} object which represents the current state of the viewport. * This property is useful to catch any change happening in the viewport, and carries valuable information. */ public ReadOnlyObjectProperty> stateProperty() { return manager.stateProperty(); } public NumberRange getLastRowsRange() { return manager.getLastRowsRange(); } /** * Specifies the last range of displayed rows by the viewport as an {@link IntegerRange}. */ public ReadOnlyObjectProperty> lastRowsRangeProperty() { return manager.lastRowsRangeProperty(); } public NumberRange getLastColumnsRange() { return manager.getLastColumnsRange(); } /** * Specifies the last range of displayed columns by the viewport as an {@link IntegerRange}. */ public ReadOnlyObjectProperty> lastColumnsRangeProperty() { return manager.lastColumnsRangeProperty(); } /** * Specifies whether a change is being processed by {@link TableManager#onChange(ListChangeListener.Change)}. */ public boolean isProcessingChange() { return manager.isProcessingChange(); } /** * Scrolls to the first row. */ public void scrollToFirstRow() { scrollToRow(0); } /** * Scrolls to the last row. */ public void scrollToLastRow() { scrollToRow(getItems().size() - 1); } /** * Scrolls to the given row index. */ public void scrollToRow(int index) { getTableHelper().scrollToRow(index); } /** * Scrolls to the first columns. */ public void scrollToFirstColumn() { scrollToColumn(0); } /** * Scrolls to the last column. */ public void scrollToLastColumn() { scrollToColumn(getColumns().size() - 1); } /** * Scrolls to the given column index. */ public void scrollToColumn(int index) { getTableHelper().scrollToColumn(index); } /** * Scrolls by the given amount of pixels in the given direction. * * @param orientation the scroll direction */ public void scrollBy(double pixels, Orientation orientation) { getTableHelper().scrollBy(pixels, orientation); } /** * Scrolls to the given pixel value in the given direction. * * @param orientation the scroll direction */ public void scrollTo(double pixel, Orientation orientation) { getTableHelper().scrollTo(pixel, orientation); } //================================================================================ // Overridden Methods //================================================================================ @Override protected Skin createDefaultSkin() { return new VirtualTableSkin<>(this); } @Override protected List> getControlCssMetaData() { return getClassCssMetaData(); } @Override public VirtualScrollPane wrap() { return VSPUtils.wrap(this); } //================================================================================ // Styleable Properties //================================================================================ private final StyleableDoubleProperty cellHeight = new StyleableDoubleProperty( StyleableProperties.CELL_HEIGHT, this, "cellHeight", 32.0 ) { @Override protected void invalidated() { onCellHeightChanged(); } }; private final StyleableSizeProperty columnSize = new StyleableSizeProperty( StyleableProperties.COLUMN_SIZE, this, "columnSize", Size.of(100.0, 32.0) ) { @Override protected void invalidated() { onColumnSizeChanged(); } }; private final StyleableObjectProperty columnsLayoutMode = new StyleableObjectProperty<>( StyleableProperties.COLUMNS_LAYOUT_MODE, this, "columnsLayoutMode", ColumnsLayoutMode.FIXED ) { @Override protected void invalidated() { TableHelper helper = getTableHelperSupplier().get(); setTableHelper(helper); manager.reset(); } }; private final StyleableDoubleProperty clipBorderRadius = new StyleableDoubleProperty( StyleableProperties.CLIP_BORDER_RADIUS, this, "clipBorderRadius", 0.0 ); private final StyleableBooleanProperty enableColumnsCache = new StyleableBooleanProperty( StyleableProperties.ENABLE_COLUMNS_CACHE, this, "enableColumnsCache", true ) { @Override protected void invalidated() { boolean val = get(); if (!val) cache.clear(); } }; public double getCellHeight() { return cellHeight.get(); } /** * Specifies the fixed height for the rows/cells. *

* It is also possible to set this property via CSS with the {@code "-fx-cell-height"} property. */ public StyleableDoubleProperty cellHeightProperty() { return cellHeight; } public void setCellHeight(double cellHeight) { this.cellHeight.set(cellHeight); } public Size getColumnSize() { return columnSize.get(); } /** * Specifies the size of the columns as a {@link Size}. *

* It is also possible to set this property via CSS with the {@code "-fx-column-size"} property, * check {@link StyleableSizeProperty} and {@link SizeConverter} for more info. *

* This size is used differently according to the se {@link #columnsLayoutModeProperty()}: *

- In {@link ColumnsLayoutMode#FIXED} mode the specified width is fixed for all the columns * (except the last one if more space is available in the table's viewport) *

- In {@link ColumnsLayoutMode#VARIABLE} mode the specified width is used as the minimum size * all columns must have at the start */ public StyleableSizeProperty columnSizeProperty() { return columnSize; } public void setColumnSize(Size columnSize) { this.columnSize.set(columnSize); } public ColumnsLayoutMode getColumnsLayoutMode() { return columnsLayoutMode.get(); } /** * Specifies how columns are laid out and managed by the table. * * @see VirtualTable * @see #columnSizeProperty() * @see ColumnsLayoutMode */ public StyleableObjectProperty columnsLayoutModeProperty() { return columnsLayoutMode; } public void setColumnsLayoutMode(ColumnsLayoutMode columnsLayoutMode) { this.columnsLayoutMode.set(columnsLayoutMode); } 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 virtual table, this * prevents the content from going outside the view. *

* This is mostly useful if not using the table with {@link VirtualScrollPane}, this is the same as * {@link VirtualScrollPane#clipBorderRadiusProperty()}. *

* 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. *

* It is also possible to set this property via CSS with the {@code "-fx-clip-border-radius"} property. */ public StyleableDoubleProperty clipBorderRadiusProperty() { return clipBorderRadius; } public void setClipBorderRadius(double clipBorderRadius) { this.clipBorderRadius.set(clipBorderRadius); } public boolean isColumnsCacheEnabled() { return enableColumnsCache.get(); } /** * Specifies whether to enable or not the cache mechanism for the columns. *

* This is also settable via CSS with the "-fx-enable-columns-cache" property, by default * it is enabled. * * @see VirtualTable * @see TableCache */ public StyleableBooleanProperty enableColumnsCacheProperty() { return enableColumnsCache; } public void setEnableColumnsCache(boolean enableColumnsCache) { this.enableColumnsCache.set(enableColumnsCache); } //================================================================================ // CssMetaData //================================================================================ private static class StyleableProperties { private static final StyleablePropertyFactory> FACTORY = new StyleablePropertyFactory<>(Control.getClassCssMetaData()); private static final List> cssMetaDataList; private static final CssMetaData, Number> CELL_HEIGHT = FACTORY.createSizeCssMetaData( "-fx-cell-height", VirtualTable::cellHeightProperty, 32.0 ); private static final CssMetaData, Size> COLUMN_SIZE = new CssMetaData<>("-fx-column-size", SizeConverter.getInstance(), Size.of(100.0, 32.0)) { @Override public boolean isSettable(VirtualTable styleable) { return !styleable.columnSizeProperty().isBound(); } @Override public StyleableProperty getStyleableProperty(VirtualTable styleable) { return styleable.columnSizeProperty(); } }; private static final CssMetaData, ColumnsLayoutMode> COLUMNS_LAYOUT_MODE = FACTORY.createEnumCssMetaData( ColumnsLayoutMode.class, "-fx-columns-layout-mode", VirtualTable::columnsLayoutModeProperty, ColumnsLayoutMode.FIXED ); private static final CssMetaData, Number> CLIP_BORDER_RADIUS = FACTORY.createSizeCssMetaData( "-fx-clip-border-radius", VirtualTable::clipBorderRadiusProperty, 0.0 ); private static final CssMetaData, Boolean> ENABLE_COLUMNS_CACHE = FACTORY.createBooleanCssMetaData( "-fx-enable-columns-cache", VirtualTable::enableColumnsCacheProperty, true ); static { cssMetaDataList = StyleUtils.cssMetaDataList( Control.getClassCssMetaData(), CELL_HEIGHT, COLUMN_SIZE, COLUMNS_LAYOUT_MODE, CLIP_BORDER_RADIUS, ENABLE_COLUMNS_CACHE ); } } public static List> getClassCssMetaData() { return StyleableProperties.cssMetaDataList; } //================================================================================ // Getters/Setters //================================================================================ /** * @return the {@link TableManager} instance for this {@code VirtualTable} */ protected TableManager getViewportManager() { return manager; } /** * @return the {@link TableCache} instance of this {@code VirtualTable} */ public TableCache getTableCache() { return cache; } public ObservableList getItems() { return items.get(); } /** * Specifies the items list. */ public ObjectProperty> itemsProperty() { return items; } public void setItems(ObservableList items) { this.items.set(items); } /** * @return the column at the given index in the {@link #getColumns()} list. * Note that this is an "exception safe" approach, if the index is invalid this will return * null */ public TableColumn> getColumn(int index) { try { return columns.get(index); } catch (Exception ex) { return null; } } /** * Returns the index at which the given column resides in the {@link #getColumns()} list. * Since performance is critical for a virtualized control, this first attempts at getting the index * from the {@link #getIndexedColumns()} map, if it is not found then proceeds with {@link List#indexOf(Object)}. */ public int getColumnIndex(TableColumn> column) { return Optional.ofNullable(idxColumns.get(column)) .orElseGet(() -> columns.indexOf(column)); } /** * @return the list containing the columns */ public ObservableList>> getColumns() { return columns; } /** * This map is built by iterating on the columns list and mapping them to the index * at which they are found. Can also be considered a sort of cache to avoid {@link List#indexOf(Object)} * operations which can be too slow in some cases. */ public Map>, Integer> getIndexedColumns() { return Collections.unmodifiableMap(idxColumns); } public BiFunction> getRowFactory() { return rowFactory.get(); } /** * Specifies the function used by {@link TableState} to produce new {@link TableRow}s. */ public BiFunctionProperty> rowFactoryProperty() { return rowFactory; } public void setRowFactory(BiFunction> rowFactory) { this.rowFactory.set(rowFactory); } /** * Shortcut for {@link #setPosition(double, double)}, which uses the current hPos as the x value for the new * {@link Position} object. */ public void setVPos(double vPos) { setPosition(getHPos(), vPos); } /** * @return the vertical position of the viewport */ public double getVPos() { return getPosition().getY(); } /** * Shortcut for {@link #setPosition(double, double)}, which uses the current vPos as the y value for the new * {@link Position} object. */ public void setHPos(double hPos) { setPosition(hPos, getVPos()); } /** * @return the horizontal position of the viewport */ public double getHPos() { return getPosition().getX(); } public Position getPosition() { return position.get(); } /** * Specifies the current position of the viewport as a {@link Position} object which has * both the x and y positions. */ public PositionProperty positionProperty() { return position; } /** * Convenience method, alternative to {@link #setPosition(Position)}. */ public void setPosition(double x, double y) { setPosition(Position.of(x, y)); } public void setPosition(Position position) { this.position.set(position); } public TableHelper getTableHelper() { return tableHelper.get(); } /** * The current built helper for the grid, see {@link TableHelper}. */ public ReadOnlyObjectProperty tableHelperProperty() { return tableHelper.getReadOnlyProperty(); } public void setTableHelper(TableHelper tableHelper) { this.tableHelper.set(tableHelper); } public Supplier getTableHelperSupplier() { return tableHelperSupplier.get(); } /** * To allow more customization on how the columns, rows and cells are laid out, the virtual table allows you * to specify a supplier used to build a {@link TableHelper} which is responsible for some core actions * about layout. This way you could implement your own helper and set it through this factory since the * {@link #tableHelperProperty()} is intended to be read-only. */ public SupplierProperty tableHelperSupplierProperty() { return tableHelperSupplier; } public void setTableHelperSupplier(Supplier tableHelperSupplier) { this.tableHelperSupplier.set(tableHelperSupplier); } 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); } /** * @return whether {@link #updateTable(boolean)} was invoked */ public boolean isUpdateRequested() { return updateRequested; } /** * Sets the "updateRequested" flag to true. This can be used by * cells to detect if a forced update of the cells has being invoked for example by * {@link #updateTable(boolean)}. */ protected void setUpdateRequested(boolean updateRequested) { this.updateRequested = updateRequested; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy