From 08564e5ea260de732b510aba5ed66fc7a45d0f9e Mon Sep 17 00:00:00 2001 From: huangyuhui Date: Thu, 30 Aug 2018 22:14:30 +0800 Subject: [PATCH] Add account list view --- .../jackhuang/hmcl/ui/AdvancedListItem2.java | 4 + .../hmcl/ui/AdvancedListItemViewModel.java | 5 + .../org/jackhuang/hmcl/ui/Controllers.java | 12 ++ .../jackhuang/hmcl/ui/LeftPaneController.java | 120 ++------------ .../main/java/org/jackhuang/hmcl/ui/SVG.java | 9 +- .../AccountAdvancedListItemViewModel.java | 100 +++++++++++ .../hmcl/ui/account/AccountListItemView.java | 88 ++++++++++ .../ui/account/AccountListItemViewModel.java | 116 +++++++++++++ .../hmcl/ui/account/AccountListView.java | 81 +++++++++ .../hmcl/ui/account/AccountListViewModel.java | 73 +++++++++ .../hmcl/ui/account/AccountPage.java | 155 ------------------ .../GameAdvancedListItemViewModel.java | 7 +- HMCL/src/main/resources/assets/css/root.css | 2 +- .../main/resources/assets/img/craft_table.png | Bin 0 -> 12058 bytes .../resources/assets/lang/I18N.properties | 5 +- .../resources/assets/lang/I18N_zh.properties | 5 +- .../assets/lang/I18N_zh_CN.properties | 5 +- 17 files changed, 513 insertions(+), 274 deletions(-) create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItemViewModel.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemView.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemViewModel.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListView.java create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListViewModel.java delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountPage.java rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{ => versions}/GameAdvancedListItemViewModel.java (94%) create mode 100644 HMCL/src/main/resources/assets/img/craft_table.png diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java index 93506b15d..bd091f404 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItem2.java @@ -29,6 +29,8 @@ import javafx.scene.layout.VBox; import javafx.scene.text.TextAlignment; import org.jackhuang.hmcl.setting.Theme; +import java.util.Optional; + public class AdvancedListItem2 extends StackPane { public AdvancedListItem2(AdvancedListItemViewModel viewModel) { @@ -47,6 +49,8 @@ public class AdvancedListItem2 extends StackPane { FXUtils.limitSize(imageView, 32, 32); imageView.setPreserveRatio(true); imageView.imageProperty().bind(viewModel.imageProperty()); + Optional.ofNullable(viewModel.viewportProperty()) + .ifPresent(imageView.viewportProperty()::bind); imageViewContainer.getChildren().setAll(imageView); VBox vbox = new VBox(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemViewModel.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemViewModel.java index d902c6ae9..ffb1fa339 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemViewModel.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AdvancedListItemViewModel.java @@ -19,6 +19,7 @@ package org.jackhuang.hmcl.ui; import javafx.beans.property.ObjectProperty; import javafx.beans.property.StringProperty; +import javafx.geometry.Rectangle2D; import javafx.scene.image.Image; public abstract class AdvancedListItemViewModel { @@ -27,6 +28,10 @@ public abstract class AdvancedListItemViewModel { public abstract ObjectProperty imageProperty(); + public ObjectProperty viewportProperty() { + return null; + } + public abstract StringProperty titleProperty(); public abstract StringProperty subtitleProperty(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java index c27d2ee4a..a70dc1b0f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -27,6 +27,8 @@ import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.setting.Settings; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; +import org.jackhuang.hmcl.ui.account.AccountListView; +import org.jackhuang.hmcl.ui.account.AccountListViewModel; import org.jackhuang.hmcl.ui.account.AuthlibInjectorServersPage; import org.jackhuang.hmcl.ui.construct.InputDialogPane; import org.jackhuang.hmcl.ui.construct.MessageBox; @@ -49,6 +51,7 @@ public final class Controllers { private static SettingsPage settingsPage = null; private static VersionPage versionPage = null; private static GameListView gameListView = null; + private static AccountListView accountListView = null; private static AuthlibInjectorServersPage serversPage = null; private static LeftPaneController leftPaneController; private static Decorator decorator; @@ -75,6 +78,13 @@ public final class Controllers { return gameListView; } + // FXThread + public static AccountListView getAccountListView() { + if (accountListView == null) + accountListView = new AccountListView(new AccountListViewModel()); + return accountListView; + } + // FXThread public static VersionPage getVersionPage() { if (versionPage == null) @@ -192,5 +202,7 @@ public final class Controllers { decorator = null; stage = null; scene = null; + gameListView = null; + accountListView = null; } } 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 cec6a6dcb..562d41b37 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java @@ -19,28 +19,15 @@ package org.jackhuang.hmcl.ui; import com.jfoenix.concurrency.JFXUtilities; import com.jfoenix.controls.JFXButton; -import com.jfoenix.controls.JFXPopup; import javafx.application.Platform; -import javafx.beans.binding.Bindings; import javafx.beans.binding.When; -import javafx.beans.property.ListProperty; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleListProperty; -import javafx.beans.property.SimpleObjectProperty; import javafx.scene.Node; -import javafx.scene.control.Tooltip; -import javafx.scene.image.Image; -import javafx.scene.input.MouseButton; import javafx.scene.layout.Region; 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.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; @@ -49,12 +36,12 @@ import org.jackhuang.hmcl.setting.*; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; -import org.jackhuang.hmcl.ui.account.AccountPage; +import org.jackhuang.hmcl.ui.account.AccountAdvancedListItemViewModel; import org.jackhuang.hmcl.ui.account.AddAccountPane; import org.jackhuang.hmcl.ui.construct.*; +import org.jackhuang.hmcl.ui.versions.GameAdvancedListItemViewModel; import org.jackhuang.hmcl.upgrade.UpdateChecker; import org.jackhuang.hmcl.util.Lang; -import org.jackhuang.hmcl.util.MappedObservableList; import java.io.File; import java.util.LinkedList; @@ -62,37 +49,20 @@ import java.util.Objects; import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; -import static javafx.collections.FXCollections.singletonObservableList; -import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; public final class LeftPaneController { private final AdvancedListBox leftPane; private final VBox profilePane = new VBox(); private final VBox accountPane = new VBox(); - private final IconedItem launcherSettingsItem; - private final AdvancedListItem2 gameListItem; - - private ListProperty accountItems = new SimpleListProperty<>(); - private ObjectProperty selectedAccount = new SimpleObjectProperty() { - { - accountItems.addListener(onInvalidating(this::invalidated)); - } - - @Override - protected void invalidated() { - Account selected = get(); - accountItems.forEach(item -> item.setSelected( - getAccountFromItem(item) - .map(it -> it == selected) - .orElse(false))); - } - }; public LeftPaneController(AdvancedListBox leftPane) { this.leftPane = leftPane; - launcherSettingsItem = new IconedItem(SVG.gear(Theme.blackFillBinding(), 20, 20)); + AdvancedListItem2 accountListItem = new AdvancedListItem2(new AccountAdvancedListItemViewModel()); + AdvancedListItem2 gameListItem = new AdvancedListItem2(new GameAdvancedListItemViewModel()); + + IconedItem launcherSettingsItem = new IconedItem(SVG.gear(Theme.blackFillBinding(), 20, 20)); launcherSettingsItem.getLabel().textProperty().bind( new When(UpdateChecker.outdatedProperty()) @@ -107,17 +77,12 @@ public final class LeftPaneController { launcherSettingsItem.maxWidthProperty().bind(leftPane.widthProperty()); launcherSettingsItem.setOnMouseClicked(e -> Controllers.navigate(Controllers.getSettingsPage())); - gameListItem = new AdvancedListItem2(new GameAdvancedListItemViewModel()); - leftPane - .add(new ClassTitle(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(i18n("launcher").toUpperCase()) + .startCategory(i18n("account").toUpperCase()) + .add(accountListItem) + .startCategory(i18n("version").toUpperCase()) .add(gameListItem) + .startCategory(i18n("launcher").toUpperCase()) .add(launcherSettingsItem) .add(new ClassTitle(i18n("profile.title").toUpperCase(), Lang.apply(new JFXButton(), button -> { button.setGraphic(SVG.plus(Theme.blackFillBinding(), 10, 10)); @@ -127,22 +92,6 @@ public final class LeftPaneController { }))) .add(profilePane); - // ==== Accounts ==== - // Missing account item - AdvancedListItem missingAccountItem = new AdvancedListItem(i18n("account.missing"), i18n("message.unknown")); - RipplerContainer missingAccountRippler = new RipplerContainer(missingAccountItem); - missingAccountItem.setOnSettingsButtonClicked(e -> addNewAccount()); - missingAccountRippler.setOnMouseClicked(e -> addNewAccount()); - - accountItems.bind( - new When(Accounts.accountsProperty().emptyProperty()) - .then(singletonObservableList(missingAccountRippler)) - .otherwise(MappedObservableList.create(Accounts.getAccounts(), this::createAccountItem))); - Bindings.bindContent(accountPane.getChildren(), accountItems); - - selectedAccount.bindBidirectional(Accounts.selectedAccountProperty()); - // ==== - 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); @@ -154,55 +103,6 @@ public final class LeftPaneController { .map(Account.class::cast); } - private static String accountSubtitle(Account account) { - if (account instanceof OfflineAccount) - return i18n("account.methods.offline"); - else if (account instanceof YggdrasilAccount) - return account.getUsername(); - else - return ""; - } - - private RipplerContainer createAccountItem(Account account) { - AdvancedListItem item = new AdvancedListItem(account.getCharacter(), accountSubtitle(account)); - RipplerContainer rippler = new RipplerContainer(item); - item.setOnSettingsButtonClicked(e -> { - AccountPage accountPage = new AccountPage(account, item); - JFXPopup popup = new JFXPopup(accountPage); - accountPage.setOnDelete(popup::hide); - popup.show((Node) e.getSource(), JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.LEFT, e.getX(), e.getY()); - }); - rippler.setOnMouseClicked(e -> { - if (e.getButton() == MouseButton.PRIMARY) { - selectedAccount.set(account); - } - }); - rippler.getProperties().put("account", account); - rippler.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.getUUID(), 4), AccountHelper.getViewport(4)); - } - - if (account instanceof AuthlibInjectorAccount) { - FXUtils.installTooltip(rippler, 500, 5000, 0, new Tooltip(((AuthlibInjectorAccount) account).getServer().getName())); - } - - // update skin - if (account instanceof YggdrasilAccount) { - AccountHelper.refreshSkinAsync((YggdrasilAccount) account) - .subscribe(Schedulers.javafx(), () -> { - Image image = AccountHelper.getSkin((YggdrasilAccount) account, 4); - item.setImage(image, AccountHelper.getViewport(4)); - }); - } - - return rippler; - } - public void checkAccount() { if (Accounts.getAccounts().isEmpty()) addNewAccount(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java index 96928efb6..8925dd194 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java @@ -18,8 +18,10 @@ package org.jackhuang.hmcl.ui; import javafx.beans.binding.ObjectBinding; +import javafx.geometry.Pos; import javafx.scene.Group; import javafx.scene.Node; +import javafx.scene.layout.StackPane; import javafx.scene.paint.Paint; import javafx.scene.shape.SVGPath; @@ -33,8 +35,11 @@ public final class SVG { path.setContent(d); path.fillProperty().bind(fill); - if (width < 0 || height < 0) - return path; + if (width < 0 || height < 0) { + StackPane pane = new StackPane(path); + pane.setAlignment(Pos.CENTER); + return pane; + } Group svg = new Group(path); double scale = Math.min(width / svg.getBoundsInParent().getWidth(), height / svg.getBoundsInParent().getHeight()); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItemViewModel.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItemViewModel.java new file mode 100644 index 000000000..8110af52b --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItemViewModel.java @@ -0,0 +1,100 @@ +/* + * 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.account; + +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.geometry.Rectangle2D; +import javafx.scene.image.Image; +import org.jackhuang.hmcl.auth.Account; +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.task.Schedulers; +import org.jackhuang.hmcl.ui.AdvancedListItemViewModel; +import org.jackhuang.hmcl.ui.Controllers; +import org.jackhuang.hmcl.ui.FXUtils; + +import static org.jackhuang.hmcl.util.i18n.I18n.i18n; + +public class AccountAdvancedListItemViewModel extends AdvancedListItemViewModel { + private final ObjectProperty image = new SimpleObjectProperty<>(); + private final ObjectProperty viewport = new SimpleObjectProperty<>(AccountHelper.getViewport(4)); + private final StringProperty title = new SimpleStringProperty(); + private final StringProperty subtitle = new SimpleStringProperty(); + + public AccountAdvancedListItemViewModel() { + + FXUtils.onChangeAndOperate(Accounts.selectedAccountProperty(), account -> { + if (account == null) { + title.set(i18n("account.missing")); + subtitle.set(i18n("account.missing.add")); + image.set(new Image("/assets/img/craft_table.png")); + } else { + title.set(account.getCharacter()); + subtitle.set(accountSubtitle(account)); + + this.image.set(AccountHelper.getDefaultSkin(account.getUUID(), 4)); + + if (account instanceof YggdrasilAccount) { + AccountHelper.loadSkinAsync((YggdrasilAccount) account).subscribe(Schedulers.javafx(), () -> { + Image image = AccountHelper.getSkin((YggdrasilAccount) account, 4); + this.image.set(image); + }); + } + } + }); + } + + @Override + public void action() { + Controllers.navigate(Controllers.getAccountListView()); + } + + @Override + public ObjectProperty imageProperty() { + return image; + } + + @Override + public ObjectProperty viewportProperty() { + return viewport; + } + + @Override + public StringProperty titleProperty() { + return title; + } + + @Override + public StringProperty subtitleProperty() { + return subtitle; + } + + private static String accountSubtitle(Account account) { + if (account instanceof OfflineAccount) + return i18n("account.methods.offline"); + else if (account instanceof YggdrasilAccount) + return account.getUsername(); + else + return ""; + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemView.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemView.java new file mode 100644 index 000000000..8728c20df --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemView.java @@ -0,0 +1,88 @@ +/* + * 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.account; + +import com.jfoenix.concurrency.JFXUtilities; +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXRadioButton; +import com.jfoenix.effects.JFXDepthManager; +import javafx.geometry.Pos; +import javafx.scene.image.ImageView; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.StackPane; +import org.jackhuang.hmcl.setting.Theme; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.SVG; +import org.jackhuang.hmcl.ui.TwoLineListItem; + +import static org.jackhuang.hmcl.util.i18n.I18n.i18n; + +public class AccountListItemView extends BorderPane { + + public AccountListItemView(AccountListItemViewModel viewModel) { + JFXRadioButton chkSelected = new JFXRadioButton(); + BorderPane.setAlignment(chkSelected, Pos.CENTER); + chkSelected.setUserData(viewModel); + chkSelected.selectedProperty().bindBidirectional(viewModel.selectedProperty()); + chkSelected.setToggleGroup(viewModel.getToggleGroup()); + setLeft(chkSelected); + + HBox center = new HBox(); + center.setSpacing(8); + center.setAlignment(Pos.CENTER_LEFT); + + StackPane imageViewContainer = new StackPane(); + FXUtils.setLimitWidth(imageViewContainer, 32); + FXUtils.setLimitHeight(imageViewContainer, 32); + + ImageView imageView = new ImageView(); + FXUtils.limitSize(imageView, 32, 32); + imageView.imageProperty().bind(viewModel.imageProperty()); + imageView.viewportProperty().bind(viewModel.viewportProperty()); + imageViewContainer.getChildren().setAll(imageView); + + TwoLineListItem item = new TwoLineListItem(); + BorderPane.setAlignment(item, Pos.CENTER); + center.getChildren().setAll(imageView, item); + setCenter(center); + + HBox right = new HBox(); + right.setAlignment(Pos.CENTER_RIGHT); + JFXButton btnRefresh = new JFXButton(); + btnRefresh.setOnMouseClicked(e -> viewModel.refresh()); + btnRefresh.getStyleClass().add("toggle-icon4"); + btnRefresh.setGraphic(SVG.refresh(Theme.blackFillBinding(), -1, -1)); + JFXUtilities.runInFX(() -> FXUtils.installTooltip(btnRefresh, i18n("button.refresh"))); + right.getChildren().add(btnRefresh); + + JFXButton btnRemove = new JFXButton(); + btnRemove.setOnMouseClicked(e -> viewModel.remove()); + btnRemove.getStyleClass().add("toggle-icon4"); + BorderPane.setAlignment(btnRemove, Pos.CENTER); + btnRemove.setGraphic(SVG.delete(Theme.blackFillBinding(), -1, -1)); + JFXUtilities.runInFX(() -> FXUtils.installTooltip(btnRefresh, i18n("button.delete"))); + right.getChildren().add(btnRemove); + setRight(right); + + setStyle("-fx-background-color: white; -fx-padding: 8 8 8 0;"); + JFXDepthManager.setDepth(this, 1); + item.titleProperty().bind(viewModel.titleProperty()); + item.subtitleProperty().bind(viewModel.subtitleProperty()); + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemViewModel.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemViewModel.java new file mode 100644 index 000000000..c28e107df --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemViewModel.java @@ -0,0 +1,116 @@ +/* + * 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.account; + +import javafx.beans.property.*; +import javafx.geometry.Rectangle2D; +import javafx.scene.control.ToggleGroup; +import javafx.scene.image.Image; +import org.jackhuang.hmcl.auth.Account; +import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount; +import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer; +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.task.Schedulers; + +import static org.jackhuang.hmcl.util.i18n.I18n.i18n; + +public class AccountListItemViewModel { + private final Account account; + private final ToggleGroup toggleGroup; + private final StringProperty title = new SimpleStringProperty(); + private final StringProperty subtitle = new SimpleStringProperty(); + private final BooleanProperty selected = new SimpleBooleanProperty(); + private final ObjectProperty image = new SimpleObjectProperty<>(); + private final ObjectProperty viewport = new SimpleObjectProperty<>(); + + public AccountListItemViewModel(ToggleGroup toggleGroup, Account account) { + this.account = account; + this.toggleGroup = toggleGroup; + + StringBuilder subtitleString = new StringBuilder(Accounts.getAccountTypeName(account)); + if (account instanceof AuthlibInjectorAccount) { + AuthlibInjectorServer server = ((AuthlibInjectorAccount) account).getServer(); + subtitleString.append(", ").append(i18n("account.injector.server")).append(": ").append(server.getName()); + } + + if (account instanceof OfflineAccount) + title.set(account.getCharacter()); + else + title.set(account.getUsername() + " - " + account.getCharacter()); + subtitle.set(subtitleString.toString()); + selected.set(Accounts.selectedAccountProperty().get() == account); + + viewport.set(AccountHelper.getViewport(4)); + if (account instanceof YggdrasilAccount) { + Image image = AccountHelper.getSkin((YggdrasilAccount) account, 4); + this.image.set(image); + } else { + this.image.set(AccountHelper.getDefaultSkin(account.getUUID(), 4)); + } + } + + public ToggleGroup getToggleGroup() { + return toggleGroup; + } + + public Account getAccount() { + return account; + } + + public StringProperty titleProperty() { + return title; + } + + public StringProperty subtitleProperty() { + return subtitle; + } + + public BooleanProperty selectedProperty() { + return selected; + } + + public ObjectProperty imageProperty() { + return image; + } + + public ObjectProperty viewportProperty() { + return viewport; + } + + public void refresh() { + if (account instanceof YggdrasilAccount) { + // progressBar.setVisible(true); + AccountHelper.refreshSkinAsync((YggdrasilAccount) account) + .finalized(Schedulers.javafx(), (variables, isDependentsSucceeded) -> { + // progressBar.setVisible(false); + + if (isDependentsSucceeded) { + Image image = AccountHelper.getSkin((YggdrasilAccount) account, 4); + this.image.set(image); + } + }).start(); + } + } + + public void remove() { + Accounts.getAccounts().remove(account); + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListView.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListView.java new file mode 100644 index 000000000..8e0cc566d --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListView.java @@ -0,0 +1,81 @@ +/* + * 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.account; + +import com.jfoenix.controls.JFXButton; +import javafx.beans.binding.Bindings; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.ScrollPane; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import org.jackhuang.hmcl.setting.Theme; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.SVG; +import org.jackhuang.hmcl.ui.wizard.DecoratorPage; +import org.jackhuang.hmcl.util.MappedObservableList; + +import static org.jackhuang.hmcl.util.i18n.I18n.i18n; + +public class AccountListView extends StackPane implements DecoratorPage { + private final StringProperty title = new SimpleStringProperty(i18n("account.manage")); + + public AccountListView(AccountListViewModel viewModel) { + ScrollPane scrollPane = new ScrollPane(); + { + scrollPane.setFitToWidth(true); + + VBox accountList = new VBox(); + accountList.maxWidthProperty().bind(scrollPane.widthProperty()); + accountList.setSpacing(10); + accountList.setStyle("-fx-padding: 10 10 10 10;"); + + Bindings.bindContent(accountList.getChildren(), + MappedObservableList.create(viewModel.itemsProperty(), AccountListItemView::new)); + + scrollPane.setContent(accountList); + } + + VBox vBox = new VBox(); + { + vBox.setAlignment(Pos.BOTTOM_RIGHT); + vBox.setPickOnBounds(false); + vBox.setPadding(new Insets(15)); + vBox.setSpacing(15); + + JFXButton btnAdd = new JFXButton(); + FXUtils.setLimitWidth(btnAdd, 40); + FXUtils.setLimitHeight(btnAdd, 40); + btnAdd.getStyleClass().setAll("jfx-button-raised-round"); + btnAdd.setButtonType(JFXButton.ButtonType.RAISED); + btnAdd.setGraphic(SVG.plus(Theme.whiteFillBinding(), -1, -1)); + btnAdd.setOnMouseClicked(e -> viewModel.addNewAccount()); + + vBox.getChildren().setAll(btnAdd); + } + + getChildren().setAll(scrollPane, vBox); + } + + @Override + public StringProperty titleProperty() { + return title; + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListViewModel.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListViewModel.java new file mode 100644 index 000000000..d952e2772 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListViewModel.java @@ -0,0 +1,73 @@ +/* + * 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.account; + +import javafx.beans.property.ListProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleListProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.collections.FXCollections; +import javafx.scene.control.ToggleGroup; +import org.jackhuang.hmcl.auth.Account; +import org.jackhuang.hmcl.setting.Accounts; +import org.jackhuang.hmcl.ui.Controllers; +import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.util.MappedObservableList; + +import java.util.Comparator; + +import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating; + +public class AccountListViewModel { + private final ListProperty items = new SimpleListProperty<>(FXCollections.observableArrayList()); + private ObjectProperty selectedAccount = new SimpleObjectProperty() { + { + items.addListener(onInvalidating(this::invalidated)); + } + + @Override + protected void invalidated() { + Account selected = get(); + items.forEach(item -> item.selectedProperty().set(item.getAccount() == selected)); + } + }; + + private ToggleGroup toggleGroup; + + public AccountListViewModel() { + toggleGroup = new ToggleGroup(); + + items.bindContent(MappedObservableList.create( + Accounts.accountsProperty(), + account -> new AccountListItemViewModel(toggleGroup, account))); + + selectedAccount.bindBidirectional(Accounts.selectedAccountProperty()); + toggleGroup.selectedToggleProperty().addListener((o, a, toggle) -> { + if (toggle == null || toggle.getUserData() == null) return; + selectedAccount.set(((AccountListItemViewModel) toggle.getUserData()).getAccount()); + }); + } + + public void addNewAccount() { + Controllers.dialog(new AddAccountPane()); + } + + public ListProperty itemsProperty() { + return items; + } +} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountPage.java deleted file mode 100644 index f0eff72d8..000000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountPage.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * 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.account; - -import com.jfoenix.controls.JFXButton; -import com.jfoenix.controls.JFXProgressBar; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; -import javafx.fxml.FXML; -import javafx.scene.control.Label; -import javafx.scene.image.Image; -import javafx.scene.layout.BorderPane; -import javafx.scene.layout.StackPane; - -import org.jackhuang.hmcl.auth.Account; -import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount; -import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer; -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.Theme; -import org.jackhuang.hmcl.task.Schedulers; -import org.jackhuang.hmcl.ui.FXUtils; -import org.jackhuang.hmcl.ui.SVG; -import org.jackhuang.hmcl.ui.construct.AdvancedListItem; -import org.jackhuang.hmcl.ui.construct.ComponentList; -import org.jackhuang.hmcl.ui.wizard.DecoratorPage; - -import static org.jackhuang.hmcl.ui.FXUtils.installTooltip; -import static org.jackhuang.hmcl.util.i18n.I18n.i18n; - -import java.util.Optional; - -public class AccountPage extends StackPane implements DecoratorPage { - private final StringProperty title; - private final ObjectProperty onDelete = new SimpleObjectProperty<>(this, "onDelete"); - private final AdvancedListItem item; - 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 BorderPane paneEmail; - @FXML - private ComponentList componentList; - @FXML - private JFXButton btnDelete; - @FXML - private JFXButton btnRefresh; - @FXML - private JFXProgressBar progressBar; - - public AccountPage(Account account, AdvancedListItem item) { - this.account = account; - this.item = item; - - title = new SimpleStringProperty(this, "title", i18n("account") + " - " + account.getCharacter()); - - FXUtils.loadFXML(this, "/assets/fxml/account.fxml"); - - if (account instanceof AuthlibInjectorAccount) { - AuthlibInjectorServer server = ((AuthlibInjectorAccount) account).getServer(); - lblServer.setText(server.getName()); - installTooltip(lblServer, server.getUrl()); - } else { - componentList.removeChildren(paneServer); - - if (account instanceof OfflineAccount) { - componentList.removeChildren(paneEmail); - } - } - - btnDelete.setGraphic(SVG.delete(Theme.blackFillBinding(), 15, 15)); - btnRefresh.setGraphic(SVG.refresh(Theme.blackFillBinding(), 15, 15)); - - lblCharacter.setText(account.getCharacter()); - lblType.setText(Accounts.getAccountTypeName(account)); - lblEmail.setText(account.getUsername()); - - btnRefresh.setVisible(account instanceof YggdrasilAccount); - } - - @FXML - private void onDelete() { - Accounts.getAccounts().remove(account); - Optional.ofNullable(onDelete.get()).ifPresent(Runnable::run); - } - - @FXML - private void onRefresh() { - if (account instanceof YggdrasilAccount) { - progressBar.setVisible(true); - AccountHelper.refreshSkinAsync((YggdrasilAccount) account) - .finalized(Schedulers.javafx(), (variables, isDependentsSucceeded) -> { - progressBar.setVisible(false); - - if (isDependentsSucceeded) { - Image image = AccountHelper.getSkin((YggdrasilAccount) account, 4); - item.setImage(image, AccountHelper.getViewport(4)); - } - }).start(); - } - } - - public String getTitle() { - return title.get(); - } - - @Override - public StringProperty titleProperty() { - return title; - } - - public void setTitle(String title) { - this.title.set(title); - } - - public Runnable getOnDelete() { - return onDelete.get(); - } - - public ObjectProperty onDeleteProperty() { - return onDelete; - } - - public void setOnDelete(Runnable onDelete) { - this.onDelete.set(onDelete); - } -} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameAdvancedListItemViewModel.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItemViewModel.java similarity index 94% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/GameAdvancedListItemViewModel.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItemViewModel.java index a484888fe..f4581c0f0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameAdvancedListItemViewModel.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItemViewModel.java @@ -15,7 +15,7 @@ * 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; +package org.jackhuang.hmcl.ui.versions; import com.jfoenix.concurrency.JFXUtilities; import javafx.beans.InvalidationListener; @@ -23,14 +23,15 @@ import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; -import javafx.beans.value.ChangeListener; import javafx.scene.image.Image; import org.jackhuang.hmcl.event.EventBus; import org.jackhuang.hmcl.event.ProfileChangedEvent; import org.jackhuang.hmcl.event.RefreshedVersionsEvent; -import org.jackhuang.hmcl.event.RefreshingVersionsEvent; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Settings; +import org.jackhuang.hmcl.ui.AdvancedListItemViewModel; +import org.jackhuang.hmcl.ui.Controllers; +import org.jackhuang.hmcl.ui.WeakListenerHelper; import java.io.File; diff --git a/HMCL/src/main/resources/assets/css/root.css b/HMCL/src/main/resources/assets/css/root.css index 04ad657a9..94fe55876 100644 --- a/HMCL/src/main/resources/assets/css/root.css +++ b/HMCL/src/main/resources/assets/css/root.css @@ -52,7 +52,7 @@ } .advanced-list-box-item { - -fx-padding: 10 16 10 16; + -fx-padding: 0 16 0 16; } .advanced-list-box-content { diff --git a/HMCL/src/main/resources/assets/img/craft_table.png b/HMCL/src/main/resources/assets/img/craft_table.png new file mode 100644 index 0000000000000000000000000000000000000000..45740510a49e45390674610b603169eaa4685d9f GIT binary patch literal 12058 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 eJ=h~J!v6pOM>u`} literal 0 HcmV?d00001 diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 2302fd043..48b52fa7e 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -48,11 +48,13 @@ account.injector.http=Warning: This server is using HTTP, which will cause your account.injector.server=Auth Server account.injector.server_url=Server URL account.injector.server_name=Server Name +account.manage=Account List account.methods=Login Type account.methods.authlib_injector=authlib-injector account.methods.offline=Offline account.methods.yggdrasil=Mojang -account.missing=None +account.missing=No Account +account.missing.add=Click the button on the right to add account.password=Password account.username=Name @@ -325,6 +327,7 @@ update.latest=This is latest Version. update.no_browser=Cannot open any browser. The link has been copied to the clipboard. Paste it to a browser address bar to update. update.tooltip=Update +version=Games version.cannot_read=Unable to gather the game version. Cannot continue auto-installing. version.forbidden_name=Forbidden name, do not use this. version.game.old=Old diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index baffe02dc..632f4d877 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -48,11 +48,13 @@ account.injector.http=警告:此伺服器使用不安全的 HTTP 協議,您 account.injector.server=認證伺服器 account.injector.server_url=伺服器位址 account.injector.server_name=伺服器名稱 +account.manage=帳戶列表 account.methods=登入方式 account.methods.authlib_injector=authlib-injector 登入 account.methods.offline=離線模式 account.methods.yggdrasil=正版登入 -account.missing=沒有帳戶 +account.missing=沒有遊戲帳戶 +account.missing.add=點擊右邊按鈕添加 account.password=密碼 account.username=使用者名稱 @@ -325,6 +327,7 @@ update.latest=目前版本為最新版本 update.no_browser=無法打開瀏覽器,網址已經複製到剪貼簿了,您可以手動複製網址打開頁面 update.tooltip=更新 +version=遊戲 version.cannot_read=讀取遊戲版本失敗,無法進行自動安裝 version.forbidden_name=此版本名稱不受支援,請換一個名字 version.game.old=老舊版本 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index 8eee5b0d6..036e06861 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -48,11 +48,13 @@ account.injector.http=警告:此服务器使用不安全的 HTTP 协议,您 account.injector.server=认证服务器 account.injector.server_url=服务器地址 account.injector.server_name=服务器名称 +account.manage=账户列表 account.methods=登录方式 account.methods.authlib_injector=authlib-injector 登录 account.methods.offline=离线模式 account.methods.yggdrasil=正版登录 -account.missing=没有账户 +account.missing=没有游戏账户 +account.missing.add=点击右边按钮添加 account.password=密码 account.username=用户名 @@ -325,6 +327,7 @@ update.latest=当前版本为最新版本 update.no_browser=无法打开浏览器,网址已经复制到剪贴板了,您可以手动粘贴网址打开页面 update.tooltip=更新 +version=游戏 version.cannot_read=读取游戏版本失败,无法进行自动安装 version.forbidden_name=此版本名称不受支持,请换一个名字 version.game.old=远古版