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

io.github.palexdev.virtualizedfx.grid.paginated.PaginatedVirtualGrid 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.grid.paginated;

import io.github.palexdev.mfxcore.base.beans.range.IntegerRange;
import io.github.palexdev.mfxcore.base.properties.styleable.StyleableIntegerProperty;
import io.github.palexdev.mfxcore.collections.ObservableGrid;
import io.github.palexdev.mfxcore.utils.NumberUtils;
import io.github.palexdev.mfxcore.utils.fx.StyleUtils;
import io.github.palexdev.virtualizedfx.cell.GridCell;
import io.github.palexdev.virtualizedfx.controls.VirtualScrollPane;
import io.github.palexdev.virtualizedfx.grid.GridHelper;
import io.github.palexdev.virtualizedfx.grid.GridRow;
import io.github.palexdev.virtualizedfx.grid.GridState;
import io.github.palexdev.virtualizedfx.grid.VirtualGrid;
import io.github.palexdev.virtualizedfx.grid.paginated.PaginatedHelper.PaginatedGridHelper;
import io.github.palexdev.virtualizedfx.utils.VSPUtils;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ReadOnlyIntegerProperty;
import javafx.beans.property.ReadOnlyIntegerWrapper;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.css.CssMetaData;
import javafx.css.Styleable;
import javafx.css.StyleablePropertyFactory;
import javafx.scene.control.Skin;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Extension of {@link VirtualGrid} to offer pagination features.
 * 

* In addition to all inherited features this adds: *

- The current displayed page, {@link #currentPageProperty()} *

- The max number of pages, {@link #maxPageProperty()} *

- The number of rows per page, {@link #rowsPerPageProperty()}. Note that this is also * settable via CSS *

* Note that pages start from index 1. *

* This grid also has its own skin, which is basically the same as {@link VirtualGrid} but adapted to * resize the control depending on the {@link #rowsPerPageProperty()}. *

* Little tips and warnings: *

* Note that this is a naive implementation. Some things/components, like the {@link GridState} or {@link GridRow}, have been * adapted to work with this but if you are not careful you could potentially break or mess things up. *

* For example: the correct way to scroll is to change the current page property, but nothing prevents you to set the position * using the related setters. * To be more precise you can still set the hPos freely as pages are vertically arranged. *

* This grid is intended to use implementations of {@link PaginatedHelper}. Again, nothing prevents you from setting a * {@link #gridHelperSupplierProperty()} that only implements {@link GridHelper}, don't do that! In such case note that * {@link #goToPage(int)} won't work and will end with an exception. */ public class PaginatedVirtualGrid> extends VirtualGrid { //================================================================================ // Properties //================================================================================ private final String STYLE_CLASS = "paginated-virtual-grid"; private final IntegerProperty currentPage = new SimpleIntegerProperty(1) { @Override public void set(int newValue) { int clamped = NumberUtils.clamp(newValue, 1, getMaxPage()); super.set(clamped); } @Override protected void invalidated() { int page = get(); changePage(page); } }; private final ReadOnlyIntegerWrapper maxPage = new ReadOnlyIntegerWrapper() { @Override public void set(int newValue) { int clamped = Math.max(1, newValue); super.set(clamped); } @Override protected void invalidated() { int max = get(); int curr = NumberUtils.clamp(getCurrentPage(), 1, max); setCurrentPage(curr); } }; //================================================================================ // Constructors //================================================================================ public PaginatedVirtualGrid() { super(); initialize(); } public PaginatedVirtualGrid(ObservableGrid items, Function cellFactory) { super(items, cellFactory); initialize(); } //================================================================================ // Methods //================================================================================ private void initialize() { getStyleClass().add(STYLE_CLASS); setGridHelperSupplier(() -> new PaginatedGridHelper(this)); } /** * Shortcut for {@link #setRowsPerPage(int)}. * When the {@link #currentPageProperty()} is invalidated {@link #changePage(int)} is automatically called */ public void goToPage(int page) { setCurrentPage(page); } /** * Shortcut for {@link #goToPage(int)} with 1 as parameter. */ public void goToFirstPage() { goToPage(1); } /** * Shortcut for {@link #goToPage(int)} with {@link #getMaxPage()} as parameter. */ public void goToLastPage() { goToPage(getMaxPage()); } /** * Responsible for updating {@link #maxPageProperty()} when needed. *

* The value is given by {@code Math.ceil(rows / rowsPerPage)}. */ public void updateMaxPage() { int rows = getRowsNum(); int rpp = getRowsPerPage(); int max = (int) Math.ceil(rows / (double) rpp); setMaxPage(max); } /** * Gets the current {@link PaginatedHelper} and calls {@link PaginatedHelper#goToPage(int)}, but * before doing so it ensures that the max page is correct by calling {@link #updateMaxPage()}. */ protected void changePage(int page) { GridHelper helper = getGridHelper(); if (!(helper instanceof PaginatedHelper)) throw new IllegalStateException("The grid's helper is not of type PaginatedHelper!"); updateMaxPage(); PaginatedHelper pHelper = (PaginatedHelper) helper; pHelper.goToPage(page); } /** * Returns the range of displayed rows in the current page. *

* It is preferable to use this instead of {@link GridState#getRowsRange()} as this range doesn't take * into account the cells that have been hidden, see {@link GridState#layoutPaginatedRows()}. *

* In case the current {@link #gridHelperProperty()} is null, returns {@code IntegerRange.of(-1)}. */ public IntegerRange getRowsRange() { return Optional.ofNullable(getGridHelper()) .map(h -> { int firstRow = h.firstRow(); int lastRow = Math.min(firstRow + h.maxRows() - 1, getRowsNum() - 1); return IntegerRange.of(firstRow, lastRow); }) .orElseGet(() -> IntegerRange.of(-1)); } /** * Returns a map containing all the currently visible cells in the page. * This is build starting from {@link GridState#getIndexedCells()} and then filtering by the * computed range, {@link #getRowsRange()}. *

* It is preferable to use this with {@link PaginatedVirtualGrid} instead of {@link #getIndexedCells()} because * of this {@link GridState#layoutPaginatedRows()} *

* In case the range is equal to {@code IntegerRange.of(-1)}, returns an empty map. */ public Map getIndexedVisibleCells() { IntegerRange range = getRowsRange(); if (IntegerRange.of(-1).equals(range)) return Map.of(); return getIndexedCells().entrySet().stream() .filter(e -> IntegerRange.inRangeOf(e.getKey(), range)) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } //================================================================================ // Overridden Methods //================================================================================ /** * The paginated grid cannot scroll to a desired row. * * @throws UnsupportedOperationException */ @Override public void scrollToFirstRow() { throw new UnsupportedOperationException("The paginated grid cannot scroll to a desired row"); } /** * The paginated grid cannot scroll to a desired row. * * @throws UnsupportedOperationException */ @Override public void scrollToLastRow() { throw new UnsupportedOperationException("The paginated grid cannot scroll to a desired row"); } @Override protected void onCellSizeChanged() { GridHelper helper = getGridHelper(); if (helper != null) { helper.computeEstimatedSize(); } if (getWidth() != 0.0 && getHeight() != 0.0) { // TODO test with w and h = 0 initially if (!getViewportManager().init()) { requestViewportLayout(); } else { goToPage(1); scrollToColumn(0); } } } @Override protected Skin createDefaultSkin() { return new PaginatedVirtualGridSkin<>(this); } @Override protected List> getControlCssMetaData() { return getClassCssMetaData(); } @Override public VirtualScrollPane wrap() { return VSPUtils.wrap(this); } //================================================================================ // Styleable Properties //================================================================================ private final StyleableIntegerProperty rowsPerPage = new StyleableIntegerProperty( StyleableProperties.ROWS_PER_PAGE, this, "rowsPerPage", 5 ) { @Override protected void invalidated() { updateMaxPage(); changePage(getCurrentPage()); requestViewportLayout(); } }; public int getRowsPerPage() { return rowsPerPage.get(); } /** * Specifies the number of rows to display per page. *

* Note that this, combined with {@link #cellSizeProperty()}, determines the height of the virtual grid. *

* This is settable via CSS with the "-fx-rows-per-page" property. */ public StyleableIntegerProperty rowsPerPageProperty() { return rowsPerPage; } public void setRowsPerPage(int rowsPerPage) { this.rowsPerPage.set(rowsPerPage); } //================================================================================ // CssMetaData //================================================================================ private static class StyleableProperties { private static final StyleablePropertyFactory> FACTORY = new StyleablePropertyFactory<>(VirtualGrid.getClassCssMetaData()); private static final List> cssMetaDataList; private static final CssMetaData, Number> ROWS_PER_PAGE = FACTORY.createSizeCssMetaData( "-fx-rows-per-page", PaginatedVirtualGrid::rowsPerPageProperty, 5 ); static { cssMetaDataList = StyleUtils.cssMetaDataList( VirtualGrid.getClassCssMetaData(), ROWS_PER_PAGE ); } } public static List> getClassCssMetaData() { return StyleableProperties.cssMetaDataList; } //================================================================================ // Getters/Setters //================================================================================ public int getCurrentPage() { return currentPage.get(); } /** * Specifies the current displayed page. */ public IntegerProperty currentPageProperty() { return currentPage; } public void setCurrentPage(int currentPage) { this.currentPage.set(currentPage); } public int getMaxPage() { return maxPage.get(); } /** * Specifies the maximum number of pages, aka last page number. */ public ReadOnlyIntegerProperty maxPageProperty() { return maxPage.getReadOnlyProperty(); } protected void setMaxPage(int maxPage) { this.maxPage.set(maxPage); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy