diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AddAccountPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AddAccountPane.java index 6724eb5bf..cc8fbb5b6 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AddAccountPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AddAccountPane.java @@ -19,6 +19,8 @@ package org.jackhuang.hmcl.ui; import com.jfoenix.concurrency.JFXUtilities; import com.jfoenix.controls.*; + +import javafx.beans.binding.Bindings; import javafx.fxml.FXML; import javafx.geometry.Pos; import javafx.scene.Node; @@ -50,6 +52,7 @@ import org.jackhuang.hmcl.ui.construct.Validator; import org.jackhuang.hmcl.util.Constants; import org.jackhuang.hmcl.util.Logging; import static org.jackhuang.hmcl.ui.FXUtils.jfxListCellFactory; +import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating; import static org.jackhuang.hmcl.ui.FXUtils.stringConverter; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; @@ -77,11 +80,9 @@ public class AddAccountPane extends StackPane { cboServers.setCellFactory(jfxListCellFactory(server -> new TwoLineListItem(server.getName(), server.getUrl()))); cboServers.setConverter(stringConverter(AuthlibInjectorServer::getName)); - cboServers.setItems(Settings.SETTINGS.authlibInjectorServers); - - // workaround: otherwise the combox will be black - if (!cboServers.getItems().isEmpty()) - cboServers.getSelectionModel().select(0); + Bindings.bindContent(cboServers.getItems(), Settings.SETTINGS.authlibInjectorServers); + cboServers.getItems().addListener(onInvalidating(this::selectDefaultServer)); + selectDefaultServer(); cboType.getItems().setAll(i18n("account.methods.offline"), i18n("account.methods.yggdrasil"), i18n("account.methods.authlib_injector")); cboType.getSelectionModel().selectedIndexProperty().addListener((a, b, newValue) -> { @@ -102,6 +103,15 @@ public class AddAccountPane extends StackPane { txtPassword.textProperty().addListener(it -> validateAcceptButton()); } + /** + * Selects the first server if no server is selected. + */ + private void selectDefaultServer() { + if (!cboServers.getItems().isEmpty() && cboServers.getSelectionModel().isEmpty()) { + cboServers.getSelectionModel().select(0); + } + } + private void validateAcceptButton() { btnAccept.setDisable(!txtUsername.validate() || (cboType.getSelectionModel().getSelectedIndex() != 0 && !txtPassword.validate())); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AddAuthlibInjectorServerPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AddAuthlibInjectorServerPane.java index 83a711ae0..24cf3a45d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/AddAuthlibInjectorServerPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/AddAuthlibInjectorServerPane.java @@ -57,6 +57,12 @@ public class AddAuthlibInjectorServerPane extends StackPane { private AuthlibInjectorServer serverBeingAdded; + public AddAuthlibInjectorServerPane(String url) { + this(); + txtServerUrl.setText(url); + onAddNext(); + } + public AddAuthlibInjectorServerPane() { loadFXML(this, "/assets/fxml/authlib-injector-server-add.fxml"); transitionHandler = new TransitionHandler(addServerContainer); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Decorator.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Decorator.java index d871f9d62..236e858eb 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Decorator.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Decorator.java @@ -44,6 +44,7 @@ import javafx.scene.control.Label; import javafx.scene.control.Tooltip; import javafx.scene.image.Image; import javafx.scene.image.ImageView; +import javafx.scene.input.DragEvent; import javafx.scene.input.MouseEvent; import javafx.scene.layout.*; import javafx.scene.paint.Color; @@ -53,6 +54,7 @@ import javafx.stage.Stage; import javafx.stage.StageStyle; import javafx.util.Duration; import org.jackhuang.hmcl.Launcher; +import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorDnD; import org.jackhuang.hmcl.setting.EnumBackgroundImage; import org.jackhuang.hmcl.setting.Settings; import org.jackhuang.hmcl.setting.Theme; @@ -214,6 +216,8 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza animationHandler = new TransitionHandler(contentPlaceHolder); loadBackground(); + + setupAuthlibInjectorDnD(); } private void loadBackground() { @@ -681,4 +685,10 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza public AdvancedListBox getLeftPane() { return leftPane; } + + private void setupAuthlibInjectorDnD() { + addEventFilter(DragEvent.DRAG_OVER, AuthlibInjectorDnD.dragOverHandler()); + addEventFilter(DragEvent.DRAG_DROPPED, AuthlibInjectorDnD.dragDroppedHandler( + url -> Controllers.dialog(new AddAuthlibInjectorServerPane(url)))); + } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/authlibinjector/AuthlibInjectorDnD.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/authlibinjector/AuthlibInjectorDnD.java new file mode 100644 index 000000000..0568df0ce --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/auth/authlibinjector/AuthlibInjectorDnD.java @@ -0,0 +1,71 @@ +/* + * Hello Minecraft! Launcher. + * Copyright (C) 2018 huangyuhui + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see {http://www.gnu.org/licenses/}. + */ +package org.jackhuang.hmcl.auth.authlibinjector; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.Optional; +import java.util.function.Consumer; + +import javafx.event.EventHandler; +import javafx.scene.input.DragEvent; +import javafx.scene.input.Dragboard; +import javafx.scene.input.TransferMode; + +/** + * @author yushijinhun + * @see https://github.com/yushijinhun/authlib-injector/wiki/%E5%90%AF%E5%8A%A8%E5%99%A8%E6%8A%80%E6%9C%AF%E8%A7%84%E8%8C%83#dnd-%E6%96%B9%E5%BC%8F%E6%B7%BB%E5%8A%A0-yggdrasil-%E6%9C%8D%E5%8A%A1%E7%AB%AF + */ +public final class AuthlibInjectorDnD { + + private static final String SCHEME = "authlib-injector"; + private static final String PATH_YGGDRASIL_SERVER = "yggdrasil-server"; + + private AuthlibInjectorDnD() {} + + public static Optional parseUrlFromDragboard(Dragboard dragboard) { + String uri = dragboard.getString(); + if (uri == null) return Optional.empty(); + + String[] uriElements = uri.split(":"); + if (uriElements.length == 3 && SCHEME.equals(uriElements[0]) && PATH_YGGDRASIL_SERVER.equals(uriElements[1])) { + try { + return Optional.of(URLDecoder.decode(uriElements[2], "UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new IllegalStateException(e); + } + } + return Optional.empty(); + } + + public static EventHandler dragOverHandler() { + return event -> parseUrlFromDragboard(event.getDragboard()).ifPresent(url -> { + event.acceptTransferModes(TransferMode.COPY); + event.consume(); + }); + } + + public static EventHandler dragDroppedHandler(Consumer onUrlTransfered) { + return event -> parseUrlFromDragboard(event.getDragboard()).ifPresent(url -> { + event.setDropCompleted(true); + event.consume(); + onUrlTransfered.accept(url); + }); + } + +}