diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorWizardDisplayer.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorWizardDisplayer.java index c6bc30477..b995b1c1e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorWizardDisplayer.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/decorator/DecoratorWizardDisplayer.java @@ -19,16 +19,28 @@ package org.jackhuang.hmcl.ui.decorator; import javafx.scene.Node; import javafx.scene.control.SkinBase; +import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.construct.Navigator; import org.jackhuang.hmcl.ui.construct.PageCloseEvent; import org.jackhuang.hmcl.ui.wizard.*; +import java.util.Map; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; -public class DecoratorWizardDisplayer extends DecoratorTransitionPage implements TaskExecutorDialogWizardDisplayer { +public class DecoratorWizardDisplayer extends DecoratorTransitionPage implements WizardDisplayer { private final WizardController wizardController = new WizardController(this); - private final Queue cancelQueue = new ConcurrentLinkedQueue<>(); + private final WizardDisplayer displayer = new TaskExecutorDialogWizardDisplayer(new ConcurrentLinkedQueue<>()) { + @Override + public void onEnd() { + super.onEnd(); + fireEvent(new PageCloseEvent()); + } + + @Override + public void navigateTo(Node page, Navigation.NavigationDirection nav) { + } + }; private final String category; @@ -45,28 +57,24 @@ public class DecoratorWizardDisplayer extends DecoratorTransitionPage implements addEventHandler(Navigator.NavigationEvent.NAVIGATED, this::onDecoratorPageNavigating); } - @Override - public WizardController getWizardController() { - return wizardController; - } - - @Override - public Queue getCancelQueue() { - return cancelQueue; - } - @Override public void onStart() { + displayer.onStart(); + } + @Override + public void onCancel() { + displayer.onCancel(); } @Override public void onEnd() { - fireEvent(new PageCloseEvent()); + displayer.onEnd(); } @Override public void navigateTo(Node page, Navigation.NavigationDirection nav) { + displayer.navigateTo(page, nav); navigate(page, nav.getAnimation().getAnimationProducer()); String prefix = category == null ? "" : category + " - "; @@ -86,6 +94,11 @@ public class DecoratorWizardDisplayer extends DecoratorTransitionPage implements } } + @Override + public void handleTask(Map settings, Task task) { + displayer.handleTask(settings, task); + } + @Override public boolean isPageCloseable() { return true; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java index e740800be..32864df62 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java @@ -101,7 +101,7 @@ public final class LocalModpackPage extends StackPane implements WizardPage { chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("modpack"), "*.zip")); selectedFile = chooser.showOpenDialog(Controllers.getStage()); if (selectedFile == null) { - Platform.runLater(controller::onEnd); + controller.onEnd(); return; } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/AbstractWizardDisplayer.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/AbstractWizardDisplayer.java index 4a88ce011..c122cb0f1 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/AbstractWizardDisplayer.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/AbstractWizardDisplayer.java @@ -26,32 +26,34 @@ import org.jackhuang.hmcl.ui.construct.TaskListPane; import java.util.Map; import java.util.Queue; -public interface AbstractWizardDisplayer extends WizardDisplayer { - WizardController getWizardController(); +public abstract class AbstractWizardDisplayer implements WizardDisplayer { + private final Queue cancelQueue; - Queue getCancelQueue(); + public AbstractWizardDisplayer(Queue cancelQueue) { + this.cancelQueue = cancelQueue; + } @Override - default void handleTask(Map settings, Task task) { + public void handleTask(Map settings, Task task) { TaskExecutor executor = task.withRunAsync(Schedulers.javafx(), this::navigateToSuccess).executor(); TaskListPane pane = new TaskListPane(); pane.setExecutor(executor); navigateTo(pane, Navigation.NavigationDirection.FINISH); - getCancelQueue().add(executor); + cancelQueue.add(executor); executor.start(); } @Override - default void onCancel() { - while (!getCancelQueue().isEmpty()) { - Object x = getCancelQueue().poll(); + public void onCancel() { + while (!cancelQueue.isEmpty()) { + Object x = cancelQueue.poll(); if (x instanceof TaskExecutor) ((TaskExecutor) x).cancel(); else if (x instanceof Thread) ((Thread) x).interrupt(); else throw new IllegalStateException("Unrecognized cancel queue element: " + x); } } - default void navigateToSuccess() { + void navigateToSuccess() { navigateTo(new Label("Successful"), Navigation.NavigationDirection.FINISH); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/DefaultWizardDisplayer.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/DefaultWizardDisplayer.java deleted file mode 100644 index f24e67876..000000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/DefaultWizardDisplayer.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.ui.wizard; - -import com.jfoenix.controls.JFXButton; -import com.jfoenix.controls.JFXToolbar; -import javafx.fxml.FXML; -import javafx.scene.Node; -import javafx.scene.control.Label; -import javafx.scene.layout.StackPane; -import org.jackhuang.hmcl.ui.FXUtils; -import org.jackhuang.hmcl.ui.animation.TransitionPane; -import org.jackhuang.hmcl.util.StringUtils; - -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; - -public class DefaultWizardDisplayer extends StackPane implements AbstractWizardDisplayer { - - private final String prefix; - private final WizardController wizardController; - private final Queue cancelQueue = new ConcurrentLinkedQueue<>(); - - private Node nowPage; - - @FXML - private TransitionPane root; - @FXML - private JFXButton backButton; - @FXML - private JFXToolbar toolbar; - @FXML - private JFXButton refreshButton; - @FXML - private Label titleLabel; - - public DefaultWizardDisplayer(String prefix, WizardProvider wizardProvider) { - this.prefix = prefix; - - FXUtils.loadFXML(this, "/assets/fxml/wizard.fxml"); - toolbar.setEffect(null); - - wizardController = new WizardController(this); - wizardController.setProvider(wizardProvider); - } - - @Override - public WizardController getWizardController() { - return wizardController; - } - - @Override - public Queue getCancelQueue() { - return cancelQueue; - } - - @Override - public void onStart() { - } - - @Override - public void onEnd() { - } - - @Override - public void onCancel() { - } - - @Override - public void navigateTo(Node page, Navigation.NavigationDirection nav) { - backButton.setDisable(!wizardController.canPrev()); - root.setContent(page, nav.getAnimation().getAnimationProducer()); - String title = StringUtils.isBlank(prefix) ? "" : prefix + " - "; - if (page instanceof WizardPage) - titleLabel.setText(title + ((WizardPage) page).getTitle()); - refreshButton.setVisible(page instanceof Refreshable); - nowPage = page; - } - - @FXML - private void initialize() { - wizardController.onStart(); - } - - @FXML - private void back() { - wizardController.onPrev(true); - } - - @FXML - private void close() { - wizardController.onCancel(); - } - - @FXML - private void refresh() { - ((Refreshable) nowPage).refresh(); - } -} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/TaskExecutorDialogWizardDisplayer.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/TaskExecutorDialogWizardDisplayer.java index 9203ecfeb..91b39d6bd 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/TaskExecutorDialogWizardDisplayer.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/TaskExecutorDialogWizardDisplayer.java @@ -28,14 +28,19 @@ import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane; import org.jackhuang.hmcl.util.StringUtils; import java.util.Map; +import java.util.Queue; import static org.jackhuang.hmcl.ui.FXUtils.runInFX; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; -public interface TaskExecutorDialogWizardDisplayer extends AbstractWizardDisplayer { +public abstract class TaskExecutorDialogWizardDisplayer extends AbstractWizardDisplayer { + + public TaskExecutorDialogWizardDisplayer(Queue cancelQueue) { + super(cancelQueue); + } @Override - default void handleTask(Map settings, Task task) { + public void handleTask(Map settings, Task task) { TaskExecutorDialogPane pane = new TaskExecutorDialogPane(it -> { it.fireEvent(new DialogCloseEvent()); onEnd(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/Wizard.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/Wizard.java deleted file mode 100644 index 7b91bf8be..000000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/Wizard.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.ui.wizard; - -import javafx.scene.Node; - -public final class Wizard { - - public static Node createWizard(WizardProvider provider) { - return createWizard("", provider); - } - - public static Node createWizard(String namespace, WizardProvider provider) { - return new DefaultWizardDisplayer(namespace, provider); - } -} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/WizardController.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/WizardController.java index 702cf4ec6..2d97e4a87 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/WizardController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/WizardController.java @@ -28,6 +28,7 @@ public class WizardController implements Navigation { private WizardProvider provider = null; private final Map settings = new HashMap<>(); private final Stack pages = new Stack<>(); + private boolean stopped = false; public WizardController(WizardDisplayer displayer) { this.displayer = displayer; @@ -60,6 +61,10 @@ public class WizardController implements Navigation { Node page = navigatingTo(0); pages.push(page); + if (stopped) { // navigatingTo may stop this wizard. + return; + } + if (page instanceof WizardPage) ((WizardPage) page).onNavigate(settings); @@ -77,6 +82,10 @@ public class WizardController implements Navigation { public void onNext(Node page) { pages.push(page); + if (stopped) { // navigatingTo may stop this wizard. + return; + } + if (page instanceof WizardPage) ((WizardPage) page).onNavigate(settings); @@ -122,6 +131,7 @@ public class WizardController implements Navigation { @Override public void onEnd() { + stopped = true; settings.clear(); pages.clear(); displayer.onEnd(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/WizardDisplayer.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/WizardDisplayer.java index 95d0414d9..1d1cb5c04 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/WizardDisplayer.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/WizardDisplayer.java @@ -23,9 +23,16 @@ import org.jackhuang.hmcl.task.Task; import java.util.Map; public interface WizardDisplayer { - void onStart(); - void onEnd(); - void onCancel(); + default void onStart() { + } + + default void onEnd() { + } + + default void onCancel() { + } + void navigateTo(Node page, Navigation.NavigationDirection nav); + void handleTask(Map settings, Task task); } diff --git a/HMCL/src/main/resources/assets/fxml/wizard.fxml b/HMCL/src/main/resources/assets/fxml/wizard.fxml deleted file mode 100644 index a7f3dfd34..000000000 --- a/HMCL/src/main/resources/assets/fxml/wizard.fxml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameRemoteVersions.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameRemoteVersions.java index d8796a9b5..6762fb111 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameRemoteVersions.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameRemoteVersions.java @@ -17,8 +17,10 @@ */ package org.jackhuang.hmcl.download.game; +import com.google.gson.JsonParseException; import com.google.gson.annotations.SerializedName; import org.jackhuang.hmcl.util.Immutable; +import org.jackhuang.hmcl.util.gson.Validation; import java.util.Collections; import java.util.List; @@ -28,7 +30,7 @@ import java.util.List; * @author huangyuhui */ @Immutable -public final class GameRemoteVersions { +public final class GameRemoteVersions implements Validation { @SerializedName("versions") private final List versions; @@ -57,4 +59,9 @@ public final class GameRemoteVersions { return versions; } + @Override + public void validate() throws JsonParseException { + if (versions == null) + throw new JsonParseException("GameRemoteVersions.versions cannot be null"); + } }