Add account list view

This commit is contained in:
huangyuhui
2018-08-30 22:14:30 +08:00
parent 1695619ef5
commit 08564e5ea2
17 changed files with 513 additions and 274 deletions

View File

@@ -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();

View File

@@ -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<Image> imageProperty();
public ObjectProperty<Rectangle2D> viewportProperty() {
return null;
}
public abstract StringProperty titleProperty();
public abstract StringProperty subtitleProperty();

View File

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

View File

@@ -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<RipplerContainer> accountItems = new SimpleListProperty<>();
private ObjectProperty<Account> selectedAccount = new SimpleObjectProperty<Account>() {
{
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();

View File

@@ -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());

View File

@@ -0,0 +1,100 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* 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> image = new SimpleObjectProperty<>();
private final ObjectProperty<Rectangle2D> 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<Image> imageProperty() {
return image;
}
@Override
public ObjectProperty<Rectangle2D> 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 "";
}
}

View File

@@ -0,0 +1,88 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* 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());
}
}

View File

@@ -0,0 +1,116 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* 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> image = new SimpleObjectProperty<>();
private final ObjectProperty<Rectangle2D> 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<Image> imageProperty() {
return image;
}
public ObjectProperty<Rectangle2D> 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);
}
}

View File

@@ -0,0 +1,81 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* 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;
}
}

View File

@@ -0,0 +1,73 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* 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<AccountListItemViewModel> items = new SimpleListProperty<>(FXCollections.observableArrayList());
private ObjectProperty<Account> selectedAccount = new SimpleObjectProperty<Account>() {
{
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<AccountListItemViewModel> itemsProperty() {
return items;
}
}

View File

@@ -1,155 +0,0 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* 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<Runnable> 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<Runnable> onDeleteProperty() {
return onDelete;
}
public void setOnDelete(Runnable onDelete) {
this.onDelete.set(onDelete);
}
}

View File

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

View File

@@ -52,7 +52,7 @@
}
.advanced-list-box-item {
-fx-padding: 10 16 10 16;
-fx-padding: 0 16 0 16;
}
.advanced-list-box-content {

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

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

View File

@@ -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=老舊版本

View File

@@ -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=远古版