mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-09-15 14:56:05 -04:00
feat: AddAccountPane tabularization
This commit is contained in:
parent
2fea07fc6c
commit
4bdacb66a6
@ -29,11 +29,13 @@ import javafx.beans.property.SimpleListProperty;
|
|||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.Hyperlink;
|
import javafx.scene.control.Hyperlink;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
import org.jackhuang.hmcl.auth.*;
|
import org.jackhuang.hmcl.auth.*;
|
||||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorDownloadException;
|
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorDownloadException;
|
||||||
@ -52,6 +54,7 @@ import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
|||||||
import org.jackhuang.hmcl.util.skin.InvalidSkinException;
|
import org.jackhuang.hmcl.util.skin.InvalidSkinException;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
@ -65,57 +68,85 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
|||||||
|
|
||||||
public class AddAccountPane extends StackPane {
|
public class AddAccountPane extends StackPane {
|
||||||
|
|
||||||
@FXML private JFXTextField txtUsername;
|
@FXML
|
||||||
@FXML private JFXPasswordField txtPassword;
|
private JFXTextField txtUsername;
|
||||||
@FXML private Label lblCreationWarning;
|
@FXML
|
||||||
@FXML private Label lblPassword;
|
private JFXPasswordField txtPassword;
|
||||||
@FXML private Label lblUsername;
|
@FXML
|
||||||
@FXML private JFXComboBox<AccountFactory<?>> cboType;
|
private Label lblCreationWarning;
|
||||||
@FXML private JFXComboBox<AuthlibInjectorServer> cboServers;
|
@FXML
|
||||||
@FXML private Label lblInjectorServer;
|
private Label lblPassword;
|
||||||
@FXML private JFXButton btnAccept;
|
@FXML
|
||||||
@FXML private JFXButton btnAddServer;
|
private Label lblUsername;
|
||||||
@FXML private JFXButton btnManageServer;
|
@FXML
|
||||||
@FXML private SpinnerPane acceptPane;
|
private StackPane tabHeaderPane;
|
||||||
@FXML private HBox linksContainer;
|
@FXML
|
||||||
|
private JFXComboBox<AuthlibInjectorServer> cboServers;
|
||||||
|
@FXML
|
||||||
|
private Label lblInjectorServer;
|
||||||
|
@FXML
|
||||||
|
private JFXButton btnAccept;
|
||||||
|
@FXML
|
||||||
|
private JFXButton btnAddServer;
|
||||||
|
@FXML
|
||||||
|
private JFXButton btnManageServer;
|
||||||
|
@FXML
|
||||||
|
private SpinnerPane acceptPane;
|
||||||
|
@FXML
|
||||||
|
private HBox linksContainer;
|
||||||
|
|
||||||
|
private final TabHeader tabHeader;
|
||||||
|
|
||||||
private ListProperty<Hyperlink> links = new SimpleListProperty<>();
|
private ListProperty<Hyperlink> links = new SimpleListProperty<>();
|
||||||
|
|
||||||
|
private final Map<AccountFactory<?>, TabControl.Tab<Node>> tabMap = new HashMap<>();
|
||||||
|
|
||||||
public AddAccountPane() {
|
public AddAccountPane() {
|
||||||
FXUtils.loadFXML(this, "/assets/fxml/account-add.fxml");
|
FXUtils.loadFXML(this, "/assets/fxml/account-add.fxml");
|
||||||
|
|
||||||
|
List<TabControl.Tab<Node>> tabs = new ArrayList<>();
|
||||||
|
for (AccountFactory<?> factory : Accounts.FACTORIES) {
|
||||||
|
TabControl.Tab<Node> tab = new TabControl.Tab<>(factory.getLoginType().name(), Accounts.getLocalizedLoginTypeName(factory));
|
||||||
|
tab.setUserData(factory);
|
||||||
|
tabMap.put(factory, tab);
|
||||||
|
tabs.add(tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
tabHeader = new TabHeader(tabs.toArray(new TabControl.Tab[0]));
|
||||||
|
tabHeader.getStyleClass().add("add-account-tab-header");
|
||||||
|
// try selecting the preferred login type
|
||||||
|
tabHeader.getSelectionModel().select(
|
||||||
|
tabMap.get(tabMap.keySet().stream().filter(factory -> Accounts.getLoginType(factory).equals(config().getPreferredLoginType()))
|
||||||
|
.findFirst().orElse(Accounts.FACTORY_OFFLINE))
|
||||||
|
);
|
||||||
|
|
||||||
|
tabHeaderPane.getChildren().setAll(tabHeader);
|
||||||
|
tabHeader.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
|
|
||||||
|
|
||||||
cboServers.setCellFactory(jfxListCellFactory(server -> new TwoLineListItem(server.getName(), server.getUrl())));
|
cboServers.setCellFactory(jfxListCellFactory(server -> new TwoLineListItem(server.getName(), server.getUrl())));
|
||||||
cboServers.setConverter(stringConverter(AuthlibInjectorServer::getName));
|
cboServers.setConverter(stringConverter(AuthlibInjectorServer::getName));
|
||||||
Bindings.bindContent(cboServers.getItems(), config().getAuthlibInjectorServers());
|
Bindings.bindContent(cboServers.getItems(), config().getAuthlibInjectorServers());
|
||||||
cboServers.getItems().addListener(onInvalidating(this::resetServerSelection));
|
cboServers.getItems().addListener(onInvalidating(this::resetServerSelection));
|
||||||
resetServerSelection();
|
resetServerSelection();
|
||||||
|
|
||||||
cboType.getItems().setAll(Accounts.FACTORIES);
|
|
||||||
cboType.setConverter(stringConverter(Accounts::getLocalizedLoginTypeName));
|
|
||||||
// try selecting the preferred login type
|
|
||||||
cboType.getSelectionModel().select(
|
|
||||||
cboType.getItems().stream()
|
|
||||||
.filter(type -> Accounts.getLoginType(type).equals(config().getPreferredLoginType()))
|
|
||||||
.findFirst()
|
|
||||||
.orElse(Accounts.FACTORY_OFFLINE));
|
|
||||||
|
|
||||||
btnAddServer.visibleProperty().bind(cboServers.visibleProperty());
|
btnAddServer.visibleProperty().bind(cboServers.visibleProperty());
|
||||||
btnManageServer.visibleProperty().bind(cboServers.visibleProperty());
|
btnManageServer.visibleProperty().bind(cboServers.visibleProperty());
|
||||||
|
|
||||||
cboServers.getItems().addListener(onInvalidating(this::checkIfNoServer));
|
cboServers.getItems().addListener(onInvalidating(this::checkIfNoServer));
|
||||||
checkIfNoServer();
|
checkIfNoServer();
|
||||||
|
|
||||||
ReadOnlyObjectProperty<AccountFactory<?>> loginType = cboType.getSelectionModel().selectedItemProperty();
|
ReadOnlyObjectProperty<TabHeader.Tab<?>> loginType = tabHeader.getSelectionModel().selectedItemProperty();
|
||||||
|
|
||||||
// remember the last used login type
|
// remember the last used login type
|
||||||
loginType.addListener((observable, oldValue, newValue) -> config().setPreferredLoginType(Accounts.getLoginType(newValue)));
|
loginType.addListener((observable, oldValue, newValue) -> config().setPreferredLoginType(Accounts.getLoginType((AccountFactory<?>) newValue.getUserData())));
|
||||||
|
|
||||||
txtUsername.visibleProperty().bind(Bindings.createBooleanBinding(() -> loginType.get().getLoginType().requiresUsername, loginType));
|
txtUsername.visibleProperty().bind(Bindings.createBooleanBinding(() -> ((AccountFactory<?>) loginType.get().getUserData()).getLoginType().requiresUsername, loginType));
|
||||||
lblUsername.visibleProperty().bind(txtUsername.visibleProperty());
|
lblUsername.visibleProperty().bind(txtUsername.visibleProperty());
|
||||||
txtPassword.visibleProperty().bind(Bindings.createBooleanBinding(() -> loginType.get().getLoginType().requiresPassword, loginType));
|
txtPassword.visibleProperty().bind(Bindings.createBooleanBinding(() -> ((AccountFactory<?>) loginType.get().getUserData()).getLoginType().requiresPassword, loginType));
|
||||||
lblPassword.visibleProperty().bind(txtPassword.visibleProperty());
|
lblPassword.visibleProperty().bind(txtPassword.visibleProperty());
|
||||||
|
|
||||||
cboServers.visibleProperty().bind(loginType.isEqualTo(Accounts.FACTORY_AUTHLIB_INJECTOR));
|
cboServers.visibleProperty().bind(loginType.isEqualTo(tabMap.get(Accounts.FACTORY_AUTHLIB_INJECTOR)));
|
||||||
lblInjectorServer.visibleProperty().bind(cboServers.visibleProperty());
|
lblInjectorServer.visibleProperty().bind(cboServers.visibleProperty());
|
||||||
|
|
||||||
txtUsername.getValidators().add(new Validator(i18n("input.email"), this::validateUsername));
|
txtUsername.getValidators().add(new Validator(i18n("input.email"), this::validateUsername));
|
||||||
@ -142,7 +173,7 @@ public class AddAccountPane extends StackPane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean validateUsername(String username) {
|
private boolean validateUsername(String username) {
|
||||||
AccountFactory<?> loginType = cboType.getSelectionModel().getSelectedItem();
|
AccountFactory<?> loginType = ((AccountFactory<?>) tabHeader.getSelectionModel().getSelectedItem().getUserData());
|
||||||
if (loginType == Accounts.FACTORY_OFFLINE) {
|
if (loginType == Accounts.FACTORY_OFFLINE) {
|
||||||
return true;
|
return true;
|
||||||
} else if (loginType == Accounts.FACTORY_AUTHLIB_INJECTOR) {
|
} else if (loginType == Accounts.FACTORY_AUTHLIB_INJECTOR) {
|
||||||
@ -155,7 +186,7 @@ public class AddAccountPane extends StackPane {
|
|||||||
return username.contains("@");
|
return username.contains("@");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String[] ALLOWED_LINKS = { "register" };
|
private static final String[] ALLOWED_LINKS = {"register"};
|
||||||
|
|
||||||
public static List<Hyperlink> createHyperlinks(AuthlibInjectorServer server) {
|
public static List<Hyperlink> createHyperlinks(AuthlibInjectorServer server) {
|
||||||
if (server == null) {
|
if (server == null) {
|
||||||
@ -197,7 +228,7 @@ public class AddAccountPane extends StackPane {
|
|||||||
* Gets the additional data that needs to be passed into {@link AccountFactory#create(CharacterSelector, String, String, Object)}.
|
* Gets the additional data that needs to be passed into {@link AccountFactory#create(CharacterSelector, String, String, Object)}.
|
||||||
*/
|
*/
|
||||||
private Object getAuthAdditionalData() {
|
private Object getAuthAdditionalData() {
|
||||||
AccountFactory<?> factory = cboType.getSelectionModel().getSelectedItem();
|
AccountFactory<?> factory = ((AccountFactory<?>) tabHeader.getSelectionModel().getSelectedItem().getUserData());
|
||||||
if (factory == Accounts.FACTORY_AUTHLIB_INJECTOR) {
|
if (factory == Accounts.FACTORY_AUTHLIB_INJECTOR) {
|
||||||
return requireNonNull(cboServers.getSelectionModel().getSelectedItem(), "selected server cannot be null");
|
return requireNonNull(cboServers.getSelectionModel().getSelectedItem(), "selected server cannot be null");
|
||||||
}
|
}
|
||||||
@ -215,7 +246,7 @@ public class AddAccountPane extends StackPane {
|
|||||||
|
|
||||||
String username = txtUsername.getText();
|
String username = txtUsername.getText();
|
||||||
String password = txtPassword.getText();
|
String password = txtPassword.getText();
|
||||||
AccountFactory<?> factory = cboType.getSelectionModel().getSelectedItem();
|
AccountFactory<?> factory = ((AccountFactory<?>) tabHeader.getSelectionModel().getSelectedItem().getUserData());
|
||||||
Object additionalData = getAuthAdditionalData();
|
Object additionalData = getAuthAdditionalData();
|
||||||
|
|
||||||
Task.supplyAsync(() -> factory.create(new Selector(), username, password, additionalData))
|
Task.supplyAsync(() -> factory.create(new Selector(), username, password, additionalData))
|
||||||
|
@ -179,6 +179,7 @@ public interface TabControl {
|
|||||||
private final StringProperty text = new SimpleStringProperty(this, "text");
|
private final StringProperty text = new SimpleStringProperty(this, "text");
|
||||||
private final ReadOnlyBooleanWrapper selected = new ReadOnlyBooleanWrapper(this, "selected");
|
private final ReadOnlyBooleanWrapper selected = new ReadOnlyBooleanWrapper(this, "selected");
|
||||||
private final ObjectProperty<T> node = new SimpleObjectProperty<>(this, "node");
|
private final ObjectProperty<T> node = new SimpleObjectProperty<>(this, "node");
|
||||||
|
private final ObjectProperty<Object> userData = new SimpleObjectProperty<>(this, "userData");
|
||||||
private Supplier<? extends T> nodeSupplier;
|
private Supplier<? extends T> nodeSupplier;
|
||||||
|
|
||||||
public Tab(String id) {
|
public Tab(String id) {
|
||||||
@ -246,6 +247,18 @@ public interface TabControl {
|
|||||||
this.node.set(node);
|
this.node.set(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object getUserData() {
|
||||||
|
return userData.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectProperty<?> userDataProperty() {
|
||||||
|
return userData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserData(Object userData) {
|
||||||
|
this.userData.set(userData);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean initializeIfNeeded() {
|
public boolean initializeIfNeeded() {
|
||||||
if (getNode() == null) {
|
if (getNode() == null) {
|
||||||
if (getNodeSupplier() == null) {
|
if (getNodeSupplier() == null) {
|
||||||
|
@ -161,7 +161,6 @@ public class TabHeader extends Control implements TabControl {
|
|||||||
getChildren().setAll(headersRegion, selectedTabLine);
|
getChildren().setAll(headersRegion, selectedTabLine);
|
||||||
headersRegion.setPickOnBounds(false);
|
headersRegion.setPickOnBounds(false);
|
||||||
headersRegion.prefHeightProperty().bind(heightProperty());
|
headersRegion.prefHeightProperty().bind(heightProperty());
|
||||||
prefWidthProperty().bind(headersRegion.widthProperty());
|
|
||||||
rotate.pivotXProperty().bind(Bindings.createDoubleBinding(() -> getSkinnable().getSide().isHorizontal() ? 0.0 : 1, getSkinnable().sideProperty()));
|
rotate.pivotXProperty().bind(Bindings.createDoubleBinding(() -> getSkinnable().getSide().isHorizontal() ? 0.0 : 1, getSkinnable().sideProperty()));
|
||||||
rotate.pivotYProperty().bind(Bindings.createDoubleBinding(() -> getSkinnable().getSide().isHorizontal() ? 1.0 : 0, getSkinnable().sideProperty()));
|
rotate.pivotYProperty().bind(Bindings.createDoubleBinding(() -> getSkinnable().getSide().isHorizontal() ? 1.0 : 0, getSkinnable().sideProperty()));
|
||||||
|
|
||||||
@ -552,6 +551,7 @@ public class TabHeader extends Control implements TabControl {
|
|||||||
inner.setCenter(tabText);
|
inner.setCenter(tabText);
|
||||||
inner.getStyleClass().add("tab-container");
|
inner.getStyleClass().add("tab-container");
|
||||||
rippler = new JFXRippler(inner, JFXRippler.RipplerPos.FRONT);
|
rippler = new JFXRippler(inner, JFXRippler.RipplerPos.FRONT);
|
||||||
|
rippler.getStyleClass().add("tab-rippler");
|
||||||
rippler.setRipplerFill(ripplerColor);
|
rippler.setRipplerFill(ripplerColor);
|
||||||
getChildren().setAll(rippler);
|
getChildren().setAll(rippler);
|
||||||
|
|
||||||
|
@ -1,3 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
package org.jackhuang.hmcl.ui.main;
|
package org.jackhuang.hmcl.ui.main;
|
||||||
|
|
||||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||||
|
@ -260,6 +260,10 @@
|
|||||||
-fx-background-color: derive(-fx-base-color, -30%);
|
-fx-background-color: derive(-fx-base-color, -30%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tab-rippler {
|
||||||
|
-jfx-rippler-fill: -fx-base-check-color;
|
||||||
|
}
|
||||||
|
|
||||||
.jfx-tab-pane .jfx-rippler {
|
.jfx-tab-pane .jfx-rippler {
|
||||||
-jfx-rippler-fill: white;
|
-jfx-rippler-fill: white;
|
||||||
}
|
}
|
||||||
@ -272,6 +276,10 @@
|
|||||||
-fx-padding: 10 17 10 17;
|
-fx-padding: 10 17 10 17;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.add-account-tab-header .tab-label {
|
||||||
|
-fx-font-size: 12;
|
||||||
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* *
|
* *
|
||||||
* JFX Dialog Layout *
|
* JFX Dialog Layout *
|
||||||
|
@ -23,10 +23,7 @@
|
|||||||
<ColumnConstraints minWidth="140"/>
|
<ColumnConstraints minWidth="140"/>
|
||||||
</columnConstraints>
|
</columnConstraints>
|
||||||
|
|
||||||
<Label text="%account.methods" GridPane.halignment="LEFT" GridPane.columnIndex="0"
|
<StackPane fx:id="tabHeaderPane" GridPane.columnIndex="0" GridPane.columnSpan="3" GridPane.rowIndex="0" />
|
||||||
GridPane.rowIndex="0"/>
|
|
||||||
|
|
||||||
<JFXComboBox fx:id="cboType" GridPane.columnIndex="1" GridPane.rowIndex="0" GridPane.columnSpan="2"/>
|
|
||||||
|
|
||||||
<Label fx:id="lblInjectorServer" text="%account.injector.server" GridPane.halignment="LEFT"
|
<Label fx:id="lblInjectorServer" text="%account.injector.server" GridPane.halignment="LEFT"
|
||||||
GridPane.columnIndex="0" GridPane.rowIndex="1"/>
|
GridPane.columnIndex="0" GridPane.rowIndex="1"/>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user