diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/TaskListPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/TaskListPane.java index 4c239432d..590402baa 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/TaskListPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/TaskListPane.java @@ -17,14 +17,17 @@ */ package org.jackhuang.hmcl.ui.construct; +import com.jfoenix.controls.JFXListView; import com.jfoenix.controls.JFXProgressBar; import javafx.application.Platform; +import javafx.beans.binding.Bindings; import javafx.beans.property.ReadOnlyIntegerProperty; import javafx.beans.property.ReadOnlyIntegerWrapper; +import javafx.geometry.Insets; import javafx.scene.control.Label; +import javafx.scene.control.ListCell; import javafx.scene.layout.BorderPane; import javafx.scene.layout.StackPane; -import javafx.scene.layout.VBox; import org.jackhuang.hmcl.download.forge.ForgeInstallTask; import org.jackhuang.hmcl.download.game.GameAssetDownloadTask; import org.jackhuang.hmcl.download.liteloader.LiteLoaderInstallTask; @@ -42,15 +45,16 @@ import java.util.Map; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; public final class TaskListPane extends StackPane { - private final AdvancedListBox listBox = new AdvancedListBox(); + private final JFXListView listBox = new JFXListView<>(); private final Map nodes = new HashMap<>(); private final ReadOnlyIntegerWrapper finishedTasks = new ReadOnlyIntegerWrapper(); private final ReadOnlyIntegerWrapper totTasks = new ReadOnlyIntegerWrapper(); public TaskListPane() { - listBox.setSpacing(0); - getChildren().setAll(listBox); + + listBox.setPadding(Insets.EMPTY); + listBox.setCellFactory(listView -> new ProgressListNode()); } public ReadOnlyIntegerProperty finishedTasksProperty() { @@ -66,7 +70,7 @@ public final class TaskListPane extends StackPane { @Override public void onStart() { Platform.runLater(() -> { - listBox.clear(); + listBox.getItems().clear(); finishedTasks.set(0); totTasks.set(0); }); @@ -108,66 +112,66 @@ public final class TaskListPane extends StackPane { task.setName(i18n("modpack.scan")); } - ProgressListNode node = new ProgressListNode(task); - nodes.put(task, node); - Platform.runLater(() -> listBox.add(node)); - + Platform.runLater(() -> listBox.getItems().add(task)); } @Override public void onFinished(Task task) { - ProgressListNode node = nodes.remove(task); - if (node == null) - return; - node.unbind(); Platform.runLater(() -> { - listBox.remove(node); - finishedTasks.set(finishedTasks.getValue() + 1); - }); - } - - @Override - public void onFailed(Task task, Throwable throwable) { - ProgressListNode node = nodes.remove(task); - if (node == null) - return; - Platform.runLater(() -> { - node.setThrowable(throwable); - finishedTasks.set(finishedTasks.getValue() + 1); + if (listBox.getItems().remove(task)) + finishedTasks.set(finishedTasks.getValue() + 1); }); } }); } - private static class ProgressListNode extends VBox { + private static class ProgressListNode extends ListCell { + private final BorderPane borderPane = new BorderPane(); private final JFXProgressBar bar = new JFXProgressBar(); private final Label title = new Label(); private final Label state = new Label(); - public ProgressListNode(Task task) { - bar.progressProperty().bind(task.progressProperty()); - title.setText(task.getName()); - state.textProperty().bind(task.messageProperty()); - - BorderPane borderPane = new BorderPane(); + { borderPane.setLeft(title); borderPane.setRight(state); - getChildren().addAll(borderPane, bar); + borderPane.setBottom(bar); + borderPane.setMinWidth(0); + borderPane.setPrefWidth(1); + + setPadding(Insets.EMPTY); bar.minWidthProperty().bind(widthProperty()); bar.prefWidthProperty().bind(widthProperty()); bar.maxWidthProperty().bind(widthProperty()); } - public void unbind() { - bar.progressProperty().unbind(); - state.textProperty().unbind(); - } + @Override + protected void updateItem(Task item, boolean empty) { + boolean wasEmpty = isEmpty(); + Task oldTask = getItem(); - public void setThrowable(Throwable throwable) { - unbind(); - state.setText(throwable.getLocalizedMessage()); - bar.setProgress(0); + if (!wasEmpty && oldTask != null) { + bar.progressProperty().unbind(); + state.textProperty().unbind(); + } + + super.updateItem(item, empty); + + if (empty || item == null) { + setGraphic(null); + } else { + setGraphic(borderPane); + bar.visibleProperty().bind(Bindings.createBooleanBinding(() -> item.progressProperty().get() != -1, item.progressProperty())); + bar.progressProperty().bind(item.progressProperty()); + state.textProperty().bind(Bindings.createObjectBinding(() -> { + if (item.getState() == Task.TaskState.FAILED) { + return item.getLastException().getLocalizedMessage(); + } else { + return item.messageProperty().get(); + } + }, item.messageProperty(), item.stateProperty())); + title.setText(item.getName()); + } } } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java index 7f6911321..60efa07e3 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java @@ -18,10 +18,7 @@ package org.jackhuang.hmcl.task; import javafx.application.Platform; -import javafx.beans.property.ReadOnlyDoubleProperty; -import javafx.beans.property.ReadOnlyDoubleWrapper; -import javafx.beans.property.ReadOnlyStringProperty; -import javafx.beans.property.ReadOnlyStringWrapper; +import javafx.beans.property.*; import org.jackhuang.hmcl.event.EventManager; import org.jackhuang.hmcl.util.AutoTypingMap; import org.jackhuang.hmcl.util.InvocationDispatcher; @@ -58,14 +55,18 @@ public abstract class Task { this.significance = significance; } - private TaskState state = TaskState.READY; + private ReadOnlyObjectWrapper state = new ReadOnlyObjectWrapper<>(this, "state", TaskState.READY); public TaskState getState() { - return state; + return state.get(); } void setState(TaskState state) { - this.state = state; + this.state.setValue(state); + } + + public ReadOnlyObjectProperty stateProperty() { + return state.getReadOnlyProperty(); } private Throwable lastException = null; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskExecutor.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskExecutor.java index 7d6f66b12..a9650e7d1 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskExecutor.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskExecutor.java @@ -211,6 +211,7 @@ public final class TaskExecutor { task.onDone().fireEvent(new TaskEvent(this, task, false)); taskListeners.forEach(it -> it.onFinished(task)); } catch (InterruptedException e) { + task.setLastException(e); if (task.getSignificance().shouldLog()) { Logging.LOG.log(Level.FINE, "Task aborted: " + task.getName()); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskListener.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskListener.java index 8c28c2217..89bb84895 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskListener.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskListener.java @@ -38,6 +38,7 @@ public abstract class TaskListener implements EventListener { } public void onFailed(Task task, Throwable throwable) { + onFinished(task); } public void onStop(boolean success, TaskExecutor executor) {