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

io.github.palexdev.materialfx.controls.MFXPagination Maven / Gradle / Ivy

There is a newer version: 11.17.0
Show newest version
/*
 * Copyright (C) 2022 Parisi Alessandro
 * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
 *
 * MaterialFX 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.
 *
 * MaterialFX 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 MaterialFX.  If not, see .
 */

package io.github.palexdev.materialfx.controls;

import io.github.palexdev.materialfx.MFXResourcesLoader;
import io.github.palexdev.materialfx.beans.properties.functional.FunctionProperty;
import io.github.palexdev.materialfx.beans.properties.functional.SupplierProperty;
import io.github.palexdev.materialfx.controls.cell.MFXPage;
import io.github.palexdev.materialfx.skins.MFXPaginationSkin;
import io.github.palexdev.materialfx.utils.NumberUtils;
import javafx.beans.property.*;
import javafx.geometry.Orientation;
import javafx.scene.control.Control;
import javafx.scene.control.Skin;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * This is the implementation of a smart, material pagination control in JavaFX.
 * 

* There are three main properties: *

- the current page property that specifies the current selected page *

- the max page property that specifies the number of pages *

- the pages to show property that specifies how many pages can be shown at a time by the control *

* {@code MFXPagination} is highly customizable. *

* The {@link #indexesSupplierProperty()} allows you to specify how to build the page indexes. The default * algorithm, set by {@link #defaultIndexesSupplier()}, is a digg-style pagination. *

* The {@link #pageCellFactoryProperty()} allows you to specify how to build the pages' button. *

* The {@link #ellipseStringProperty()} allows you to specify the string shown on the truncated pages. *

* The {@link #showPopupForTruncatedPagesProperty()} allows you to decide whether to show a little popup * when clicking ona truncated page, which contains a list of the pages in between, for faster navigation. * The popup in created and handled by the default cells, {@link MFXPage}. *

* The {@link #orientationProperty()} allows you to specify the pagination orientation. */ public class MFXPagination extends Control { //================================================================================ // Properties //================================================================================ private final String STYLE_CLASS = "mfx-pagination"; private final String STYLESHEET = MFXResourcesLoader.load("css/MFXPagination.css"); private final IntegerProperty currentPage = new SimpleIntegerProperty(1) { @Override public void set(int newValue) { int number = NumberUtils.clamp(newValue, 1, getMaxPage()); super.set(number); } }; private final IntegerProperty maxPage = new SimpleIntegerProperty(); private final IntegerProperty pagesToShow = new SimpleIntegerProperty(); private final SupplierProperty> indexesSupplier = new SupplierProperty<>(); private final FunctionProperty pageCellFactory = new FunctionProperty<>(index -> new MFXPage(this, index)); private final StringProperty ellipseString = new SimpleStringProperty("..."); private final ObjectProperty orientation = new SimpleObjectProperty<>(Orientation.HORIZONTAL); private final BooleanProperty showPopupForTruncatedPages = new SimpleBooleanProperty(false); //================================================================================ // Constructors //================================================================================ public MFXPagination() { this(0); } public MFXPagination(int maxPage) { this(maxPage, 9); } public MFXPagination(int maxPage, int toShow) { setMaxPage(maxPage); setPagesToShow(toShow); initialize(); } //================================================================================ // Methods //================================================================================ private void initialize() { getStyleClass().add(STYLE_CLASS); defaultIndexesSupplier(); } /** * Sets the default indexing algorithm. */ public void defaultIndexesSupplier() { setIndexesSupplier(this::computePagesIndex); } /** * This is the default algorithm used to build the pages. *

* This is a digg-style pagination algorithm. *

* There are some exception that won't make this work as expected. * If the number of pages is lesser than the specified number of pages to show, * or the specified number of pages to show is lesser than 5, all the indexes * will be shown. */ protected List computePagesIndex() { List indexes = new ArrayList<>(); int current = getCurrentPage(); int nPages = getMaxPage(); int toShow = getPagesToShow(); if (nPages < toShow || toShow < 5) { for (int i = 1; i <= nPages; i++) { indexes.add(i); } return indexes; } int middle = (int) Math.ceil(toShow / 2.0); if (current < middle) { int end = NumberUtils.isEven(toShow) ? middle + 1 : middle; for (int i = 1; i < end; i++) { indexes.add(i); } indexes.add(-1); int remaining = toShow - indexes.size(); for (int i = nPages - remaining + 1; i <= nPages; i++) { indexes.add(i); } } else if (current <= nPages - middle + 1) { indexes.add(1); indexes.add(-1); indexes.addAll(computeMiddleIndexes(current, toShow)); indexes.add(-1); indexes.add(nPages); } else { for (int i = 1; i < middle; i++) { indexes.add(i); } indexes.add(-1); int remaining = toShow - indexes.size(); for (int i = nPages - remaining + 1; i <= nPages; i++) { indexes.add(i); } } return indexes; } /** * Helper method to {@link #computePagesIndex()} to properly compute * the page indexes when the current page is in the middle. */ protected List computeMiddleIndexes(int current, int toShow) { int remaining = toShow - 5; List indexes = new LinkedList<>(); indexes.add(current); int lastMin = current - 1; int lastMax = current + 1; boolean after = true; while (remaining > 0) { if (after) { indexes.add(lastMax); lastMax++; after = false; } else { indexes.add(0, lastMin); lastMin--; after = true; } remaining--; } return indexes; } //================================================================================ // Overridden Methods //================================================================================ @Override protected Skin createDefaultSkin() { return new MFXPaginationSkin(this); } @Override public String getUserAgentStylesheet() { return STYLESHEET; } //================================================================================ // Getters/Setters //================================================================================ public int getCurrentPage() { return currentPage.get(); } /** * Specifies the current selected page. */ public IntegerProperty currentPageProperty() { return currentPage; } public void setCurrentPage(int currentPage) { this.currentPage.set(currentPage); } public int getMaxPage() { return maxPage.get(); } /** * Specifies the max number of pages. */ public IntegerProperty maxPageProperty() { return maxPage; } public void setMaxPage(int maxPage) { this.maxPage.set(maxPage); } public int getPagesToShow() { return pagesToShow.get(); } /** * Specifies the max number of pages to show at a time. */ public IntegerProperty pagesToShowProperty() { return pagesToShow; } public void setPagesToShow(int pagesToShow) { this.pagesToShow.set(pagesToShow); } public Supplier> getIndexesSupplier() { return indexesSupplier.get(); } /** * This supplier specifies the algorithm used to build the pages. */ public SupplierProperty> indexesSupplierProperty() { return indexesSupplier; } public void setIndexesSupplier(Supplier> indexesSupplier) { this.indexesSupplier.set(indexesSupplier); } public Function getPageCellFactory() { return pageCellFactory.get(); } /** * This function specifies how to convert an index to a page. */ public FunctionProperty pageCellFactoryProperty() { return pageCellFactory; } public void setPageCellFactory(Function pageCellFactory) { this.pageCellFactory.set(pageCellFactory); } public String getEllipseString() { return ellipseString.get(); } /** * Specifies the string to show for truncated pages. */ public StringProperty ellipseStringProperty() { return ellipseString; } public void setEllipseString(String ellipseString) { this.ellipseString.set(ellipseString); } public Orientation getOrientation() { return orientation.get(); } /** * Specifies the control's orientation. */ public ObjectProperty orientationProperty() { return orientation; } public void setOrientation(Orientation orientation) { this.orientation.set(orientation); } public boolean isShowPopupForTruncatedPages() { return showPopupForTruncatedPages.get(); } /** * Specifies whether truncated pages should show a popup * containing the pages in between, on click. */ public BooleanProperty showPopupForTruncatedPagesProperty() { return showPopupForTruncatedPages; } public void setShowPopupForTruncatedPages(boolean showPopupForTruncatedPages) { this.showPopupForTruncatedPages.set(showPopupForTruncatedPages); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy