Skip to content

Commit

Permalink
Small fixes and refactoring to PagingListView.
Browse files Browse the repository at this point in the history
  • Loading branch information
dlemmermann committed Dec 6, 2024
1 parent 2aeb7c6 commit 4b4de47
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package com.dlsc.gemsfx.demo;

import com.dlsc.gemsfx.EnhancedLabel;
import com.dlsc.gemsfx.PagingListView;
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.scenicview.ScenicView;
Expand All @@ -17,23 +18,25 @@

public class PagingListViewApp extends Application {

private final BooleanProperty simulateDelayProperty = new SimpleBooleanProperty(false);

@Override
public void start(Stage stage) {
PagingListView<String> pagingListView = new PagingListView<>();
pagingListView.setPrefWidth(400);
pagingListView.setTotalItemCount(7);
pagingListView.setTotalItemCount(205);
pagingListView.setPageSize(10);
pagingListView.setLoader(lv -> {
if (Math.random() > .75) {
pagingListView.setLoader(loadRequest -> {
if (simulateDelayProperty.get()) {
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++) {
int offset = loadRequest.getPage() * loadRequest.getPageSize();
for (int i = 0; i < loadRequest.getPageSize(); i++) {
int index = offset + i + 1;
if (index <= pagingListView.getTotalItemCount()) {
data.add("Item " + (offset + i + 1));
Expand All @@ -50,7 +53,12 @@ public void start(Stage stage) {
CheckBox fillBox = new CheckBox("Fill last page");
fillBox.selectedProperty().bindBidirectional(pagingListView.fillLastPageProperty());

VBox box = new VBox(20, pagingListView, fillBox, new PagingControlsSettingsView(pagingListView), scenicView);
CheckBox simulateDelay = new CheckBox("Simulate delay");
simulateDelay.selectedProperty().bindBidirectional(simulateDelayProperty);

HBox settingsBox = new HBox(10, fillBox, simulateDelay);

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

Scene scene = new Scene(box);
Expand Down
86 changes: 79 additions & 7 deletions gemsfx/src/main/java/com/dlsc/gemsfx/PagingListView.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,17 @@
import javafx.scene.control.Skin;
import javafx.util.Callback;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;

public class PagingListView<T> extends PagingControlBase {

private final LoadingService loadingService = new LoadingService();

private final ObservableList<T> items = FXCollections.observableArrayList();
private final ObservableList<T> items = FXCollections.observableArrayList(new CopyOnWriteArrayList<>());

private final ObservableList<T> unmodifiableItems = FXCollections.unmodifiableObservableList(items);

Expand Down Expand Up @@ -104,6 +106,11 @@ public String getUserAgentStylesheet() {
return Objects.requireNonNull(PagingListView.class.getResource("paging-list-view.css")).toExternalForm();
}

/**
* Returns the wrapped list view.
*
* @return the list view
*/
public final ListView<T> getListView() {
return listView;
}
Expand Down Expand Up @@ -135,6 +142,12 @@ public final Status getLoadingStatus() {
return loadingStatus.get();
}

/**
* The loading status used for the wrapped {@link LoadingPane}. The loading pane will appear if the
* loader takes a long time to return the new page items.
*
* @return the loading status
*/
public final ObjectProperty<Status> loadingStatusProperty() {
return loadingStatus;
}
Expand All @@ -148,40 +161,99 @@ private class LoadingService extends Service<List<T>> {
@Override
protected Task<List<T>> createTask() {
return new Task<>() {

final LoadRequest loadRequest = new LoadRequest(getPage(), getPageSize());

@Override
protected List<T> call() {
if (!isCancelled()) {
Callback<PagingListView<T>, List<T>> loader = PagingListView.this.loader.get();
Callback<LoadRequest, List<T>> loader = PagingListView.this.loader.get();
if (loader == null) {
throw new IllegalArgumentException("data loader can not be null");
}
return loader.call(PagingListView.this);

/*
* Important to wrap in a list, otherwise we can get a concurrent modification
* exception when the result gets applied to the "items" list in the service
* event handler for "success". Not sure why this fixes that issue.
*/
return new ArrayList<>(loader.call(loadRequest));
}

return Collections.emptyList();
}
};
}
}

/**
* Triggers an explicit reload of the list view.
*/
public final void reload() {
loadingService.restart();
}

/**
* Returns an unmodifiable observable list with the items shown by the current
* page.
*
* @return the currently shown items
*/
public final ObservableList<T> getUnmodifiableItems() {
return unmodifiableItems;
}

private final ObjectProperty<Callback<PagingListView<T>, List<T>>> loader = new SimpleObjectProperty<>(this, "loader");
/**
* The input parameter for the loader callback.
*
* @see #loaderProperty()
*/
public static class LoadRequest {

private final int page;
private final int pageSize;

/**
* Constructs a new load request for the given page and page size.
*
* @param page the index of the page (starts with 0)
* @param pageSize the size of the page (number of items per page)
*/
public LoadRequest(int page, int pageSize) {
this.page = page;
this.pageSize = pageSize;
}

/**
* The index of the page.
*
* @return the page index
*/
public int getPage() {
return page;
}

/**
* The size of the page.
*
* @return the page size
*/
public int getPageSize() {
return pageSize;
}
}

private final ObjectProperty<Callback<LoadRequest, List<T>>> loader = new SimpleObjectProperty<>(this, "loader");

public Callback<PagingListView<T>, List<T>> getLoader() {
public Callback<LoadRequest, List<T>> getLoader() {
return loader.get();
}

public ObjectProperty<Callback<PagingListView<T>, List<T>>> loaderProperty() {
public ObjectProperty<Callback<LoadRequest, List<T>>> loaderProperty() {
return loader;
}

public void setLoader(Callback<PagingListView<T>, List<T>> loader) {
public void setLoader(Callback<LoadRequest, List<T>> loader) {
this.loader.set(loader);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ public class SimplePagingListView<T> extends PagingListView<T> {
* Constructs a new list view and sets a loader that uses the data list.
*/
public SimplePagingListView() {
setLoader(lv -> {
int pageSize = getPageSize();
int index = getPage() * pageSize;
setLoader(request -> {
int pageSize = request.getPageSize();
int index = request.getPage() * pageSize;
return getItems().subList(index, Math.min(index + pageSize, getItems().size()));
});

Expand Down

0 comments on commit 4b4de47

Please sign in to comment.