Skip to content

Commit

Permalink
Add paging controls settings and improve loading visualization
Browse files Browse the repository at this point in the history
Introduced a new class, `PagingControlsSettingsView`, to enhance paging controls configurability. Updated loading behavior and added new 'loading' status to `PagingListView`, improving user feedback during data fetch operations. Refined CSS and bindings to better manage layout and styling of pagination elements.
  • Loading branch information
dlemmermann committed Dec 3, 2024
1 parent 2f7f192 commit 73c7e03
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 77 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.dlsc.gemsfx.demo;

import com.dlsc.gemsfx.PagingControlBase;
import com.dlsc.gemsfx.PagingControls;
import com.dlsc.gemsfx.Spacer;
import javafx.beans.binding.Bindings;
import javafx.geometry.Pos;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Label;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;

import java.util.List;

public class PagingControlsSettingsView extends VBox {

public PagingControlsSettingsView(PagingControlBase pagingControls) {
setSpacing(10);

Label pageLabel = new Label();
pageLabel.textProperty().bind(Bindings.createStringBinding(() -> "Page Index: " + pagingControls.getPage(), pagingControls.pageProperty()));

Label pageCountLabel = new Label();
pageCountLabel.textProperty().bind(Bindings.createStringBinding(() -> "Page count: " + pagingControls.getPageCount(), pagingControls.pageCountProperty()));

ChoiceBox<PagingControls.FirstLastPageDisplayMode> displayModeChoiceBox = new ChoiceBox<>();
displayModeChoiceBox.getItems().setAll(PagingControls.FirstLastPageDisplayMode.values());
displayModeChoiceBox.valueProperty().bindBidirectional(pagingControls.firstLastPageDisplayModeProperty());

CheckBox showPreviousNextButton = new CheckBox("Show prev / next buttons");
showPreviousNextButton.selectedProperty().bindBidirectional(pagingControls.showPreviousNextPageButtonProperty());

ChoiceBox<PagingControlBase.MessageLabelStrategy> strategyChoiceBox = new ChoiceBox<>();
strategyChoiceBox.getItems().addAll(PagingControlBase.MessageLabelStrategy.values());
strategyChoiceBox.valueProperty().bindBidirectional(pagingControls.messageLabelStrategyProperty());

ChoiceBox<Integer> maxPageIndicatorsBox = new ChoiceBox<>();
maxPageIndicatorsBox.getItems().setAll(List.of(1, 2, 5, 10));
maxPageIndicatorsBox.valueProperty().bindBidirectional(pagingControls.maxPageIndicatorsCountProperty().asObject());

HBox displayModeBox = new HBox(5, new Label("Display mode: "), displayModeChoiceBox);
displayModeBox.setAlignment(Pos.CENTER_LEFT);

HBox strategyBox = new HBox(5, new Label("Label strategy: "), strategyChoiceBox);
strategyBox.setAlignment(Pos.CENTER_LEFT);

HBox indicatorBox = new HBox(5, new Label("# Indicators: "), maxPageIndicatorsBox);
indicatorBox.setAlignment(Pos.CENTER_LEFT);

FlowPane flowPane = new FlowPane(pageLabel, pageCountLabel, new Spacer(), showPreviousNextButton, displayModeBox, strategyBox, indicatorBox);
flowPane.setVgap(10);
flowPane.setHgap(20);

setMaxHeight(Region.USE_PREF_SIZE);

getChildren().setAll(flowPane);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,28 @@ public class PagingListViewApp extends Application {
public void start(Stage stage) {
PagingListView<String> pagingListView = new PagingListView<>();
pagingListView.setPrefWidth(400);
pagingListView.setTotalItemCount(30);
pagingListView.setPageSize(10);
pagingListView.setTotalItemCount(300);
pagingListView.setPageSize(15);
pagingListView.setLoader(lv -> {
if (Math.random() > .75) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
List<String> data = new ArrayList<>();
int offset = lv.getPage() * lv.getPageSize();
for (int i = 0; i < lv.getPageSize(); i++) {
data.add("Item " + (offset + i));
data.add("Item " + (offset + i + 1));
}
return data;
});

Button scenicView = new Button("Scenic View");
scenicView.setOnAction(evt -> ScenicView.show(scenicView.getScene()));

VBox box = new VBox(20, pagingListView, scenicView);
VBox box = new VBox(20, pagingListView, new PagingControlsSettingsView(pagingListView), scenicView);
box.setPadding(new Insets(20));

Scene scene = new Scene(box);
Expand Down
57 changes: 57 additions & 0 deletions gemsfx/src/main/java/com/dlsc/gemsfx/PagingControlBase.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.dlsc.gemsfx;

import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
Expand All @@ -8,12 +9,48 @@
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.HPos;
import javafx.scene.Node;
import javafx.scene.control.Control;
import javafx.scene.control.Label;
import javafx.util.Callback;

public abstract class PagingControlBase extends Control {

public PagingControlBase() {
setMessageLabelProvider(view -> {
if (getPageCount() == 0) {
return "No items";
}

if (getPageCount() == 1) {
return "Showing all items";
}

int startIndex = (view.getPage() * getPageSize()) + 1;
int endIndex = startIndex + getPageSize() - 1;

endIndex = Math.min(endIndex, getTotalItemCount());
return "Showing items " + startIndex + " to " + endIndex + " of " + getTotalItemCount();
});

pageCount.bind(Bindings.createIntegerBinding(() -> {
int count = getTotalItemCount() / getPageSize();
if (getTotalItemCount() % getPageSize() > 0) {
count++;
}
return count;
}, totalItemCountProperty(), pageSizeProperty()));

Label firstPageDivider = new Label("...");
firstPageDivider.getStyleClass().addAll("page-divider", "first-page-divider");
setFirstPageDivider(firstPageDivider);

Label lastPageDivider = new Label("...");
lastPageDivider.getStyleClass().addAll("page-divider", "first-page-divider");
setLastPageDivider(lastPageDivider);
}

private final BooleanProperty showPreviousNextPageButton = new SimpleBooleanProperty(this, "showPreviousNextButton", true);

public final boolean getShowPreviousNextPageButton() {
Expand Down Expand Up @@ -306,4 +343,24 @@ public final IntegerProperty pageSizeProperty() {
public final void setPageSize(int pageSize) {
this.pageSize.set(pageSize);
}

private final ObjectProperty<HPos> alignment = new SimpleObjectProperty<>(this, "alignment", HPos.RIGHT);

public final HPos getAlignment() {
return alignment.get();
}

/**
* The alignment property controls where in the view the paging buttons will appear: left,
* center, middle.
*
* @return the alignment / the position of the paging buttons
*/
public final ObjectProperty<HPos> alignmentProperty() {
return alignment;
}

public final void setAlignment(HPos alignment) {
this.alignment.set(alignment);
}
}
54 changes: 0 additions & 54 deletions gemsfx/src/main/java/com/dlsc/gemsfx/PagingControls.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@

import com.dlsc.gemsfx.skins.PagingControlsSkin;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.HPos;
import javafx.scene.control.Label;
import javafx.scene.control.Skin;
import javafx.scene.input.KeyCode;
Expand All @@ -27,21 +24,6 @@ public PagingControls() {
getStyleClass().add(DEFAULT_STYLE_CLASS);

addEventFilter(MouseEvent.MOUSE_PRESSED, evt -> requestFocus());
setMessageLabelProvider(view -> {
if (getPageCount() == 0) {
return "No items";
}

if (getPageCount() == 1) {
return "Showing all items";
}

int startIndex = (view.getPage() * getPageSize()) + 1;
int endIndex = startIndex + getPageSize() - 1;

endIndex = Math.min(endIndex, getTotalItemCount());
return "Showing items " + startIndex + " to " + endIndex + " of " + getTotalItemCount();
});

addEventHandler(KeyEvent.KEY_PRESSED, evt -> {
if (Objects.equals(evt.getCode(), KeyCode.RIGHT)) {
Expand All @@ -54,22 +36,6 @@ public PagingControls() {
lastPage();
}
});

pageCount.bind(Bindings.createIntegerBinding(() -> {
int count = getTotalItemCount() / getPageSize();
if (getTotalItemCount() % getPageSize() > 0) {
count++;
}
return count;
}, totalItemCountProperty(), pageSizeProperty()));

Label firstPageDivider = new Label("...");
firstPageDivider.getStyleClass().addAll("page-divider", "first-page-divider");
setFirstPageDivider(firstPageDivider);

Label lastPageDivider = new Label("...");
lastPageDivider.getStyleClass().addAll("page-divider", "first-page-divider");
setLastPageDivider(lastPageDivider);
}

@Override
Expand All @@ -81,24 +47,4 @@ protected Skin<?> createDefaultSkin() {
public String getUserAgentStylesheet() {
return Objects.requireNonNull(PagingControls.class.getResource("paging-controls.css")).toExternalForm();
}

private final ObjectProperty<HPos> alignment = new SimpleObjectProperty<>(this, "alignment", HPos.RIGHT);

public final HPos getAlignment() {
return alignment.get();
}

/**
* The alignment property controls where in the view the paging buttons will appear: left,
* center, middle.
*
* @return the alignment / the position of the paging buttons
*/
public final ObjectProperty<HPos> alignmentProperty() {
return alignment;
}

public final void setAlignment(HPos alignment) {
this.alignment.set(alignment);
}
}
26 changes: 25 additions & 1 deletion gemsfx/src/main/java/com/dlsc/gemsfx/PagingListView.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.dlsc.gemsfx;

import com.dlsc.gemsfx.LoadingPane.Status;
import com.dlsc.gemsfx.skins.InnerListViewSkin;
import com.dlsc.gemsfx.skins.PagingListViewSkin;
import javafx.beans.InvalidationListener;
Expand All @@ -13,6 +14,7 @@
import javafx.collections.ObservableList;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.geometry.HPos;
import javafx.scene.Node;
import javafx.scene.control.Cell;
import javafx.scene.control.ListCell;
Expand Down Expand Up @@ -52,6 +54,7 @@ public PagingListView() {
listView.setSkin(innerListViewSkin);

loadingService.setOnSucceeded(evt -> {
loadingStatus.set(Status.OK);
List<T> newList = loadingService.getValue();
if (newList != null) {
items.setAll(newList);
Expand All @@ -60,6 +63,9 @@ public PagingListView() {
}
});

loadingService.setOnRunning(evt -> loadingStatus.set(Status.LOADING));
loadingService.setOnFailed(evt -> loadingStatus.set(Status.ERROR));

InvalidationListener loadListener = it -> loadingService.restart();

pageProperty().addListener(loadListener);
Expand Down Expand Up @@ -101,6 +107,20 @@ public final ListView<T> getListView() {
return listView;
}

private final ObjectProperty<Status> loadingStatus = new SimpleObjectProperty<>(this, "loadingStatus", Status.OK);

public final Status getLoadingStatus() {
return loadingStatus.get();
}

public final ObjectProperty<Status> loadingStatusProperty() {
return loadingStatus;
}

public final void setLoadingStatus(Status loadingStatus) {
this.loadingStatus.set(loadingStatus);
}

private class LoadingService extends Service<List<T>> {

@Override
Expand All @@ -109,7 +129,11 @@ protected Task<List<T>> createTask() {
@Override
protected List<T> call() {
if (!isCancelled()) {
return loader.get().call(PagingListView.this);
Callback<PagingListView<T>, List<T>> loader = PagingListView.this.loader.get();
if (loader == null) {
throw new IllegalArgumentException("data loader can not be null");
}
return loader.call(PagingListView.this);
}
return Collections.emptyList();
}
Expand Down
Loading

0 comments on commit 73c7e03

Please sign in to comment.