refactor: WizardDisplayer

This commit is contained in:
huanghongxun 2020-06-26 21:29:47 +08:00
parent bd18355a1a
commit dc03f28d56
10 changed files with 73 additions and 223 deletions

View File

@ -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<Object> 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<Object> 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<String, Object> settings, Task<?> task) {
displayer.handleTask(settings, task);
}
@Override
public boolean isPageCloseable() {
return true;

View File

@ -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;
}

View File

@ -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<Object> cancelQueue;
Queue<Object> getCancelQueue();
public AbstractWizardDisplayer(Queue<Object> cancelQueue) {
this.cancelQueue = cancelQueue;
}
@Override
default void handleTask(Map<String, Object> settings, Task<?> task) {
public void handleTask(Map<String, Object> 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);
}
}

View File

@ -1,114 +0,0 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
*/
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<Object> 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<Object> 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();
}
}

View File

@ -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<Object> cancelQueue) {
super(cancelQueue);
}
@Override
default void handleTask(Map<String, Object> settings, Task<?> task) {
public void handleTask(Map<String, Object> settings, Task<?> task) {
TaskExecutorDialogPane pane = new TaskExecutorDialogPane(it -> {
it.fireEvent(new DialogCloseEvent());
onEnd();

View File

@ -1,31 +0,0 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
*/
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);
}
}

View File

@ -28,6 +28,7 @@ public class WizardController implements Navigation {
private WizardProvider provider = null;
private final Map<String, Object> settings = new HashMap<>();
private final Stack<Node> 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();

View File

@ -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<String, Object> settings, Task<?> task);
}

View File

@ -1,49 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import com.jfoenix.controls.JFXButton?>
<?import com.jfoenix.controls.JFXToolbar?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.*?>
<?import org.jackhuang.hmcl.ui.animation.TransitionPane?>
<fx:root xmlns="http://javafx.com/javafx"
type="StackPane"
style="-fx-background-color: gray;"
xmlns:fx="http://javafx.com/fxml">
<VBox>
<JFXToolbar fx:id="toolbar" styleClass="jfx-tool-bar">
<leftItems>
<JFXButton maxHeight="20" styleClass="toggle-icon3"
StackPane.alignment="CENTER_RIGHT" onMouseClicked="#close">
<graphic>
<fx:include source="/assets/svg/close.fxml"/>
</graphic>
<StackPane.margin>
<Insets left="20"/>
</StackPane.margin>
</JFXButton>
<JFXButton fx:id="backButton" maxHeight="20" styleClass="toggle-icon3"
StackPane.alignment="CENTER_LEFT" onMouseClicked="#back">
<graphic>
<fx:include source="/assets/svg/arrow-left.fxml"/>
</graphic>
</JFXButton>
<Label fx:id="titleLabel" style="-fx-text-fill:WHITE; -fx-font-size: 15;"
StackPane.alignment="CENTER_LEFT"/>
</leftItems>
<rightItems>
<JFXButton fx:id="refreshButton" maxHeight="20" styleClass="toggle-icon3" disable="true"
StackPane.alignment="CENTER_RIGHT" onMouseClicked="#refrseh">
<graphic>
<fx:include source="/assets/svg/refresh.fxml"/>
</graphic>
<StackPane.margin>
<Insets left="20"/>
</StackPane.margin>
</JFXButton>
</rightItems>
</JFXToolbar>
<TransitionPane fx:id="root"/>
</VBox>
</fx:root>

View File

@ -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<GameRemoteVersionInfo> 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");
}
}