From 5e1e2c6047a20d9d0e7e360ca24e883a06b48d65 Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Fri, 2 Mar 2018 00:43:33 +0800 Subject: [PATCH] Remove accountsPage --- .../hmcl/event/AccountLoadingEvent.java | 39 ++++ .../org/jackhuang/hmcl/setting/Accounts.java | 6 + .../org/jackhuang/hmcl/setting/Settings.java | 9 + .../org/jackhuang/hmcl/ui/AccountItem.java | 133 -------------- .../jackhuang/hmcl/ui/AccountLoginPane.java | 2 +- .../org/jackhuang/hmcl/ui/AccountPage.java | 104 +++++++++++ ...{AccountsPage.java => AddAccountPane.java} | 172 ++++++------------ .../jackhuang/hmcl/ui/LeftPaneController.java | 145 +++++++++++---- .../java/org/jackhuang/hmcl/ui/MainPage.java | 64 +++---- .../hmcl/ui/construct/ClassTitle.java | 9 + .../hmcl/ui/construct/ComponentList.java | 5 + .../resources/assets/fxml/account-add.fxml | 64 +++++++ .../resources/assets/fxml/account-login.fxml | 2 +- .../main/resources/assets/fxml/account.fxml | 135 +++++++------- .../assets/fxml/authlib-injector-servers.fxml | 2 +- HMCL/src/main/resources/assets/fxml/main.fxml | 10 - .../resources/assets/lang/I18N.properties | 1 + .../assets/lang/I18N_zh_CN.properties | 1 + 18 files changed, 504 insertions(+), 399 deletions(-) create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/event/AccountLoadingEvent.java delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/AccountItem.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/AccountPage.java rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{AccountsPage.java => AddAccountPane.java} (72%) create mode 100644 HMCL/src/main/resources/assets/fxml/account-add.fxml diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/event/AccountLoadingEvent.java b/HMCL/src/main/java/org/jackhuang/hmcl/event/AccountLoadingEvent.java new file mode 100644 index 000000000..1fc343485 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/event/AccountLoadingEvent.java @@ -0,0 +1,39 @@ +/* + * Hello Minecraft! Launcher. + * Copyright (C) 2018 huangyuhui + * + * 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 {http://www.gnu.org/licenses/}. + */ +package org.jackhuang.hmcl.event; + +/** + * This event gets fired when loading accounts. + *
+ * This event is fired on the {@link org.jackhuang.hmcl.event.EventBus#EVENT_BUS} + * + * @author huangyuhui + */ +public class AccountLoadingEvent extends Event { + + /** + * Constructor. + * + * @param source {@link org.jackhuang.hmcl.setting.Settings} + */ + public AccountLoadingEvent(Object source) { + super(source); + } + +} + diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java index ddab99ef3..b1d6ac5e9 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Accounts.java @@ -30,6 +30,8 @@ import org.jackhuang.hmcl.auth.yggdrasil.MojangYggdrasilProvider; import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount; import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccountFactory; import org.jackhuang.hmcl.task.FileDownloadTask; +import org.jackhuang.hmcl.task.Task; +import org.jackhuang.hmcl.task.TaskResult; import org.jackhuang.hmcl.util.*; import java.io.File; @@ -96,4 +98,8 @@ public final class Accounts { return response.getMeta().getServerName(); } } + + public static TaskResult getAuthlibInjectorServerNameAsync(AuthlibInjectorAccount account) { + return Task.ofResult("serverName", () -> Accounts.getAuthlibInjectorServerName(account.getServerBaseURL())); + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Settings.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Settings.java index fe6498d66..cb00fdd8f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Settings.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Settings.java @@ -28,6 +28,7 @@ import org.jackhuang.hmcl.auth.Account; import org.jackhuang.hmcl.auth.AccountFactory; import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount; import org.jackhuang.hmcl.download.DownloadProvider; +import org.jackhuang.hmcl.event.AccountLoadingEvent; import org.jackhuang.hmcl.event.EventBus; import org.jackhuang.hmcl.event.ProfileChangedEvent; import org.jackhuang.hmcl.event.ProfileLoadingEvent; @@ -368,6 +369,7 @@ public class Settings { public void addAccount(Account account) { accounts.put(Accounts.getAccountId(account), account); + onAccountLoading(); } public Account getAccount(String name, String character) { @@ -381,12 +383,14 @@ public class Settings { public void deleteAccount(String name, String character) { accounts.remove(Accounts.getAccountId(name, character)); + onAccountLoading(); selectedAccount.get(); } public void deleteAccount(Account account) { accounts.remove(Accounts.getAccountId(account)); + onAccountLoading(); selectedAccount.get(); } @@ -506,6 +510,7 @@ public class Settings { throw new IllegalArgumentException("Profile's name is empty"); getProfileMap().put(ver.getName(), ver); + Schedulers.computation().schedule(this::onProfileLoading); ver.nameProperty().setChangedListener(this::profileNameChanged); @@ -546,4 +551,8 @@ public class Settings { EventBus.EVENT_BUS.fireEvent(new ProfileLoadingEvent(SETTINGS)); onProfileChanged(); } + + public void onAccountLoading() { + EventBus.EVENT_BUS.fireEvent(new AccountLoadingEvent(SETTINGS)); + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AccountItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AccountItem.java deleted file mode 100644 index 57c0db788..000000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AccountItem.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Hello Minecraft! Launcher. - * Copyright (C) 2018 huangyuhui - * - * 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 {http://www.gnu.org/licenses/}. - */ -package org.jackhuang.hmcl.ui; - -import com.jfoenix.controls.JFXButton; -import com.jfoenix.controls.JFXProgressBar; -import com.jfoenix.controls.JFXRadioButton; -import javafx.beans.binding.Bindings; -import javafx.event.EventHandler; -import javafx.fxml.FXML; -import javafx.scene.control.Label; -import javafx.scene.control.ToggleGroup; -import javafx.scene.effect.BlurType; -import javafx.scene.effect.DropShadow; -import javafx.scene.image.ImageView; -import javafx.scene.input.MouseEvent; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Pane; -import javafx.scene.layout.StackPane; -import javafx.scene.layout.VBox; -import javafx.scene.paint.Color; -import org.jackhuang.hmcl.auth.Account; -import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount; -import org.jackhuang.hmcl.auth.offline.OfflineAccount; -import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount; -import org.jackhuang.hmcl.game.AccountHelper; -import org.jackhuang.hmcl.setting.Accounts; -import org.jackhuang.hmcl.setting.Settings; -import org.jackhuang.hmcl.setting.Theme; -import org.jackhuang.hmcl.task.Schedulers; -import org.jackhuang.hmcl.task.Task; - -public final class AccountItem extends StackPane { - - private final Account account; - - @FXML - private Pane icon; - @FXML private VBox content; - @FXML private StackPane header; - @FXML private StackPane body; - @FXML private JFXButton btnDelete; - @FXML private JFXButton btnRefresh; - @FXML private Label lblUser; - @FXML private Label lblType; - @FXML private Label lblEmail; - @FXML private Label lblServer; - @FXML private Label lblCurrentAccount; - @FXML private JFXRadioButton chkSelected; - @FXML private JFXProgressBar pgsSkin; - @FXML private ImageView portraitView; - @FXML private HBox buttonPane; - - public AccountItem(int i, Account account, ToggleGroup toggleGroup) { - this.account = account; - - FXUtils.loadFXML(this, "/assets/fxml/account-item.fxml"); - - setEffect(new DropShadow(BlurType.GAUSSIAN, Color.rgb(0, 0, 0, 0.26), 5.0, 0.12, -0.5, 1.0)); - - chkSelected.setToggleGroup(toggleGroup); - btnDelete.setGraphic(SVG.delete(Theme.blackFillBinding(), 15, 15)); - btnRefresh.setGraphic(SVG.refresh(Theme.blackFillBinding(), 15, 15)); - - // create image view - icon.translateYProperty().bind(Bindings.createDoubleBinding(() -> header.getBoundsInParent().getHeight() - icon.getHeight() / 2 - 16, header.boundsInParentProperty(), icon.heightProperty())); - - chkSelected.getProperties().put("account", account); - setSelected(Settings.INSTANCE.getSelectedAccount() == account); - - lblUser.setText(account.getCharacter()); - lblType.setText(AccountsPage.accountType(account)); - lblEmail.setText(account.getUsername()); - - if (account instanceof AuthlibInjectorAccount) { - Task.ofResult("serverName", () -> Accounts.getAuthlibInjectorServerName(((AuthlibInjectorAccount) account).getServerBaseURL())) - .subscribe(Schedulers.javafx(), variables -> lblServer.setText(variables.get("serverName"))); - } - - if (account instanceof YggdrasilAccount) { - btnRefresh.setOnMouseClicked(e -> { - pgsSkin.setVisible(true); - AccountHelper.refreshSkinAsync((YggdrasilAccount) account) - .subscribe(Schedulers.javafx(), this::loadSkin); - }); - AccountHelper.loadSkinAsync((YggdrasilAccount) account) - .subscribe(Schedulers.javafx(), this::loadSkin); - } else - loadSkin(); - - if (account instanceof OfflineAccount) { // Offline Account cannot be refreshed, - buttonPane.getChildren().remove(btnRefresh); - } - } - - private void loadSkin() { - pgsSkin.setVisible(false); - portraitView.setViewport(AccountHelper.getViewport(4)); - if (account instanceof YggdrasilAccount) - portraitView.setImage(AccountHelper.getSkin((YggdrasilAccount) account, 4)); - else - portraitView.setImage(AccountHelper.getDefaultSkin(account, 4)); - FXUtils.limitSize(portraitView, 32, 32); - } - - public Account getAccount() { - return account; - } - - public void setSelected(boolean selected) { - lblCurrentAccount.setVisible(selected); - chkSelected.setSelected(selected); - } - - public void setOnDeleteButtonMouseClicked(EventHandler eventHandler) { - btnDelete.setOnMouseClicked(eventHandler); - } -} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AccountLoginPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AccountLoginPane.java index 955b1b750..4d66c6935 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AccountLoginPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AccountLoginPane.java @@ -73,7 +73,7 @@ public class AccountLoginPane extends StackPane { } else if (account instanceof NoSelectedCharacterException) { dialog.close(); } else if (account instanceof Exception) { - lblCreationWarning.setText(AccountsPage.accountException((Exception) account)); + lblCreationWarning.setText(AddAccountPane.accountException((Exception) account)); } progressBar.setVisible(false); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AccountPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AccountPage.java new file mode 100644 index 000000000..1028b5ea2 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AccountPage.java @@ -0,0 +1,104 @@ +/* + * Hello Minecraft! Launcher. + * Copyright (C) 2017 huangyuhui + * + * 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 {http://www.gnu.org/licenses/}. + */ +package org.jackhuang.hmcl.ui; + +import com.jfoenix.controls.JFXButton; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.StackPane; +import org.jackhuang.hmcl.Launcher; +import org.jackhuang.hmcl.auth.Account; +import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount; +import org.jackhuang.hmcl.auth.offline.OfflineAccount; +import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount; +import org.jackhuang.hmcl.game.AccountHelper; +import org.jackhuang.hmcl.setting.Accounts; +import org.jackhuang.hmcl.setting.Settings; +import org.jackhuang.hmcl.task.Schedulers; +import org.jackhuang.hmcl.task.Task; +import org.jackhuang.hmcl.ui.construct.ComponentList; +import org.jackhuang.hmcl.ui.wizard.DecoratorPage; + +public class AccountPage extends StackPane implements DecoratorPage { + private final StringProperty title; + + private final Account account; + + @FXML + private Label lblType; + @FXML + private Label lblServer; + @FXML + private Label lblCharacter; + @FXML + private Label lblEmail; + @FXML + private BorderPane paneServer; + @FXML + private ComponentList componentList; + @FXML + private JFXButton btnRefresh; + + public AccountPage(Account account) { + this.account = account; + title = new SimpleStringProperty(this, "title", Launcher.i18n("account") + " - " + account.getCharacter()); + + FXUtils.loadFXML(this, "/assets/fxml/account.fxml"); + + if (account instanceof AuthlibInjectorAccount) { + Task.ofResult("serverName", () -> Accounts.getAuthlibInjectorServerName(((AuthlibInjectorAccount) account).getServerBaseURL())) + .subscribe(Schedulers.javafx(), variables -> lblServer.setText(variables.get("serverName"))); + } else { + componentList.removeChildren(paneServer); + } + + lblCharacter.setText(account.getCharacter()); + lblType.setText(AddAccountPane.accountType(account)); + lblEmail.setText(account.getUsername()); + + btnRefresh.setDisable(account instanceof OfflineAccount); + + if (account instanceof YggdrasilAccount) { + btnRefresh.setOnMouseClicked(e -> { + AccountHelper.refreshSkinAsync((YggdrasilAccount) account).start(); + }); + } + } + + @FXML + private void onDelete() { + Settings.INSTANCE.deleteAccount(account); + Controllers.navigate(null); + } + + public String getTitle() { + return title.get(); + } + + @Override + public StringProperty titleProperty() { + return title; + } + + public void setTitle(String title) { + this.title.set(title); + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AccountsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AddAccountPane.java similarity index 72% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/AccountsPage.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/AddAccountPane.java index 7fc62cfcd..643ad47da 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AccountsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AddAccountPane.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher. - * Copyright (C) 2018 huangyuhui + * Copyright (C) 2017 huangyuhui * * 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 @@ -19,16 +19,11 @@ package org.jackhuang.hmcl.ui; import com.jfoenix.concurrency.JFXUtilities; import com.jfoenix.controls.*; -import javafx.application.Platform; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; import javafx.fxml.FXML; import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.Hyperlink; import javafx.scene.control.Label; -import javafx.scene.control.ScrollPane; -import javafx.scene.control.ToggleGroup; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.BorderPane; @@ -48,11 +43,9 @@ import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.construct.AdvancedListBox; import org.jackhuang.hmcl.ui.construct.IconedItem; import org.jackhuang.hmcl.ui.construct.Validator; -import org.jackhuang.hmcl.ui.wizard.DecoratorPage; import org.jackhuang.hmcl.util.Logging; import java.util.Collection; -import java.util.LinkedList; import java.util.List; import java.util.Optional; import java.util.concurrent.CountDownLatch; @@ -60,13 +53,8 @@ import java.util.logging.Level; import java.util.stream.Collectors; import java.util.stream.Stream; -public final class AccountsPage extends StackPane implements DecoratorPage { - private final StringProperty title = new SimpleStringProperty(this, "title", Launcher.i18n("account")); +public class AddAccountPane extends StackPane { - @FXML - private ScrollPane scrollPane; - @FXML private JFXMasonryPane masonryPane; - @FXML private JFXDialog dialog; @FXML private JFXTextField txtUsername; @FXML private JFXPasswordField txtPassword; @FXML private Label lblCreationWarning; @@ -76,14 +64,15 @@ public final class AccountsPage extends StackPane implements DecoratorPage { @FXML private JFXProgressBar progressBar; @FXML private Label lblAddInjectorServer; @FXML private Hyperlink linkAddInjectorServer; + @FXML private JFXDialogLayout layout; + private final Runnable finalization; - { - FXUtils.loadFXML(this, "/assets/fxml/account.fxml"); + public AddAccountPane(Runnable finalization) { + this.finalization = finalization; - getChildren().remove(dialog); - dialog.setDialogContainer(this); + FXUtils.loadFXML(this, "/assets/fxml/account-add.fxml"); - FXUtils.smoothScrolling(scrollPane); + loadServers(); cboType.getItems().setAll(Launcher.i18n("account.methods.offline"), Launcher.i18n("account.methods.yggdrasil"), Launcher.i18n("account.methods.authlib_injector")); cboType.getSelectionModel().selectedIndexProperty().addListener((a, b, newValue) -> { @@ -103,38 +92,9 @@ public final class AccountsPage extends StackPane implements DecoratorPage { txtUsername.setOnAction(e -> onCreationAccept()); txtUsername.getValidators().add(new Validator(Launcher.i18n("input.email"), str -> !txtPassword.isVisible() || str.contains("@"))); - FXUtils.onChangeAndOperate(Settings.INSTANCE.selectedAccountProperty(), account -> { - for (Node node : masonryPane.getChildren()) - if (node instanceof AccountItem) - ((AccountItem) node).setSelected(account == ((AccountItem) node).getAccount()); - }); - - loadAccounts(); - loadServers(); - - if (Settings.INSTANCE.getAccounts().isEmpty()) - addNewAccount(); } - public void loadAccounts() { - List children = new LinkedList<>(); - int i = 0; - ToggleGroup group = new ToggleGroup(); - for (Account account : Settings.INSTANCE.getAccounts()) { - children.add(buildNode(++i, account, group)); - } - group.selectedToggleProperty().addListener((a, b, newValue) -> { - if (newValue != null) - Settings.INSTANCE.setSelectedAccount((Account) newValue.getProperties().get("account")); - }); - FXUtils.resetChildren(masonryPane, children); - Platform.runLater(() -> { - masonryPane.requestLayout(); - scrollPane.requestLayout(); - }); - } - - public void loadServers() { + private void loadServers() { Task.ofResult("list", () -> Settings.INSTANCE.getAuthlibInjectorServerURLs().parallelStream() .flatMap(serverURL -> { try { @@ -152,28 +112,6 @@ public final class AccountsPage extends StackPane implements DecoratorPage { })); } - private Node buildNode(int i, Account account, ToggleGroup group) { - AccountItem item = new AccountItem(i, account, group); - item.setOnDeleteButtonMouseClicked(e -> { - Settings.INSTANCE.deleteAccount(account); - Platform.runLater(this::loadAccounts); - }); - return item; - } - - @FXML - private void addNewAccount() { - txtUsername.setText(""); - txtPassword.setText(""); - lblCreationWarning.setText(""); - dialog.show(); - } - - @FXML - private void onAddInjecterServer() { - Controllers.navigate(Controllers.getServersPage()); - } - @FXML private void onCreationAccept() { int type = cboType.getSelectionModel().getSelectedIndex(); @@ -183,23 +121,22 @@ public final class AccountsPage extends StackPane implements DecoratorPage { progressBar.setVisible(true); lblCreationWarning.setText(""); Task.ofResult("create_account", () -> { - AccountFactory factory; - switch (type) { - case 0: factory = Accounts.ACCOUNT_FACTORY.get(Accounts.OFFLINE_ACCOUNT_KEY); break; - case 1: factory = Accounts.ACCOUNT_FACTORY.get(Accounts.YGGDRASIL_ACCOUNT_KEY); break; - case 2: factory = Accounts.ACCOUNT_FACTORY.get(Accounts.AUTHLIB_INJECTOR_ACCOUNT_KEY); break; - default: throw new Error(); - } + AccountFactory factory; + switch (type) { + case 0: factory = Accounts.ACCOUNT_FACTORY.get(Accounts.OFFLINE_ACCOUNT_KEY); break; + case 1: factory = Accounts.ACCOUNT_FACTORY.get(Accounts.YGGDRASIL_ACCOUNT_KEY); break; + case 2: factory = Accounts.ACCOUNT_FACTORY.get(Accounts.AUTHLIB_INJECTOR_ACCOUNT_KEY); break; + default: throw new Error(); + } - return factory.create(new Selector(), username, password, apiRoot, Settings.INSTANCE.getProxy()); + return factory.create(new Selector(), username, password, apiRoot, Settings.INSTANCE.getProxy()); }).finalized(Schedulers.javafx(), variables -> { Settings.INSTANCE.addAccount(variables.get("create_account")); - dialog.close(); - loadAccounts(); progressBar.setVisible(false); + finalization.run(); }, exception -> { if (exception instanceof NoSelectedCharacterException) { - dialog.close(); + finalization.run(); } else { lblCreationWarning.setText(accountException(exception)); } @@ -209,46 +146,24 @@ public final class AccountsPage extends StackPane implements DecoratorPage { @FXML private void onCreationCancel() { - dialog.close(); + finalization.run(); } - public String getTitle() { - return title.get(); + @FXML + private void onAddInjecterServer() { + finalization.run(); + Controllers.navigate(Controllers.getServersPage()); } - @Override - public StringProperty titleProperty() { - return title; + private void showSelector(Node node) { + getChildren().setAll(node); } - public void setTitle(String title) { - this.title.set(title); + private void closeSelector() { + getChildren().setAll(layout); } - public static String accountException(Exception exception) { - if (exception instanceof InvalidCredentialsException) { - return Launcher.i18n("account.failed.invalid_credentials"); - } else if (exception instanceof NoCharacterException) { - return Launcher.i18n("account.failed.no_charactor"); - } else if (exception instanceof ServerDisconnectException) { - return Launcher.i18n("account.failed.connect_authentication_server"); - } else if (exception instanceof InvalidTokenException) { - return Launcher.i18n("account.failed.invalid_token"); - } else if (exception instanceof InvalidPasswordException) { - return Launcher.i18n("account.failed.invalid_password"); - } else { - return exception.getClass() + ": " + exception.getLocalizedMessage(); - } - } - - public static String accountType(Account account) { - if (account instanceof OfflineAccount) return Launcher.i18n("account.methods.offline"); - else if (account instanceof AuthlibInjectorAccount) return Launcher.i18n("account.methods.authlib_injector"); - else if (account instanceof YggdrasilAccount) return Launcher.i18n("account.methods.yggdrasil"); - else throw new Error(Launcher.i18n("account.methods.no_method") + ": " + account); - } - - private static class Selector extends BorderPane implements CharacterSelector { + private class Selector extends BorderPane implements CharacterSelector { private final AdvancedListBox listBox = new AdvancedListBox(); private final JFXButton cancel = new JFXButton(); @@ -303,20 +218,43 @@ public final class AccountsPage extends StackPane implements DecoratorPage { listBox.add(accountItem); } - JFXUtilities.runInFX(() -> Controllers.dialog(this)); + JFXUtilities.runInFX(() -> showSelector(this)); try { latch.await(); - JFXUtilities.runInFX(Controllers::closeDialog); - if (selectedProfile == null) throw new NoSelectedCharacterException(account); + JFXUtilities.runInFX(AddAccountPane.this::closeSelector); + return selectedProfile; } catch (InterruptedException ignore) { throw new NoSelectedCharacterException(account); } } } + + public static String accountException(Exception exception) { + if (exception instanceof InvalidCredentialsException) { + return Launcher.i18n("account.failed.invalid_credentials"); + } else if (exception instanceof NoCharacterException) { + return Launcher.i18n("account.failed.no_charactor"); + } else if (exception instanceof ServerDisconnectException) { + return Launcher.i18n("account.failed.connect_authentication_server"); + } else if (exception instanceof InvalidTokenException) { + return Launcher.i18n("account.failed.invalid_token"); + } else if (exception instanceof InvalidPasswordException) { + return Launcher.i18n("account.failed.invalid_password"); + } else { + return exception.getClass() + ": " + exception.getLocalizedMessage(); + } + } + + public static String accountType(Account account) { + if (account instanceof OfflineAccount) return Launcher.i18n("account.methods.offline"); + else if (account instanceof AuthlibInjectorAccount) return Launcher.i18n("account.methods.authlib_injector"); + else if (account instanceof YggdrasilAccount) return Launcher.i18n("account.methods.yggdrasil"); + else throw new Error(Launcher.i18n("account.methods.no_method") + ": " + account); + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java index 9b28546e3..211bb34d4 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java @@ -19,27 +19,26 @@ package org.jackhuang.hmcl.ui; import com.jfoenix.concurrency.JFXUtilities; import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXListView; +import com.jfoenix.controls.JFXPopup; import javafx.application.Platform; import javafx.scene.Node; +import javafx.scene.control.Tooltip; import javafx.scene.image.Image; -import javafx.scene.layout.BorderPane; +import javafx.scene.input.MouseButton; import javafx.scene.layout.VBox; -import javafx.scene.text.Text; import org.jackhuang.hmcl.Launcher; +import org.jackhuang.hmcl.auth.Account; +import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount; +import org.jackhuang.hmcl.auth.offline.OfflineAccount; import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount; -import org.jackhuang.hmcl.event.EventBus; -import org.jackhuang.hmcl.event.ProfileChangedEvent; -import org.jackhuang.hmcl.event.ProfileLoadingEvent; -import org.jackhuang.hmcl.event.RefreshedVersionsEvent; +import org.jackhuang.hmcl.event.*; import org.jackhuang.hmcl.game.AccountHelper; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.game.ModpackHelper; import org.jackhuang.hmcl.mod.Modpack; import org.jackhuang.hmcl.mod.UnsupportedModpackException; -import org.jackhuang.hmcl.setting.Profile; -import org.jackhuang.hmcl.setting.Profiles; -import org.jackhuang.hmcl.setting.Settings; -import org.jackhuang.hmcl.setting.Theme; +import org.jackhuang.hmcl.setting.*; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.construct.AdvancedListBox; @@ -55,50 +54,53 @@ import java.util.Objects; public final class LeftPaneController { private final AdvancedListBox leftPane; private final VBox profilePane = new VBox(); - private final VersionListItem accountItem = new VersionListItem("", ""); + private final VBox accountPane = new VBox(); + private final VersionListItem missingAccountItem = new VersionListItem(Launcher.i18n("account.missing"), Launcher.i18n("message.unknown")); public LeftPaneController(AdvancedListBox leftPane) { this.leftPane = leftPane; - leftPane.startCategory(Launcher.i18n("account").toUpperCase()) - .add(Lang.apply(new RipplerContainer(accountItem), rippler -> { - rippler.setOnMouseClicked(e -> Controllers.navigate(new AccountsPage())); - accountItem.setOnSettingsButtonClicked(() -> Controllers.navigate(new AccountsPage())); - })) + leftPane + .add(new ClassTitle(Launcher.i18n("account").toUpperCase(), Lang.apply(new JFXButton(), button -> { + button.setGraphic(SVG.plus(Theme.blackFillBinding(), 10, 10)); + button.getStyleClass().add("toggle-icon-tiny"); + button.setOnMouseClicked(e -> addNewAccount()); + }))) + .add(accountPane) .startCategory(Launcher.i18n("launcher").toUpperCase()) .add(Lang.apply(new IconedItem(SVG.gear(Theme.blackFillBinding(), 20, 20), Launcher.i18n("settings.launcher")), iconedItem -> { iconedItem.prefWidthProperty().bind(leftPane.widthProperty()); iconedItem.setOnMouseClicked(e -> Controllers.navigate(Controllers.getSettingsPage())); })) - .add(new ClassTitle(Lang.apply(new BorderPane(), borderPane -> { - borderPane.setLeft(Lang.apply(new VBox(), vBox -> vBox.getChildren().setAll(new Text(Launcher.i18n("profile.title").toUpperCase())))); - JFXButton addProfileButton = new JFXButton(); - addProfileButton.setGraphic(SVG.plus(Theme.blackFillBinding(), 10, 10)); - addProfileButton.getStyleClass().add("toggle-icon-tiny"); - addProfileButton.setOnMouseClicked(e -> + .add(new ClassTitle(Launcher.i18n("profile.title").toUpperCase(), Lang.apply(new JFXButton(), button -> { + button.setGraphic(SVG.plus(Theme.blackFillBinding(), 10, 10)); + button.getStyleClass().add("toggle-icon-tiny"); + button.setOnMouseClicked(e -> Controllers.getDecorator().showPage(new ProfilePage(null))); - borderPane.setRight(addProfileButton); }))) .add(profilePane); + EventBus.EVENT_BUS.channel(AccountLoadingEvent.class).register(this::onAccountsLoading); EventBus.EVENT_BUS.channel(ProfileLoadingEvent.class).register(this::onProfilesLoading); EventBus.EVENT_BUS.channel(ProfileChangedEvent.class).register(this::onProfileChanged); EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).register(this::onRefreshedVersions); - FXUtils.onChangeAndOperate(Settings.INSTANCE.selectedAccountProperty(), it -> { - if (it == null) { - accountItem.setVersionName(Launcher.i18n("account.missing")); - accountItem.setGameVersion(Launcher.i18n("message.unknown")); - } else { - accountItem.setVersionName(it.getCharacter()); - accountItem.setGameVersion(AccountsPage.accountType(it)); - } + FXUtils.onChangeAndOperate(Settings.INSTANCE.selectedAccountProperty(), this::onSelectedAccountChanged); + onAccountsLoading(); + } - if (it instanceof YggdrasilAccount) { - Image image = AccountHelper.getSkin((YggdrasilAccount) it, 4); - accountItem.setImage(image, AccountHelper.getViewport(4)); - } else - accountItem.setImage(AccountHelper.getDefaultSkin(it, 4), AccountHelper.getViewport(4)); + private void addNewAccount() { + Controllers.dialog(new AddAccountPane(Controllers::closeDialog)); + } + + private void onSelectedAccountChanged(Account newAccount) { + Platform.runLater(() -> { + for (Node node : accountPane.getChildren()) { + if (node instanceof RipplerContainer && node.getProperties().get("account") instanceof Account) { + boolean current = Objects.equals(node.getProperties().get("account"), newAccount); + ((RipplerContainer) node).setSelected(current); + } + } }); } @@ -130,6 +132,73 @@ public final class LeftPaneController { Platform.runLater(() -> profilePane.getChildren().setAll(list)); } + private static String accountType(Account account) { + if (account instanceof OfflineAccount) return Launcher.i18n("account.methods.offline"); + else if (account instanceof YggdrasilAccount) return account.getUsername(); + else throw new Error(Launcher.i18n("account.methods.no_method") + ": " + account); + } + + private void onAccountsLoading() { + LinkedList list = new LinkedList<>(); + Account selectedAccount = Settings.INSTANCE.getSelectedAccount(); + for (Account account : Settings.INSTANCE.getAccounts()) { + VersionListItem item = new VersionListItem(account.getCharacter(), accountType(account)); + RipplerContainer ripplerContainer = new RipplerContainer(item); + item.setOnSettingsButtonClicked(() -> Controllers.getDecorator().showPage(new AccountPage(account))); + ripplerContainer.setOnMouseClicked(e -> { + if (e.getButton() == MouseButton.PRIMARY) + Settings.INSTANCE.setSelectedAccount(account); + else if (e.getButton() == MouseButton.SECONDARY) { + JFXListView listView = new JFXListView<>(); + JFXPopup popup = new JFXPopup(listView); + listView.getStyleClass().add("option-list-view"); + listView.getItems().add(Launcher.i18n("button.delete")); + if (account instanceof YggdrasilAccount) + listView.getItems().add(Launcher.i18n("button.refresh")); + listView.setOnMouseClicked(e2 ->{ + popup.hide(); + switch (listView.getSelectionModel().getSelectedIndex()) { + case 0: + Settings.INSTANCE.deleteAccount(account); + break; + case 1: + if (account instanceof YggdrasilAccount) + AccountHelper.refreshSkinAsync((YggdrasilAccount) account).start(); + break; + default: + throw new Error(); + }}); + popup.show(item, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.LEFT, e.getX(), e.getY()); + } + }); + ripplerContainer.getProperties().put("account", account); + ripplerContainer.maxWidthProperty().bind(leftPane.widthProperty()); + + if (account instanceof YggdrasilAccount) { + Image image = AccountHelper.getSkin((YggdrasilAccount) account, 4); + item.setImage(image, AccountHelper.getViewport(4)); + } else + item.setImage(AccountHelper.getDefaultSkin(account, 4), AccountHelper.getViewport(4)); + + if (account instanceof AuthlibInjectorAccount) + Accounts.getAuthlibInjectorServerNameAsync((AuthlibInjectorAccount) account) + .subscribe(Schedulers.javafx(), variables -> FXUtils.installTooltip(ripplerContainer, 500, 5000, 0, new Tooltip(variables.get("serverName")))); + + if (selectedAccount == account) + ripplerContainer.setSelected(true); + + list.add(ripplerContainer); + } + + if (Settings.INSTANCE.getAccounts().isEmpty()) { + RipplerContainer container = new RipplerContainer(missingAccountItem); + missingAccountItem.setOnSettingsButtonClicked(this::addNewAccount); + list.add(container); + } + + Platform.runLater(() -> accountPane.getChildren().setAll(list)); + } + private boolean checkedModpack = false; private void onRefreshedVersions(RefreshedVersionsEvent event) { @@ -162,8 +231,8 @@ public final class LeftPaneController { }); } - private void checkAccount() { + public void checkAccount() { if (Settings.INSTANCE.getAccounts().isEmpty()) - Controllers.navigate(new AccountsPage()); + addNewAccount(); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/MainPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/MainPage.java index 725a635ec..e6977c24d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/MainPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/MainPage.java @@ -44,6 +44,7 @@ import org.jackhuang.hmcl.ui.construct.MessageBox; import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane; import org.jackhuang.hmcl.ui.download.DownloadWizardProvider; import org.jackhuang.hmcl.ui.wizard.DecoratorPage; +import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.OperatingSystem; import org.jackhuang.hmcl.util.StringUtils; @@ -58,8 +59,6 @@ public final class MainPage extends StackPane implements DecoratorPage { private final StringProperty title = new SimpleStringProperty(this, "title", Launcher.i18n("main_page")); private Profile profile; - private String rightClickedVersion; - private HMCLGameRepository rightClickedRepository; @FXML private JFXButton btnRefresh; @@ -71,10 +70,6 @@ public final class MainPage extends StackPane implements DecoratorPage { private JFXSpinner spinner; @FXML private JFXMasonryPane masonryPane; - @FXML - private JFXListView versionList; - - private final JFXPopup versionPopup; { FXUtils.loadFXML(this, "/assets/fxml/main.fxml"); @@ -93,9 +88,6 @@ public final class MainPage extends StackPane implements DecoratorPage { this.profile = event.getProfile(); }); - versionPopup = new JFXPopup(versionList); - getChildren().remove(versionList); - btnAdd.setOnMouseClicked(e -> Controllers.getDecorator().startWizard(new DownloadWizardProvider(), Launcher.i18n("install"))); FXUtils.installTooltip(btnAdd, Launcher.i18n("install")); btnRefresh.setOnMouseClicked(e -> Settings.INSTANCE.getSelectedProfile().getRepository().refreshVersionsAsync().start()); @@ -126,7 +118,7 @@ public final class MainPage extends StackPane implements DecoratorPage { item.setLibraries(libraries.toString()); item.setOnLaunchButtonClicked(e -> { if (Settings.INSTANCE.getSelectedAccount() == null) - Controllers.dialog(Launcher.i18n("login.empty_username")); + Controllers.getLeftPaneController().checkAccount(); else LauncherHelper.INSTANCE.launch(profile, Settings.INSTANCE.getSelectedAccount(), id, null); }); @@ -174,9 +166,34 @@ public final class MainPage extends StackPane implements DecoratorPage { }); item.setOnMouseClicked(event -> { if (event.getButton() == MouseButton.SECONDARY) { - rightClickedVersion = id; - rightClickedRepository = repository; - versionList.getSelectionModel().select(-1); + JFXListView versionList = new JFXListView<>(); + JFXPopup versionPopup = new JFXPopup(versionList); + versionList.getStyleClass().add("option-list-view"); + FXUtils.setLimitWidth(versionList, 150); + versionList.getItems().setAll(Lang.immutableListOf( + Launcher.i18n("version.manage.rename"), + Launcher.i18n("version.manage.remove"), + Launcher.i18n("modpack.export"), + Launcher.i18n("folder.game") + )); + versionList.setOnMouseClicked(e ->{ + versionPopup.hide(); + switch (versionList.getSelectionModel().getSelectedIndex()) { + case 0: + VersionPage.renameVersion(profile, id); + break; + case 1: + VersionPage.deleteVersion(profile, id); + break; + case 2: + VersionPage.exportVersion(profile, id); + break; + case 3: + FXUtils.openFolder(repository.getRunDirectory(id)); + break; + default: + throw new Error(); + }}); versionPopup.show(item, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.LEFT, event.getX(), event.getY()); } else if (event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2) { if (Settings.INSTANCE.getSelectedAccount() == null) @@ -209,27 +226,6 @@ public final class MainPage extends StackPane implements DecoratorPage { }); } - @FXML - private void onVersionManagement() { - versionPopup.hide(); - switch (versionList.getSelectionModel().getSelectedIndex()) { - case 0: - VersionPage.renameVersion(rightClickedRepository.getProfile(), rightClickedVersion); - break; - case 1: - VersionPage.deleteVersion(rightClickedRepository.getProfile(), rightClickedVersion); - break; - case 2: - VersionPage.exportVersion(rightClickedRepository.getProfile(), rightClickedVersion); - break; - case 3: - FXUtils.openFolder(rightClickedRepository.getRunDirectory(rightClickedVersion)); - break; - default: - throw new Error(); - } - } - public String getTitle() { return title.get(); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ClassTitle.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ClassTitle.java index 90abf3c26..88c168d43 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ClassTitle.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ClassTitle.java @@ -18,11 +18,13 @@ package org.jackhuang.hmcl.ui.construct; import javafx.scene.Node; +import javafx.scene.layout.BorderPane; import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import javafx.scene.shape.Rectangle; import javafx.scene.text.Text; +import org.jackhuang.hmcl.util.Lang; /** * @author huangyuhui @@ -48,6 +50,13 @@ public class ClassTitle extends StackPane { getStyleClass().add("class-title"); } + public ClassTitle(String text, Node rightNode) { + this(Lang.apply(new BorderPane(), borderPane -> { + borderPane.setLeft(Lang.apply(new VBox(), vBox -> vBox.getChildren().setAll(new Text(text)))); + borderPane.setRight(rightNode); + })); + } + public Node getContent() { return content; } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentList.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentList.java index b79bfb431..6a3439202 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentList.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentList.java @@ -60,9 +60,14 @@ public class ComponentList extends StackPane { child.getStyleClass().add("options-list-item-ahead"); else child.getStyleClass().add("options-list-item"); + child.getProperties().put("node", node); vbox.getChildren().add(child); } + public void removeChildren(Node node) { + vbox.getChildren().removeIf(node1 -> node1.getProperties().get("node") == node); + } + public String getTitle() { return title.get(); } diff --git a/HMCL/src/main/resources/assets/fxml/account-add.fxml b/HMCL/src/main/resources/assets/fxml/account-add.fxml new file mode 100644 index 000000000..f6ff6f095 --- /dev/null +++ b/HMCL/src/main/resources/assets/fxml/account-add.fxml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HMCL/src/main/resources/assets/fxml/account-login.fxml b/HMCL/src/main/resources/assets/fxml/account-login.fxml index 9402eae99..97065068e 100644 --- a/HMCL/src/main/resources/assets/fxml/account-login.fxml +++ b/HMCL/src/main/resources/assets/fxml/account-login.fxml @@ -26,7 +26,7 @@ diff --git a/HMCL/src/main/resources/assets/fxml/account.fxml b/HMCL/src/main/resources/assets/fxml/account.fxml index 75fc2add3..c79517b9e 100644 --- a/HMCL/src/main/resources/assets/fxml/account.fxml +++ b/HMCL/src/main/resources/assets/fxml/account.fxml @@ -4,72 +4,79 @@ - + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + diff --git a/HMCL/src/main/resources/assets/fxml/authlib-injector-servers.fxml b/HMCL/src/main/resources/assets/fxml/authlib-injector-servers.fxml index c3e330f1b..e37c03188 100644 --- a/HMCL/src/main/resources/assets/fxml/authlib-injector-servers.fxml +++ b/HMCL/src/main/resources/assets/fxml/authlib-injector-servers.fxml @@ -32,7 +32,7 @@