Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
655e5ca7e0 | ||
|
|
3067d323de | ||
|
|
1404a080ad | ||
|
|
cbb2a1b575 | ||
|
|
30b276ec1a | ||
|
|
13a24d5b00 | ||
|
|
40360cf18a | ||
|
|
139e2ee78d | ||
|
|
3ff466462c | ||
|
|
9ff2442789 | ||
|
|
b11e4ab1db | ||
|
|
a52cc8c093 | ||
|
|
506c409303 | ||
|
|
53a8446f4a | ||
|
|
ab5bb8ea6c | ||
|
|
d097b84c82 | ||
|
|
f285635e76 | ||
|
|
0f7b5299e9 | ||
|
|
7a788d1e6e | ||
|
|
6c4231a6b2 | ||
|
|
6e202341ad | ||
|
|
95a509b86c | ||
|
|
602114af55 | ||
|
|
e5ccc40358 | ||
|
|
2310f6e1c0 | ||
|
|
9ae952bb78 | ||
|
|
57a7eaccf5 | ||
|
|
1934bfa962 | ||
|
|
a5f3ec7b9b | ||
|
|
3cfcf1e772 | ||
|
|
b45907a9f6 | ||
|
|
8fd9395e23 | ||
|
|
126b2a19e7 | ||
|
|
bde01af89d | ||
|
|
65491ef18e | ||
|
|
44a9470613 | ||
|
|
470045f228 | ||
|
|
d081a6ee92 | ||
|
|
95f404bb64 | ||
|
|
dcea76bcd3 | ||
|
|
e9b816fb87 | ||
|
|
20a7039f59 | ||
|
|
4ad172dda4 | ||
|
|
adf6c19996 | ||
|
|
24ec0adacf | ||
|
|
83bc6748a3 | ||
|
|
bfed651263 | ||
|
|
8bd4f2b5b5 | ||
|
|
3d7bc0b593 | ||
|
|
473c6f2b3c | ||
|
|
484ce6c895 | ||
|
|
eeccdbca72 | ||
|
|
552238676c |
No files matched your search
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -25,6 +25,7 @@ import org.jackhuang.hmcl.setting.Settings;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.upgrade.AppDataUpgrader;
|
||||
import org.jackhuang.hmcl.upgrade.IUpgrader;
|
||||
import org.jackhuang.hmcl.upgrade.UpdateChecker;
|
||||
import org.jackhuang.hmcl.util.*;
|
||||
@@ -32,6 +33,7 @@ import org.jackhuang.hmcl.util.*;
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public final class Main extends Application {
|
||||
@@ -59,7 +61,7 @@ public final class Main extends Application {
|
||||
try {
|
||||
// NetworkUtils.setUserAgentSupplier(() -> "Hello Minecraft! Launcher");
|
||||
Constants.UI_THREAD_SCHEDULER = Constants.JAVAFX_UI_THREAD_SCHEDULER;
|
||||
IUpgrader.NOW_UPGRADER.parseArguments(VersionNumber.asVersion(VERSION), Arrays.asList(args));
|
||||
UPGRADER.parseArguments(VersionNumber.asVersion(VERSION), Arrays.asList(args));
|
||||
|
||||
Logging.LOG.info("*** " + TITLE + " ***");
|
||||
|
||||
@@ -89,6 +91,10 @@ public final class Main extends Application {
|
||||
|
||||
Logging.LOG.info("Shutting down executor services.");
|
||||
Schedulers.shutdown();
|
||||
|
||||
Controllers.shutdown();
|
||||
|
||||
Lang.executeDelayed(OperatingSystem::forceGC, TimeUnit.SECONDS, 5, true);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -121,13 +127,14 @@ public final class Main extends Application {
|
||||
}
|
||||
|
||||
public static final File MINECRAFT_DIRECTORY = getWorkingDirectory("minecraft");
|
||||
public static final File HMCL_DIRECTORY = getWorkingDirectory("hmcl");
|
||||
|
||||
public static final String VERSION = "@HELLO_MINECRAFT_LAUNCHER_VERSION_FOR_GRADLE_REPLACING@";
|
||||
public static final String NAME = "HMCL";
|
||||
public static final String TITLE = NAME + " " + VERSION;
|
||||
public static final File APPDATA = getWorkingDirectory("hmcl");
|
||||
public static final ResourceBundle RESOURCE_BUNDLE = Settings.INSTANCE.getLocale().getResourceBundle();
|
||||
public static final UpdateChecker UPDATE_CHECKER = new UpdateChecker(VersionNumber.asVersion(VERSION));
|
||||
public static final IUpgrader UPGRADER = new AppDataUpgrader();
|
||||
public static final CrashReporter CRASH_REPORTER = new CrashReporter();
|
||||
|
||||
public static final String CONTACT = "http://huangyuhui.duapp.com/hmcl.php";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -22,7 +22,7 @@ import javafx.scene.image.Image;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.auth.Account;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.GameProfile;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.ProfileTexture;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.Texture;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
|
||||
import org.jackhuang.hmcl.setting.Settings;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
@@ -30,11 +30,9 @@ import org.jackhuang.hmcl.task.Scheduler;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.DialogController;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.Proxy;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
@@ -45,7 +43,7 @@ public final class AccountHelper {
|
||||
public static final AccountHelper INSTANCE = new AccountHelper();
|
||||
private AccountHelper() {}
|
||||
|
||||
public static final File SKIN_DIR = new File(Main.APPDATA, "skins");
|
||||
public static final File SKIN_DIR = new File(Main.HMCL_DIRECTORY, "skins");
|
||||
|
||||
public static void loadSkins() {
|
||||
loadSkins(Proxy.NO_PROXY);
|
||||
@@ -84,12 +82,9 @@ public final class AccountHelper {
|
||||
}
|
||||
|
||||
public static Image getSkin(YggdrasilAccount account, double scaleRatio) {
|
||||
if (account.getSelectedProfile() == null)
|
||||
if (account.getCharacter() == null)
|
||||
return getDefaultSkin(account, scaleRatio);
|
||||
String name = account.getSelectedProfile().getName();
|
||||
if (name == null)
|
||||
return getDefaultSkin(account, scaleRatio);
|
||||
File file = getSkinFile(name);
|
||||
File file = getSkinFile(account.getCharacter());
|
||||
if (file.exists()) {
|
||||
Image original = new Image("file:" + file.getAbsolutePath());
|
||||
return new Image("file:" + file.getAbsolutePath(),
|
||||
@@ -144,21 +139,33 @@ public final class AccountHelper {
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
if (account.canLogIn() && (account.getSelectedProfile() == null || refresh))
|
||||
if (!account.isLoggedIn() && (account.getCharacter() == null || refresh))
|
||||
DialogController.logIn(account);
|
||||
|
||||
downloadSkin(account, account.getSelectedProfile(), refresh, proxy);
|
||||
downloadSkin(account, refresh, proxy);
|
||||
}
|
||||
}
|
||||
|
||||
private static void downloadSkin(YggdrasilAccount account, GameProfile profile, boolean refresh, Proxy proxy) throws Exception {
|
||||
if (profile == null) return;
|
||||
String name = profile.getName();
|
||||
if (name == null) return;
|
||||
Optional<ProfileTexture> texture = account.getSkin(profile);
|
||||
account.clearCache();
|
||||
|
||||
Optional<Texture> texture = account.getSkin(profile);
|
||||
if (!texture.isPresent()) return;
|
||||
String url = texture.get().getUrl();
|
||||
File file = getSkinFile(name);
|
||||
File file = getSkinFile(profile.getName());
|
||||
if (!refresh && file.exists())
|
||||
return;
|
||||
new FileDownloadTask(NetworkUtils.toURL(url), file, proxy).run();
|
||||
}
|
||||
|
||||
private static void downloadSkin(YggdrasilAccount account, boolean refresh, Proxy proxy) throws Exception {
|
||||
account.clearCache();
|
||||
|
||||
if (account.getCharacter() == null) return;
|
||||
Optional<Texture> texture = account.getSkin();
|
||||
if (!texture.isPresent()) return;
|
||||
String url = texture.get().getUrl();
|
||||
File file = getSkinFile(account.getCharacter());
|
||||
if (!refresh && file.exists())
|
||||
return;
|
||||
new FileDownloadTask(NetworkUtils.toURL(url), file, proxy).run();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.download.DefaultGameBuilder;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
import org.jackhuang.hmcl.setting.Settings;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
@@ -55,7 +54,7 @@ public class HMCLGameDownloadTask extends Task {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws IOException {
|
||||
public void execute() {
|
||||
File jar = profile.getRepository().getVersionJar(version);
|
||||
|
||||
File cache = new File(Settings.INSTANCE.getCommonPath(), "jars/" + gameVersion + ".jar");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -26,8 +26,6 @@ import org.jackhuang.hmcl.setting.EnumGameDirectory;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
import org.jackhuang.hmcl.setting.Settings;
|
||||
import org.jackhuang.hmcl.setting.VersionSetting;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.FileUtils;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -85,7 +85,7 @@ public class HMCLModpackExportTask extends TaskResult<ZipEngine> {
|
||||
return null;
|
||||
});
|
||||
|
||||
Version mv = repository.getVersion(version).resolve(repository);
|
||||
Version mv = repository.getResolvedVersion(version);
|
||||
String gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version))
|
||||
.orElseThrow(() -> new IllegalStateException("Cannot parse the version of " + version));
|
||||
zip.putTextFile(Constants.GSON.toJson(mv.setJar(gameVersion)), "minecraft/pack.json"); // Making "jar" to gameVersion is to be compatible with old HMCL.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -21,7 +21,10 @@ import com.google.gson.JsonParseException;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import org.jackhuang.hmcl.download.DependencyManager;
|
||||
import org.jackhuang.hmcl.download.game.VersionJsonSaveTask;
|
||||
import org.jackhuang.hmcl.mod.*;
|
||||
import org.jackhuang.hmcl.mod.MinecraftInstanceTask;
|
||||
import org.jackhuang.hmcl.mod.Modpack;
|
||||
import org.jackhuang.hmcl.mod.ModpackConfiguration;
|
||||
import org.jackhuang.hmcl.mod.ModpackInstallTask;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.CompressingUtils;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -20,7 +20,8 @@ package org.jackhuang.hmcl.game;
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.jackhuang.hmcl.mod.Modpack;
|
||||
import org.jackhuang.hmcl.util.CompressingUtils;
|
||||
import org.jackhuang.hmcl.util.Constants;
|
||||
import org.jackhuang.hmcl.util.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
@@ -33,7 +34,7 @@ import java.util.List;
|
||||
*/
|
||||
public final class HMCLModpackManager {
|
||||
|
||||
public static final List<String> MODPACK_BLACK_LIST = Arrays.asList(
|
||||
public static final List<String> MODPACK_BLACK_LIST = Lang.immutableListOf(
|
||||
"usernamecache.json", "usercache.json", // Minecraft
|
||||
"launcher_profiles.json", "launcher.pack.lzma", // Minecraft Launcher
|
||||
"pack.json", "launcher.jar", "hmclmc.log", // HMCL
|
||||
@@ -43,7 +44,7 @@ public final class HMCLModpackManager {
|
||||
"downloads", // Curse
|
||||
"asm", "backups", "TCNodeTracker", "CustomDISkins", "data" // Mods
|
||||
);
|
||||
public static final List<String> MODPACK_SUGGESTED_BLACK_LIST = Arrays.asList(
|
||||
public static final List<String> MODPACK_SUGGESTED_BLACK_LIST = Lang.immutableListOf(
|
||||
"fonts", // BetterFonts
|
||||
"saves", "servers.dat", "options.txt", // Minecraft
|
||||
"blueprints" /* BuildCraft */,
|
||||
@@ -59,7 +60,7 @@ public final class HMCLModpackManager {
|
||||
return ModAdviser.ModSuggestion.NORMAL;
|
||||
else
|
||||
return ModAdviser.ModSuggestion.SUGGESTED;
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean match(List<String> l, String fileName, boolean isDirectory) {
|
||||
for (String s : l)
|
||||
@@ -81,13 +82,9 @@ public final class HMCLModpackManager {
|
||||
*/
|
||||
public static Modpack readHMCLModpackManifest(File file) throws IOException, JsonParseException {
|
||||
String manifestJson = CompressingUtils.readTextZipEntry(file, "modpack.json");
|
||||
Modpack manifest = Constants.GSON.fromJson(manifestJson, Modpack.class);
|
||||
if (manifest == null)
|
||||
throw new JsonParseException("`modpack.json` not found. " + file + " is not a valid HMCL modpack.");
|
||||
Modpack manifest = JsonUtils.fromNonNullJson(manifestJson, Modpack.class);
|
||||
String gameJson = CompressingUtils.readTextZipEntry(file, "minecraft/pack.json");
|
||||
Version game = Constants.GSON.fromJson(gameJson, Version.class);
|
||||
if (game == null)
|
||||
throw new JsonParseException("`minecraft/pack.json` not found. " + file + " iot a valid HMCL modpack.");
|
||||
Version game = JsonUtils.fromNonNullJson(gameJson, Version.class);
|
||||
if (game.getJar() == null)
|
||||
if (StringUtils.isBlank(manifest.getVersion()))
|
||||
throw new JsonParseException("Cannot recognize the game version of modpack " + file + ".");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -20,21 +20,28 @@ package org.jackhuang.hmcl.game;
|
||||
import com.jfoenix.concurrency.JFXUtilities;
|
||||
import javafx.application.Platform;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.auth.*;
|
||||
import org.jackhuang.hmcl.auth.Account;
|
||||
import org.jackhuang.hmcl.auth.AuthInfo;
|
||||
import org.jackhuang.hmcl.auth.AuthenticationException;
|
||||
import org.jackhuang.hmcl.auth.ServerDisconnectException;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.download.MaintainTask;
|
||||
import org.jackhuang.hmcl.launch.*;
|
||||
import org.jackhuang.hmcl.mod.CurseCompletionTask;
|
||||
import org.jackhuang.hmcl.setting.*;
|
||||
import org.jackhuang.hmcl.setting.LauncherVisibility;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
import org.jackhuang.hmcl.setting.Settings;
|
||||
import org.jackhuang.hmcl.setting.VersionSetting;
|
||||
import org.jackhuang.hmcl.task.*;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.DialogController;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
|
||||
import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane;
|
||||
import org.jackhuang.hmcl.ui.LogWindow;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageBox;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
|
||||
import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane;
|
||||
import org.jackhuang.hmcl.util.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
@@ -55,7 +62,7 @@ public final class LauncherHelper {
|
||||
if (account == null)
|
||||
throw new IllegalStateException("No account");
|
||||
|
||||
Version version = repository.getVersion(selectedVersion);
|
||||
Version version = repository.getResolvedVersion(selectedVersion);
|
||||
VersionSetting setting = profile.getVersionSetting(selectedVersion);
|
||||
|
||||
Platform.runLater(() -> {
|
||||
@@ -70,9 +77,10 @@ public final class LauncherHelper {
|
||||
Profile profile = Settings.INSTANCE.getSelectedProfile();
|
||||
GameRepository repository = profile.getRepository();
|
||||
DefaultDependencyManager dependencyManager = profile.getDependency();
|
||||
Version version = repository.getVersion(selectedVersion);
|
||||
Version version = repository.getResolvedVersion(selectedVersion);
|
||||
Account account = Settings.INSTANCE.getSelectedAccount();
|
||||
VersionSetting setting = profile.getVersionSetting(selectedVersion);
|
||||
Optional<String> gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version));
|
||||
|
||||
executor = Task.of(Schedulers.javafx(), () -> Controllers.dialog(launchingStepsPane))
|
||||
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.DEPENDENCIES)))
|
||||
@@ -87,12 +95,14 @@ public final class LauncherHelper {
|
||||
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.LOGGING_IN)))
|
||||
.then(Task.of(Main.i18n("account.methods"), variables -> {
|
||||
try {
|
||||
variables.set("account", account.logIn(new SpecificCharacterSelector(Accounts.getCurrentCharacter(account)), Settings.INSTANCE.getProxy()));
|
||||
} catch (ServerDisconnectException e) {
|
||||
if (account.canPlayOffline())
|
||||
variables.set("account", account.playOffline());
|
||||
else
|
||||
throw e;
|
||||
try {
|
||||
variables.set("account", account.logIn());
|
||||
} catch (ServerDisconnectException e) {
|
||||
if (account.canPlayOffline())
|
||||
variables.set("account", account.playOffline());
|
||||
else
|
||||
throw e;
|
||||
}
|
||||
} catch (AuthenticationException e) {
|
||||
variables.set("account", DialogController.logIn(account));
|
||||
JFXUtilities.runInFX(() -> Controllers.dialog(launchingStepsPane));
|
||||
@@ -101,11 +111,11 @@ public final class LauncherHelper {
|
||||
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.LAUNCHING)))
|
||||
.then(Task.of(variables -> {
|
||||
variables.set("launcher", new HMCLGameLauncher(
|
||||
repository, selectedVersion, variables.get("account"), setting.toLaunchOptions(profile.getGameDir()), new HMCLProcessListener(variables.get("account"), setting)
|
||||
repository, selectedVersion, variables.get("account"), setting.toLaunchOptions(profile.getGameDir()), new HMCLProcessListener(variables.get("account"), setting, gameVersion.isPresent())
|
||||
));
|
||||
}))
|
||||
.then(variables -> {
|
||||
DefaultLauncher launcher = variables.<DefaultLauncher>get("launcher");
|
||||
DefaultLauncher launcher = variables.get("launcher");
|
||||
if (scriptFile == null) {
|
||||
return new LaunchTask<>(launcher::launch).setName(Main.i18n("version.launch"));
|
||||
} else {
|
||||
@@ -135,26 +145,29 @@ public final class LauncherHelper {
|
||||
|
||||
launchingStepsPane.setExecutor(executor);
|
||||
executor.addTaskListener(new TaskListener() {
|
||||
AtomicInteger finished = new AtomicInteger(0);
|
||||
final AtomicInteger finished = new AtomicInteger(0);
|
||||
|
||||
@Override
|
||||
public void onFinished(Task task) {
|
||||
finished.incrementAndGet();
|
||||
Platform.runLater(() -> {
|
||||
launchingStepsPane.setProgress(1.0 * finished.get() / executor.getRunningTasks());
|
||||
});
|
||||
int runningTasks = executor.getRunningTasks();
|
||||
Platform.runLater(() -> launchingStepsPane.setProgress(1.0 * finished.get() / runningTasks));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTerminate() {
|
||||
Platform.runLater(() -> {
|
||||
if (executor.getLastException() != null)
|
||||
Controllers.dialog(I18nException.getStackTrace(executor.getLastException()),
|
||||
scriptFile == null ? Main.i18n("launch.failed") : Main.i18n("version.launch_script.failed"),
|
||||
MessageBox.ERROR_MESSAGE, Controllers::closeDialog);
|
||||
else
|
||||
Controllers.closeDialog();
|
||||
});
|
||||
public void onStop(boolean success, TaskExecutor executor) {
|
||||
if (!success) {
|
||||
Platform.runLater(() -> {
|
||||
if (executor.getLastException() != null)
|
||||
Controllers.dialog(I18nException.getStackTrace(executor.getLastException()),
|
||||
scriptFile == null ? Main.i18n("launch.failed") : Main.i18n("version.launch_script.failed"),
|
||||
MessageBox.ERROR_MESSAGE, Controllers::closeDialog);
|
||||
else
|
||||
Controllers.closeDialog();
|
||||
});
|
||||
}
|
||||
|
||||
LauncherHelper.this.executor = null;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -207,15 +220,11 @@ public final class LauncherHelper {
|
||||
}
|
||||
|
||||
public static void stopManagedProcesses() {
|
||||
synchronized (PROCESSES) {
|
||||
while (!PROCESSES.isEmpty())
|
||||
Optional.ofNullable(PROCESSES.poll()).ifPresent(ManagedProcess::stop);
|
||||
}
|
||||
while (!PROCESSES.isEmpty())
|
||||
Optional.ofNullable(PROCESSES.poll()).ifPresent(ManagedProcess::stop);
|
||||
}
|
||||
|
||||
public void emitStatus(LoadingState state) {
|
||||
if (state == LoadingState.DONE)
|
||||
Controllers.closeDialog();
|
||||
|
||||
launchingStepsPane.setTitle(state.getLocalizedMessage());
|
||||
launchingStepsPane.setSubtitle((state.ordinal() + 1) + " / " + LoadingState.values().length);
|
||||
@@ -243,7 +252,7 @@ public final class LauncherHelper {
|
||||
}
|
||||
}
|
||||
|
||||
class LaunchTask<T> extends TaskResult<T> {
|
||||
private static class LaunchTask<T> extends TaskResult<T> {
|
||||
private final ExceptionalSupplier<T, Exception> supplier;
|
||||
|
||||
public LaunchTask(ExceptionalSupplier<T, Exception> supplier) {
|
||||
@@ -284,17 +293,19 @@ public final class LauncherHelper {
|
||||
private ManagedProcess process;
|
||||
private boolean lwjgl;
|
||||
private LogWindow logWindow;
|
||||
private final boolean detectWindow;
|
||||
private final LinkedList<Pair<String, Log4jLevel>> logs;
|
||||
private final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
public HMCLProcessListener(AuthInfo authInfo, VersionSetting setting) {
|
||||
public HMCLProcessListener(AuthInfo authInfo, VersionSetting setting, boolean detectWindow) {
|
||||
this.setting = setting;
|
||||
this.detectWindow = detectWindow;
|
||||
|
||||
if (authInfo == null)
|
||||
forbiddenTokens = Collections.emptyMap();
|
||||
else
|
||||
forbiddenTokens = Lang.mapOf(
|
||||
new Pair<>(authInfo.getAuthToken(), "<access token>"),
|
||||
new Pair<>(authInfo.getAccessToken(), "<access token>"),
|
||||
new Pair<>(authInfo.getUserId(), "<uuid>"),
|
||||
new Pair<>(authInfo.getUsername(), "<player>")
|
||||
);
|
||||
@@ -339,8 +350,7 @@ public final class LauncherHelper {
|
||||
Platform.runLater(() -> logWindow.logLine(log, level));
|
||||
}
|
||||
|
||||
|
||||
if (!lwjgl && log.contains("LWJGL Version: ")) {
|
||||
if (!lwjgl && log.contains("LWJGL Version: ") || !detectWindow) {
|
||||
lwjgl = true;
|
||||
switch (visibility) {
|
||||
case HIDE_AND_REOPEN:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -106,7 +106,7 @@ public final class ModpackHelper {
|
||||
else throw new IllegalStateException("Unrecognized modpack: " + modpack);
|
||||
}
|
||||
|
||||
public static Task getUpdateTask(Profile profile, File zipFile, String name, ModpackConfiguration configuration) throws UnsupportedModpackException, MismatchedModpackTypeException, IOException {
|
||||
public static Task getUpdateTask(Profile profile, File zipFile, String name, ModpackConfiguration<?> configuration) throws UnsupportedModpackException, MismatchedModpackTypeException {
|
||||
Modpack modpack = ModpackHelper.readModpackManifest(zipFile);
|
||||
|
||||
switch (configuration.getType()) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -17,17 +17,27 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.setting;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.auth.Account;
|
||||
import org.jackhuang.hmcl.auth.AccountFactory;
|
||||
import org.jackhuang.hmcl.auth.OfflineAccount;
|
||||
import org.jackhuang.hmcl.auth.OfflineAccountFactory;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccountFactory;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorBuildInfo;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServerResponse;
|
||||
import org.jackhuang.hmcl.auth.offline.OfflineAccount;
|
||||
import org.jackhuang.hmcl.auth.offline.OfflineAccountFactory;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.MojangYggdrasilProvider;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccountFactory;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.Pair;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.util.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author huangyuhui
|
||||
@@ -37,42 +47,58 @@ public final class Accounts {
|
||||
|
||||
public static final String OFFLINE_ACCOUNT_KEY = "offline";
|
||||
public static final String YGGDRASIL_ACCOUNT_KEY = "yggdrasil";
|
||||
public static final String AUTHLIB_INJECTOR_ACCOUNT_KEY = "authlibInjector";
|
||||
|
||||
public static final Map<String, AccountFactory<?>> ACCOUNT_FACTORY = Lang.mapOf(
|
||||
new Pair<>(OFFLINE_ACCOUNT_KEY, OfflineAccountFactory.INSTANCE),
|
||||
new Pair<>(YGGDRASIL_ACCOUNT_KEY, YggdrasilAccountFactory.INSTANCE)
|
||||
new Pair<>(YGGDRASIL_ACCOUNT_KEY, new YggdrasilAccountFactory(MojangYggdrasilProvider.INSTANCE)),
|
||||
new Pair<>(AUTHLIB_INJECTOR_ACCOUNT_KEY, new AuthlibInjectorAccountFactory(Accounts::downloadAuthlibInjector))
|
||||
);
|
||||
|
||||
private static final Map<String, String> AUTHLIB_INJECTOR_SERVER_NAMES = new HashMap<>();
|
||||
|
||||
public static String getAccountType(Account account) {
|
||||
if (account instanceof OfflineAccount) return OFFLINE_ACCOUNT_KEY;
|
||||
else if (account instanceof AuthlibInjectorAccount) return AUTHLIB_INJECTOR_ACCOUNT_KEY;
|
||||
else if (account instanceof YggdrasilAccount) return YGGDRASIL_ACCOUNT_KEY;
|
||||
else return YGGDRASIL_ACCOUNT_KEY;
|
||||
}
|
||||
|
||||
public static void setCurrentCharacter(Account account, String character) {
|
||||
account.getProperties().put("character", character);
|
||||
}
|
||||
|
||||
public static boolean hasCurrentCharacter(Account account) {
|
||||
return Lang.get(account.getProperties(), "character", String.class, null) != null;
|
||||
}
|
||||
|
||||
public static String getCurrentCharacter(Account account) {
|
||||
return Lang.get(account.getProperties(), "character", String.class)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Account " + account + " has not set current character."));
|
||||
}
|
||||
|
||||
public static Optional<String> getCurrentCharacter(Map<Object, Object> storage) {
|
||||
Optional<Map> properties = Lang.get(storage, "properties", Map.class);
|
||||
if (!properties.isPresent()) return Optional.empty();
|
||||
return Lang.get(properties.get(), "character", String.class);
|
||||
}
|
||||
|
||||
static String getAccountId(Account account) {
|
||||
return getAccountId(account.getUsername(), getCurrentCharacter(account));
|
||||
return getAccountId(account.getUsername(), account.getCharacter());
|
||||
}
|
||||
|
||||
static String getAccountId(String username, String character) {
|
||||
return username + ":" + character;
|
||||
}
|
||||
|
||||
private static String downloadAuthlibInjector() throws Exception {
|
||||
AuthlibInjectorBuildInfo buildInfo = AuthlibInjectorBuildInfo.requestBuildInfo();
|
||||
File jar = new File(Main.HMCL_DIRECTORY, "authlib-injector.jar");
|
||||
File local = new File(Main.HMCL_DIRECTORY, "authlib-injector.txt");
|
||||
int buildNumber = 0;
|
||||
try {
|
||||
buildNumber = Integer.parseInt(FileUtils.readText(local));
|
||||
} catch (IOException | NumberFormatException ignore) {
|
||||
}
|
||||
if (buildNumber < buildInfo.getBuildNumber()) {
|
||||
new FileDownloadTask(new URL(buildInfo.getUrl()), jar).run();
|
||||
FileUtils.writeText(local, String.valueOf(buildInfo.getBuildNumber()));
|
||||
}
|
||||
return jar.getAbsolutePath();
|
||||
}
|
||||
|
||||
public static String getAuthlibInjectorServerName(String serverIp) {
|
||||
if (AUTHLIB_INJECTOR_SERVER_NAMES.containsKey(serverIp))
|
||||
return AUTHLIB_INJECTOR_SERVER_NAMES.get(serverIp);
|
||||
else {
|
||||
try {
|
||||
AuthlibInjectorServerResponse response = Constants.GSON.fromJson(NetworkUtils.doGet(NetworkUtils.toURL(serverIp)), AuthlibInjectorServerResponse.class);
|
||||
AUTHLIB_INJECTOR_SERVER_NAMES.put(serverIp, response.getMeta().getServerName());
|
||||
return response.getMeta().getServerName();
|
||||
} catch (JsonParseException | IOException | NullPointerException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -21,12 +21,9 @@ import com.google.gson.annotations.SerializedName;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.util.JavaVersion;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.*;
|
||||
|
||||
public final class Config {
|
||||
final class Config {
|
||||
|
||||
@SerializedName("last")
|
||||
private String selectedProfile = "";
|
||||
@@ -40,6 +37,12 @@ public final class Config {
|
||||
@SerializedName("commonpath")
|
||||
private String commonDirectory = Main.MINECRAFT_DIRECTORY.getAbsolutePath();
|
||||
|
||||
@SerializedName("hasProxy")
|
||||
private boolean hasProxy = false;
|
||||
|
||||
@SerializedName("hasProxyAuth")
|
||||
private boolean hasProxyAuth = false;
|
||||
|
||||
@SerializedName("proxyType")
|
||||
private int proxyType = 0;
|
||||
|
||||
@@ -85,6 +88,9 @@ public final class Config {
|
||||
@SerializedName("logLines")
|
||||
private int logLines = 100;
|
||||
|
||||
@SerializedName("authlibInjectorServerURLs")
|
||||
private Set<String> authlibInjectorServerURLs = new HashSet<>();
|
||||
|
||||
public String getSelectedProfile() {
|
||||
return selectedProfile;
|
||||
}
|
||||
@@ -121,6 +127,24 @@ public final class Config {
|
||||
Settings.INSTANCE.save();
|
||||
}
|
||||
|
||||
public boolean isHasProxy() {
|
||||
return hasProxy;
|
||||
}
|
||||
|
||||
public void setHasProxy(boolean hasProxy) {
|
||||
this.hasProxy = hasProxy;
|
||||
Settings.INSTANCE.save();
|
||||
}
|
||||
|
||||
public boolean isHasProxyAuth() {
|
||||
return hasProxyAuth;
|
||||
}
|
||||
|
||||
public void setHasProxyAuth(boolean hasProxyAuth) {
|
||||
this.hasProxyAuth = hasProxyAuth;
|
||||
Settings.INSTANCE.save();
|
||||
}
|
||||
|
||||
public int getProxyType() {
|
||||
return proxyType;
|
||||
}
|
||||
@@ -254,4 +278,13 @@ public final class Config {
|
||||
public void setLogLines(int logLines) {
|
||||
this.logLines = logLines;
|
||||
}
|
||||
|
||||
public Set<String> getAuthlibInjectorServerURLs() {
|
||||
return authlibInjectorServerURLs;
|
||||
}
|
||||
|
||||
public void setAuthlibInjectorServerURLs(Set<String> authlibInjectorServerURLs) {
|
||||
this.authlibInjectorServerURLs = authlibInjectorServerURLs;
|
||||
Settings.INSTANCE.save();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -18,6 +18,7 @@
|
||||
package org.jackhuang.hmcl.setting;
|
||||
|
||||
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider;
|
||||
import org.jackhuang.hmcl.download.CurseCDNDownloadProvider;
|
||||
import org.jackhuang.hmcl.download.DownloadProvider;
|
||||
import org.jackhuang.hmcl.download.MojangDownloadProvider;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
@@ -28,9 +29,9 @@ import java.util.List;
|
||||
public final class DownloadProviders {
|
||||
private DownloadProviders() {}
|
||||
|
||||
public static List<DownloadProvider> DOWNLOAD_PROVIDERS = Arrays.asList(MojangDownloadProvider.INSTANCE, BMCLAPIDownloadProvider.INSTANCE);
|
||||
public static final List<DownloadProvider> DOWNLOAD_PROVIDERS = Lang.immutableListOf(new MojangDownloadProvider(Settings.INSTANCE.getLocale() == Locales.ZH_CN), BMCLAPIDownloadProvider.INSTANCE, CurseCDNDownloadProvider.INSTANCE);
|
||||
|
||||
public static DownloadProvider getDownloadProvider(int index) {
|
||||
return Lang.get(DOWNLOAD_PROVIDERS, index).orElse(MojangDownloadProvider.INSTANCE);
|
||||
return Lang.get(DOWNLOAD_PROVIDERS, index).orElse(DOWNLOAD_PROVIDERS.get(0));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -54,7 +54,7 @@ public final class Locales {
|
||||
*/
|
||||
public static final SupportedLocale RU = new SupportedLocale(new Locale("ru"));
|
||||
|
||||
public static final List<SupportedLocale> LOCALES = Arrays.asList(DEFAULT, EN, ZH_CN);
|
||||
public static final List<SupportedLocale> LOCALES = Lang.immutableListOf(DEFAULT, EN, ZH_CN);
|
||||
|
||||
public static SupportedLocale getLocale(int index) {
|
||||
return Lang.get(LOCALES, index).orElse(DEFAULT);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2013 huangyuhui
|
||||
* 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
|
||||
@@ -19,7 +19,6 @@ package org.jackhuang.hmcl.setting;
|
||||
|
||||
import com.google.gson.*;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.game.HMCLDependencyManager;
|
||||
import org.jackhuang.hmcl.game.HMCLGameRepository;
|
||||
import org.jackhuang.hmcl.mod.ModManager;
|
||||
|
||||
+14
-24
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -15,32 +15,22 @@
|
||||
* 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.yggdrasil;
|
||||
package org.jackhuang.hmcl.setting;
|
||||
|
||||
public class ProfileResponse {
|
||||
private final String id;
|
||||
private final String name;
|
||||
private final PropertyMap properties;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
|
||||
public ProfileResponse() {
|
||||
this("", "", null);
|
||||
public final class Profiles {
|
||||
private Profiles() {
|
||||
}
|
||||
|
||||
public ProfileResponse(String id, String name, PropertyMap properties) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public PropertyMap getProperties() {
|
||||
return properties;
|
||||
public static String getProfileDisplayName(Profile profile) {
|
||||
switch (profile.getName()) {
|
||||
case Settings.DEFAULT_PROFILE:
|
||||
return Main.i18n("profile.default");
|
||||
case Settings.HOME_PROFILE:
|
||||
return Main.i18n("profile.home");
|
||||
default:
|
||||
return profile.getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -29,7 +29,7 @@ import java.util.List;
|
||||
public final class Proxies {
|
||||
private Proxies() {}
|
||||
|
||||
public static final List<Proxy.Type> PROXIES = Arrays.asList(Proxy.Type.DIRECT, Proxy.Type.HTTP, Proxy.Type.SOCKS);
|
||||
public static final List<Proxy.Type> PROXIES = Lang.immutableListOf(Proxy.Type.DIRECT, Proxy.Type.HTTP, Proxy.Type.SOCKS);
|
||||
|
||||
public static Proxy.Type getProxyType(int index) {
|
||||
return Lang.get(PROXIES, index).orElse(null);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -19,18 +19,15 @@ package org.jackhuang.hmcl.setting;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.text.Font;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.auth.Account;
|
||||
import org.jackhuang.hmcl.auth.AccountFactory;
|
||||
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
|
||||
import org.jackhuang.hmcl.download.DownloadProvider;
|
||||
import org.jackhuang.hmcl.download.MojangDownloadProvider;
|
||||
import org.jackhuang.hmcl.event.EventBus;
|
||||
import org.jackhuang.hmcl.event.ProfileChangedEvent;
|
||||
import org.jackhuang.hmcl.event.ProfileLoadingEvent;
|
||||
@@ -68,44 +65,42 @@ public class Settings {
|
||||
private Map<String, Account> accounts = new HashMap<>();
|
||||
|
||||
{
|
||||
for (Map<Object, Object> settings : SETTINGS.getAccounts()) {
|
||||
Optional<String> characterName = Accounts.getCurrentCharacter(settings);
|
||||
AccountFactory factory = Accounts.ACCOUNT_FACTORY.get(Lang.get(settings, "type", String.class, ""));
|
||||
if (factory == null || !characterName.isPresent()) {
|
||||
loadProxy();
|
||||
|
||||
for (Iterator<Map<Object, Object>> iterator = SETTINGS.getAccounts().iterator(); iterator.hasNext(); ) {
|
||||
Map<Object, Object> settings = iterator.next();
|
||||
AccountFactory<?> factory = Accounts.ACCOUNT_FACTORY.get(Lang.get(settings, "type", String.class).orElse(""));
|
||||
if (factory == null) {
|
||||
// unrecognized account type, so remove it.
|
||||
SETTINGS.getAccounts().remove(settings);
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
Account account;
|
||||
try {
|
||||
account = factory.fromStorage(settings);
|
||||
account = factory.fromStorage(settings, getProxy());
|
||||
} catch (Exception e) {
|
||||
SETTINGS.getAccounts().remove(settings);
|
||||
// storage is malformed, delete.
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
accounts.put(Accounts.getAccountId(account), account);
|
||||
}
|
||||
|
||||
save();
|
||||
|
||||
checkAuthlibInjectorServerURLs();
|
||||
checkAuthlibInjectorAccounts();
|
||||
checkProfileMap();
|
||||
|
||||
save();
|
||||
|
||||
for (Map.Entry<String, Profile> entry2 : getProfileMap().entrySet()) {
|
||||
entry2.getValue().setName(entry2.getKey());
|
||||
entry2.getValue().nameProperty().setChangedListener(this::profileNameChanged);
|
||||
entry2.getValue().addPropertyChangedListener(e -> {
|
||||
save();
|
||||
});
|
||||
entry2.getValue().addPropertyChangedListener(e -> save());
|
||||
}
|
||||
|
||||
Lang.ignoringException(() -> {
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(this::save));
|
||||
});
|
||||
|
||||
loadProxy();
|
||||
Lang.ignoringException(() -> Runtime.getRuntime().addShutdownHook(new Thread(this::save)));
|
||||
}
|
||||
|
||||
private Config initSettings() {
|
||||
@@ -124,11 +119,6 @@ public class Settings {
|
||||
} catch (Exception e) {
|
||||
Logging.LOG.log(Level.WARNING, "Something happened wrongly when load settings.", e);
|
||||
}
|
||||
else {
|
||||
Logging.LOG.config("No settings file here, may be first loading.");
|
||||
if (!c.getConfigurations().containsKey(HOME_PROFILE))
|
||||
c.getConfigurations().put(HOME_PROFILE, new Profile(HOME_PROFILE, Main.MINECRAFT_DIRECTORY));
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -229,22 +219,27 @@ public class Settings {
|
||||
SETTINGS.setProxyPass(proxyPass);
|
||||
}
|
||||
|
||||
public boolean hasProxy() { return SETTINGS.isHasProxy(); }
|
||||
|
||||
public void setHasProxy(boolean hasProxy) { SETTINGS.setHasProxy(hasProxy); }
|
||||
|
||||
public boolean hasProxyAuth() { return SETTINGS.isHasProxyAuth(); }
|
||||
|
||||
public void setHasProxyAuth(boolean hasProxyAuth) { SETTINGS.setHasProxyAuth(hasProxyAuth); }
|
||||
|
||||
private void loadProxy() {
|
||||
String host = getProxyHost();
|
||||
Integer port = Lang.toIntOrNull(getProxyPort());
|
||||
if (StringUtils.isBlank(host) || port == null)
|
||||
if (!hasProxy() || StringUtils.isBlank(host) || port == null || getProxyType() == Proxy.Type.DIRECT)
|
||||
proxy = Proxy.NO_PROXY;
|
||||
else {
|
||||
System.setProperty("http.proxyHost", getProxyHost());
|
||||
System.setProperty("http.proxyPort", getProxyPort());
|
||||
if (getProxyType() == Proxy.Type.DIRECT)
|
||||
proxy = Proxy.NO_PROXY;
|
||||
else
|
||||
proxy = new Proxy(proxyType, new InetSocketAddress(host, port));
|
||||
proxy = new Proxy(proxyType, new InetSocketAddress(host, port));
|
||||
|
||||
String user = getProxyUser();
|
||||
String pass = getProxyPass();
|
||||
if (StringUtils.isNotBlank(user) && StringUtils.isNotBlank(pass)) {
|
||||
if (hasProxyAuth() && StringUtils.isNotBlank(user) && StringUtils.isNotBlank(pass)) {
|
||||
System.setProperty("http.proxyUser", user);
|
||||
System.setProperty("http.proxyPassword", pass);
|
||||
|
||||
@@ -275,24 +270,58 @@ public class Settings {
|
||||
SETTINGS.setLogLines(logLines);
|
||||
}
|
||||
|
||||
public DownloadProvider getDownloadProvider() {
|
||||
switch (SETTINGS.getDownloadType()) {
|
||||
case 0:
|
||||
return MojangDownloadProvider.INSTANCE;
|
||||
case 1:
|
||||
return BMCLAPIDownloadProvider.INSTANCE;
|
||||
default:
|
||||
return MojangDownloadProvider.INSTANCE;
|
||||
/****************************************
|
||||
* AUTHLIB INJECTORS *
|
||||
****************************************/
|
||||
|
||||
public Set<String> getAuthlibInjectorServerURLs() {
|
||||
return SETTINGS.getAuthlibInjectorServerURLs();
|
||||
}
|
||||
|
||||
public void removeAuthlibInjectorServerURL(String serverURL) {
|
||||
checkAuthlibInjectorServerURLs();
|
||||
|
||||
SETTINGS.getAuthlibInjectorServerURLs().remove(serverURL);
|
||||
|
||||
checkAuthlibInjectorAccounts();
|
||||
save();
|
||||
}
|
||||
|
||||
public void addAuthlibInjectorServerURL(String serverURL) {
|
||||
checkAuthlibInjectorServerURLs();
|
||||
|
||||
SETTINGS.getAuthlibInjectorServerURLs().add(serverURL);
|
||||
save();
|
||||
}
|
||||
|
||||
private void checkAuthlibInjectorServerURLs() {
|
||||
if (SETTINGS.getAuthlibInjectorServerURLs() == null)
|
||||
SETTINGS.setAuthlibInjectorServerURLs(new HashSet<>());
|
||||
}
|
||||
|
||||
private void checkAuthlibInjectorAccounts() {
|
||||
for (Account account : getAccounts()) {
|
||||
if (account instanceof AuthlibInjectorAccount) {
|
||||
AuthlibInjectorAccount injectorAccount = (AuthlibInjectorAccount) account;
|
||||
if (!SETTINGS.getAuthlibInjectorServerURLs().contains(injectorAccount.getServerBaseURL()))
|
||||
deleteAccount(account);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************
|
||||
* DOWNLOAD PROVIDERS *
|
||||
****************************************/
|
||||
|
||||
public DownloadProvider getDownloadProvider() {
|
||||
return DownloadProviders.getDownloadProvider(SETTINGS.getDownloadType());
|
||||
}
|
||||
|
||||
public void setDownloadProvider(DownloadProvider downloadProvider) {
|
||||
if (downloadProvider == MojangDownloadProvider.INSTANCE)
|
||||
SETTINGS.setDownloadType(0);
|
||||
else if (downloadProvider == BMCLAPIDownloadProvider.INSTANCE)
|
||||
SETTINGS.setDownloadType(1);
|
||||
else
|
||||
int index = DownloadProviders.DOWNLOAD_PROVIDERS.indexOf(downloadProvider);
|
||||
if (index == -1)
|
||||
throw new IllegalArgumentException("Unknown download provider: " + downloadProvider);
|
||||
SETTINGS.setDownloadType(index);
|
||||
}
|
||||
|
||||
/****************************************
|
||||
@@ -416,7 +445,7 @@ public class Settings {
|
||||
public void invalidated() {
|
||||
super.invalidated();
|
||||
|
||||
SETTINGS.setTheme(get().name().toLowerCase());
|
||||
SETTINGS.setTheme(get().getName().toLowerCase());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -436,8 +465,6 @@ public class Settings {
|
||||
* PROFILES *
|
||||
****************************************/
|
||||
|
||||
private Profile selectedProfile;
|
||||
|
||||
public Profile getSelectedProfile() {
|
||||
checkProfileMap();
|
||||
|
||||
@@ -463,7 +490,7 @@ public class Settings {
|
||||
}
|
||||
|
||||
public boolean hasProfile(String name) {
|
||||
return getProfileMap().containsKey(Lang.nonNull(name, DEFAULT_PROFILE));
|
||||
return getProfileMap().containsKey(name);
|
||||
}
|
||||
|
||||
public Map<String, Profile> getProfileMap() {
|
||||
@@ -481,6 +508,8 @@ public class Settings {
|
||||
getProfileMap().put(ver.getName(), ver);
|
||||
|
||||
ver.nameProperty().setChangedListener(this::profileNameChanged);
|
||||
|
||||
save();
|
||||
}
|
||||
|
||||
public void deleteProfile(Profile profile) {
|
||||
@@ -494,13 +523,15 @@ public class Settings {
|
||||
}
|
||||
|
||||
private void checkProfileMap() {
|
||||
if (getProfileMap().isEmpty())
|
||||
if (getProfileMap().isEmpty()) {
|
||||
getProfileMap().put(DEFAULT_PROFILE, new Profile(DEFAULT_PROFILE));
|
||||
getProfileMap().put(HOME_PROFILE, new Profile(HOME_PROFILE, Main.MINECRAFT_DIRECTORY));
|
||||
}
|
||||
}
|
||||
|
||||
private void onProfileChanged() {
|
||||
getSelectedProfile().getRepository().refreshVersionsAsync().start();
|
||||
EventBus.EVENT_BUS.fireEvent(new ProfileChangedEvent(SETTINGS, getSelectedProfile()));
|
||||
getSelectedProfile().getRepository().refreshVersionsAsync().subscribe(() ->
|
||||
EventBus.EVENT_BUS.fireEvent(new ProfileChangedEvent(SETTINGS, getSelectedProfile())));
|
||||
}
|
||||
|
||||
private void profileNameChanged(ObservableValue<? extends String> observableValue, String oldValue, String newValue) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -17,29 +17,114 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.setting;
|
||||
|
||||
import java.util.Optional;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.ObjectBinding;
|
||||
import javafx.scene.paint.Color;
|
||||
import org.jackhuang.hmcl.util.FileUtils;
|
||||
import org.jackhuang.hmcl.util.IOUtils;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
|
||||
public enum Theme {
|
||||
BLUE,
|
||||
DARK_BLUE,
|
||||
GREEN,
|
||||
ORANGE,
|
||||
PURPLE,
|
||||
RED;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class Theme {
|
||||
public static final Theme BLUE = new Theme("blue", "#5C6BC0");
|
||||
public static final Theme DARK_BLUE = new Theme("dark_blue", "#283593");
|
||||
public static final Theme GREEN = new Theme("green", "#43A047");
|
||||
public static final Theme ORANGE = new Theme("orange", "#E67E22");
|
||||
public static final Theme PURPLE = new Theme("purple", "#9C27B0");
|
||||
public static final Theme RED = new Theme("red", "#F44336");
|
||||
|
||||
public static final Theme[] VALUES = new Theme[]{BLUE, DARK_BLUE, GREEN, ORANGE, PURPLE, RED};
|
||||
|
||||
private final String color;
|
||||
private final String name;
|
||||
|
||||
Theme(String name, String color) {
|
||||
this.name = name;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public boolean isCustom() {
|
||||
return name.startsWith("#");
|
||||
}
|
||||
|
||||
public boolean isLight() {
|
||||
return Color.web(color).grayscale().getRed() >= 0.5;
|
||||
}
|
||||
|
||||
public Color getForegroundColor() {
|
||||
return isLight() ? Color.BLACK : Color.WHITE;
|
||||
}
|
||||
|
||||
public String[] getStylesheets() {
|
||||
String name = isCustom() ? BLUE.getName() : this.name;
|
||||
String css = Theme.class.getResource("/assets/css/" + name + ".css").toExternalForm();
|
||||
try {
|
||||
File temp = File.createTempFile("hmcl", ".css");
|
||||
FileUtils.writeText(temp, IOUtils.readFullyAsString(Theme.class.getResourceAsStream("/assets/css/custom.css"))
|
||||
.replace("%base-color%", color)
|
||||
.replace("%font-color%", getColorDisplayName(getForegroundColor())));
|
||||
css = temp.toURI().toString();
|
||||
} catch (IOException e) {
|
||||
Logging.LOG.log(Level.SEVERE, "Unable to create theme stylesheet", e);
|
||||
}
|
||||
|
||||
return new String[]{
|
||||
Theme.class.getResource("/css/jfoenix-fonts.css").toExternalForm(),
|
||||
Theme.class.getResource("/css/jfoenix-design.css").toExternalForm(),
|
||||
Theme.class.getResource("/assets/css/" + name().toLowerCase() + ".css").toExternalForm(),
|
||||
css,
|
||||
Theme.class.getResource("/assets/css/root.css").toExternalForm()
|
||||
};
|
||||
}
|
||||
|
||||
public static Theme custom(String color) {
|
||||
if (!color.startsWith("#"))
|
||||
throw new IllegalArgumentException();
|
||||
return new Theme(color, color);
|
||||
}
|
||||
|
||||
public static Optional<Theme> getTheme(String name) {
|
||||
for (Theme theme : values())
|
||||
if (theme.name().equalsIgnoreCase(name))
|
||||
if (name == null)
|
||||
return Optional.empty();
|
||||
|
||||
for (Theme theme : VALUES)
|
||||
if (theme.name.equalsIgnoreCase(name))
|
||||
return Optional.of(theme);
|
||||
|
||||
if (name.startsWith("#"))
|
||||
try {
|
||||
Color.web(name);
|
||||
return Optional.of(custom(name));
|
||||
} catch (IllegalArgumentException ignore) {
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public static String getColorDisplayName(Color c) {
|
||||
return c != null ? String.format("#%02x%02x%02x", Math.round(c.getRed() * 255.0D), Math.round(c.getGreen() * 255.0D), Math.round(c.getBlue() * 255.0D)).toUpperCase() : null;
|
||||
}
|
||||
|
||||
public static ObjectBinding<Color> foregroundFillBinding() {
|
||||
return Bindings.createObjectBinding(() -> Settings.INSTANCE.getTheme().getForegroundColor(), Settings.INSTANCE.themeProperty());
|
||||
}
|
||||
|
||||
public static ObjectBinding<Color> blackFillBinding() {
|
||||
return Bindings.createObjectBinding(() -> Color.BLACK);
|
||||
}
|
||||
|
||||
public static ObjectBinding<Color> whiteFillBinding() {
|
||||
return Bindings.createObjectBinding(() -> Color.WHITE);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2013 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -138,7 +138,7 @@ public final class VersionSetting {
|
||||
permSizeProperty.set(permSize);
|
||||
}
|
||||
|
||||
private final ImmediateIntegerProperty maxMemoryProperty = new ImmediateIntegerProperty(this, "maxMemory", (int) OperatingSystem.SUGGESTED_MEMORY);
|
||||
private final ImmediateIntegerProperty maxMemoryProperty = new ImmediateIntegerProperty(this, "maxMemory", OperatingSystem.SUGGESTED_MEMORY);
|
||||
|
||||
public ImmediateIntegerProperty maxMemoryProperty() {
|
||||
return maxMemoryProperty;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -35,12 +35,15 @@ import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.paint.Color;
|
||||
import org.jackhuang.hmcl.auth.Account;
|
||||
import org.jackhuang.hmcl.auth.OfflineAccount;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
|
||||
import org.jackhuang.hmcl.auth.offline.OfflineAccount;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
|
||||
import org.jackhuang.hmcl.game.AccountHelper;
|
||||
import org.jackhuang.hmcl.setting.Accounts;
|
||||
import org.jackhuang.hmcl.setting.Settings;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
|
||||
public final class AccountItem extends StackPane {
|
||||
|
||||
@@ -56,6 +59,7 @@ public final class AccountItem extends StackPane {
|
||||
@FXML private Label lblUser;
|
||||
@FXML private Label lblType;
|
||||
@FXML private Label lblEmail;
|
||||
@FXML private Label lblServer;
|
||||
@FXML private Label lblCurrentAccount;
|
||||
@FXML private JFXRadioButton chkSelected;
|
||||
@FXML private JFXProgressBar pgsSkin;
|
||||
@@ -67,29 +71,27 @@ public final class AccountItem extends StackPane {
|
||||
|
||||
FXUtils.loadFXML(this, "/assets/fxml/account-item.fxml");
|
||||
|
||||
FXUtils.limitWidth(this, 160);
|
||||
FXUtils.limitHeight(this, 156);
|
||||
|
||||
setEffect(new DropShadow(BlurType.GAUSSIAN, Color.rgb(0, 0, 0, 0.26), 5.0, 0.12, -0.5, 1.0));
|
||||
|
||||
chkSelected.setToggleGroup(toggleGroup);
|
||||
btnDelete.setGraphic(SVG.delete("black", 15, 15));
|
||||
btnRefresh.setGraphic(SVG.refresh("black", 15, 15));
|
||||
|
||||
// create content
|
||||
String headerColor = getDefaultColor(i % 12);
|
||||
header.setStyle("-fx-background-radius: 2 2 0 0; -fx-background-color: " + headerColor);
|
||||
btnDelete.setGraphic(SVG.delete(Theme.blackFillBinding(), 15, 15));
|
||||
btnRefresh.setGraphic(SVG.refresh(Theme.blackFillBinding(), 15, 15));
|
||||
|
||||
// create image view
|
||||
icon.translateYProperty().bind(Bindings.createDoubleBinding(() -> header.getBoundsInParent().getHeight() - icon.getHeight() / 2 - 32.0, header.boundsInParentProperty(), icon.heightProperty()));
|
||||
icon.translateYProperty().bind(Bindings.createDoubleBinding(() -> header.getBoundsInParent().getHeight() - icon.getHeight() / 2 - 16, header.boundsInParentProperty(), icon.heightProperty()));
|
||||
|
||||
chkSelected.getProperties().put("account", account);
|
||||
setSelected(Settings.INSTANCE.getSelectedAccount() == account);
|
||||
|
||||
lblUser.setText(Accounts.getCurrentCharacter(account));
|
||||
lblUser.setText(account.getCharacter());
|
||||
lblType.setText(AccountsPage.accountType(account));
|
||||
lblEmail.setText(account.getUsername());
|
||||
|
||||
if (account instanceof AuthlibInjectorAccount) {
|
||||
Task.ofResult("serverName", () -> Accounts.getAuthlibInjectorServerName(((AuthlibInjectorAccount) account).getServerBaseURL()))
|
||||
.subscribe(Schedulers.javafx(), variables -> lblServer.setText(variables.get("serverName")));
|
||||
}
|
||||
|
||||
if (account instanceof YggdrasilAccount) {
|
||||
btnRefresh.setOnMouseClicked(e -> {
|
||||
pgsSkin.setVisible(true);
|
||||
@@ -116,25 +118,6 @@ public final class AccountItem extends StackPane {
|
||||
FXUtils.limitSize(portraitView, 32, 32);
|
||||
}
|
||||
|
||||
private String getDefaultColor(int i) {
|
||||
switch (i) {
|
||||
case 0: return "#8F3F7E";
|
||||
case 1: return "#B5305F";
|
||||
case 2: return "#CE584A";
|
||||
case 3: return "#DB8D5C";
|
||||
case 4: return "#DA854E";
|
||||
case 5: return "#E9AB44";
|
||||
case 6: return "#FEE435";
|
||||
case 7: return "#99C286";
|
||||
case 8: return "#01A05E";
|
||||
case 9: return "#4A8895";
|
||||
case 10: return "#16669B";
|
||||
case 11: return "#2F65A5";
|
||||
case 12: return "#4E6A9C";
|
||||
default: return "#FFFFFF";
|
||||
}
|
||||
}
|
||||
|
||||
public Account getAccount() {
|
||||
return account;
|
||||
}
|
||||
|
||||
+10
-18
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -23,22 +23,16 @@ import com.jfoenix.controls.JFXProgressBar;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.auth.Account;
|
||||
import org.jackhuang.hmcl.auth.AuthInfo;
|
||||
import org.jackhuang.hmcl.auth.SpecificCharacterSelector;
|
||||
import org.jackhuang.hmcl.auth.InvalidCredentialsException;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccountFactory;
|
||||
import org.jackhuang.hmcl.setting.Accounts;
|
||||
import org.jackhuang.hmcl.setting.Settings;
|
||||
import org.jackhuang.hmcl.auth.NoSelectedCharacterException;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class YggdrasilAccountLoginPane extends StackPane {
|
||||
private final YggdrasilAccount oldAccount;
|
||||
public class AccountLoginPane extends StackPane {
|
||||
private final Account oldAccount;
|
||||
private final Consumer<AuthInfo> success;
|
||||
private final Runnable failed;
|
||||
|
||||
@@ -49,12 +43,12 @@ public class YggdrasilAccountLoginPane extends StackPane {
|
||||
@FXML private JFXProgressBar progressBar;
|
||||
private JFXDialog dialog;
|
||||
|
||||
public YggdrasilAccountLoginPane(YggdrasilAccount oldAccount, Consumer<AuthInfo> success, Runnable failed) {
|
||||
public AccountLoginPane(Account oldAccount, Consumer<AuthInfo> success, Runnable failed) {
|
||||
this.oldAccount = oldAccount;
|
||||
this.success = success;
|
||||
this.failed = failed;
|
||||
|
||||
FXUtils.loadFXML(this, "/assets/fxml/yggdrasil-account-login.fxml");
|
||||
FXUtils.loadFXML(this, "/assets/fxml/account-login.fxml");
|
||||
|
||||
lblUsername.setText(oldAccount.getUsername());
|
||||
txtPassword.setOnAction(e -> onAccept());
|
||||
@@ -62,14 +56,12 @@ public class YggdrasilAccountLoginPane extends StackPane {
|
||||
|
||||
@FXML
|
||||
private void onAccept() {
|
||||
String username = oldAccount.getUsername();
|
||||
String password = txtPassword.getText();
|
||||
progressBar.setVisible(true);
|
||||
lblCreationWarning.setText("");
|
||||
Task.ofResult("login", () -> {
|
||||
try {
|
||||
Account account = YggdrasilAccountFactory.INSTANCE.fromUsername(username, password);
|
||||
return account.logIn(new SpecificCharacterSelector(Accounts.getCurrentCharacter(oldAccount)), Settings.INSTANCE.getProxy());
|
||||
return oldAccount.logInWithPassword(password);
|
||||
} catch (Exception e) {
|
||||
return e;
|
||||
}
|
||||
@@ -78,10 +70,10 @@ public class YggdrasilAccountLoginPane extends StackPane {
|
||||
if (account instanceof AuthInfo) {
|
||||
success.accept(((AuthInfo) account));
|
||||
dialog.close();
|
||||
} else if (account instanceof InvalidCredentialsException) {
|
||||
lblCreationWarning.setText(Main.i18n("account.failed.wrong_password"));
|
||||
} else if (account instanceof NoSelectedCharacterException) {
|
||||
dialog.close();
|
||||
} else if (account instanceof Exception) {
|
||||
lblCreationWarning.setText(account.getClass().toString() + ": " + ((Exception) account).getLocalizedMessage());
|
||||
lblCreationWarning.setText(AccountsPage.accountException((Exception) account));
|
||||
}
|
||||
|
||||
progressBar.setVisible(false);
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -25,6 +25,7 @@ import javafx.beans.property.StringProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Hyperlink;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.control.ToggleGroup;
|
||||
@@ -35,10 +36,10 @@ import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.auth.*;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
|
||||
import org.jackhuang.hmcl.auth.offline.OfflineAccount;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.GameProfile;
|
||||
import org.jackhuang.hmcl.auth.InvalidCredentialsException;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccountFactory;
|
||||
import org.jackhuang.hmcl.game.AccountHelper;
|
||||
import org.jackhuang.hmcl.setting.Accounts;
|
||||
import org.jackhuang.hmcl.setting.Settings;
|
||||
@@ -49,9 +50,12 @@ import org.jackhuang.hmcl.ui.construct.IconedItem;
|
||||
import org.jackhuang.hmcl.ui.construct.Validator;
|
||||
import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class AccountsPage extends StackPane implements DecoratorPage {
|
||||
private final StringProperty title = new SimpleStringProperty(this, "title", Main.i18n("account"));
|
||||
@@ -63,8 +67,12 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
|
||||
@FXML private JFXTextField txtUsername;
|
||||
@FXML private JFXPasswordField txtPassword;
|
||||
@FXML private Label lblCreationWarning;
|
||||
@FXML private Label lblPassword;
|
||||
@FXML private JFXComboBox<String> cboType;
|
||||
@FXML private JFXComboBox<TwoLineListItem> cboServers;
|
||||
@FXML private JFXProgressBar progressBar;
|
||||
@FXML private Label lblAddInjectorServer;
|
||||
@FXML private Hyperlink linkAddInjectorServer;
|
||||
|
||||
{
|
||||
FXUtils.loadFXML(this, "/assets/fxml/account.fxml");
|
||||
@@ -73,15 +81,21 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
|
||||
dialog.setDialogContainer(this);
|
||||
|
||||
FXUtils.smoothScrolling(scrollPane);
|
||||
FXUtils.setValidateWhileTextChanged(txtUsername);
|
||||
FXUtils.setValidateWhileTextChanged(txtPassword);
|
||||
|
||||
cboType.getItems().setAll(Main.i18n("account.methods.offline"), Main.i18n("account.methods.yggdrasil"));
|
||||
cboType.getItems().setAll(Main.i18n("account.methods.offline"), Main.i18n("account.methods.yggdrasil"), Main.i18n("account.methods.authlib_injector"));
|
||||
cboType.getSelectionModel().selectedIndexProperty().addListener((a, b, newValue) -> {
|
||||
txtPassword.setVisible(newValue.intValue() != 0);
|
||||
lblPassword.setVisible(newValue.intValue() != 0);
|
||||
cboServers.setVisible(newValue.intValue() == 2);
|
||||
linkAddInjectorServer.setVisible(newValue.intValue() == 2);
|
||||
lblAddInjectorServer.setVisible(newValue.intValue() == 2);
|
||||
});
|
||||
cboType.getSelectionModel().select(0);
|
||||
|
||||
// These two lines can eliminate black, don't know why.
|
||||
cboServers.getItems().setAll(new TwoLineListItem("", ""));
|
||||
cboServers.getSelectionModel().select(0);
|
||||
|
||||
txtPassword.setOnAction(e -> onCreationAccept());
|
||||
txtUsername.setOnAction(e -> onCreationAccept());
|
||||
txtUsername.getValidators().add(new Validator(Main.i18n("input.email"), str -> !txtPassword.isVisible() || str.contains("@")));
|
||||
@@ -93,6 +107,7 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
|
||||
});
|
||||
|
||||
loadAccounts();
|
||||
loadServers();
|
||||
|
||||
if (Settings.INSTANCE.getAccounts().isEmpty())
|
||||
addNewAccount();
|
||||
@@ -116,6 +131,17 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
|
||||
});
|
||||
}
|
||||
|
||||
public void loadServers() {
|
||||
Task.ofResult("list", () -> Settings.INSTANCE.getAuthlibInjectorServerURLs().parallelStream()
|
||||
.map(serverURL -> new TwoLineListItem(Accounts.getAuthlibInjectorServerName(serverURL), serverURL))
|
||||
.collect(Collectors.toList()))
|
||||
.subscribe(Task.of(Schedulers.javafx(), variables -> {
|
||||
cboServers.getItems().setAll(variables.<Collection<TwoLineListItem>>get("list"));
|
||||
if (!cboServers.getItems().isEmpty())
|
||||
cboServers.getSelectionModel().select(0);
|
||||
}));
|
||||
}
|
||||
|
||||
private Node buildNode(int i, Account account, ToggleGroup group) {
|
||||
AccountItem item = new AccountItem(i, account, group);
|
||||
item.setOnDeleteButtonMouseClicked(e -> {
|
||||
@@ -129,54 +155,46 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
|
||||
private void addNewAccount() {
|
||||
txtUsername.setText("");
|
||||
txtPassword.setText("");
|
||||
lblCreationWarning.setText("");
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onAddInjecterServer() {
|
||||
Controllers.navigate(Controllers.getServersPage());
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onCreationAccept() {
|
||||
int type = cboType.getSelectionModel().getSelectedIndex();
|
||||
String username = txtUsername.getText();
|
||||
String password = txtPassword.getText();
|
||||
String apiRoot = Optional.ofNullable(cboServers.getSelectionModel().getSelectedItem()).map(TwoLineListItem::getSubtitle).orElse(null);
|
||||
progressBar.setVisible(true);
|
||||
lblCreationWarning.setText("");
|
||||
Task.ofResult("create_account", () -> {
|
||||
try {
|
||||
Account account;
|
||||
AccountFactory<?> factory;
|
||||
switch (type) {
|
||||
case 0: account = OfflineAccountFactory.INSTANCE.fromUsername(username); break;
|
||||
case 1: account = YggdrasilAccountFactory.INSTANCE.fromUsername(username, password); break;
|
||||
case 0: factory = Accounts.ACCOUNT_FACTORY.get(Accounts.OFFLINE_ACCOUNT_KEY); break;
|
||||
case 1: factory = Accounts.ACCOUNT_FACTORY.get(Accounts.YGGDRASIL_ACCOUNT_KEY); break;
|
||||
case 2: factory = Accounts.ACCOUNT_FACTORY.get(Accounts.AUTHLIB_INJECTOR_ACCOUNT_KEY); break;
|
||||
default: throw new Error();
|
||||
}
|
||||
|
||||
AuthInfo info = account.logIn(new CharacterSelector(), Settings.INSTANCE.getProxy());
|
||||
Accounts.setCurrentCharacter(account, info.getUsername());
|
||||
return account;
|
||||
} catch (Exception e) {
|
||||
return e;
|
||||
}
|
||||
}).subscribe(Schedulers.javafx(), variables -> {
|
||||
Object account = variables.get("create_account");
|
||||
if (account instanceof Account) {
|
||||
Settings.INSTANCE.addAccount((Account) account);
|
||||
return factory.create(new Selector(), username, password, apiRoot, Settings.INSTANCE.getProxy());
|
||||
}).finalized(Schedulers.javafx(), variables -> {
|
||||
Settings.INSTANCE.addAccount(variables.get("create_account"));
|
||||
dialog.close();
|
||||
loadAccounts();
|
||||
progressBar.setVisible(false);
|
||||
}, exception -> {
|
||||
if (exception instanceof NoSelectedCharacterException) {
|
||||
dialog.close();
|
||||
loadAccounts();
|
||||
} else if (account instanceof InvalidCredentialsException) {
|
||||
lblCreationWarning.setText(Main.i18n("account.failed.wrong_password"));
|
||||
} else if (account instanceof NoCharacterException) {
|
||||
lblCreationWarning.setText(Main.i18n("account.failed.no_charactor"));
|
||||
} else if (account instanceof ServerDisconnectException) {
|
||||
lblCreationWarning.setText(Main.i18n("account.failed.connect_authentication_server"));
|
||||
} else if (account instanceof InvalidTokenException) {
|
||||
lblCreationWarning.setText(Main.i18n("account.failed.invalid_token"));
|
||||
} else if (account instanceof InvalidPasswordException) {
|
||||
lblCreationWarning.setText(Main.i18n("account.failed.invalid_password"));
|
||||
} else if (account instanceof NoSelectedCharacterException) {
|
||||
dialog.close();
|
||||
} else if (account instanceof Exception) {
|
||||
lblCreationWarning.setText(account.getClass() + ": " + ((Exception) account).getLocalizedMessage());
|
||||
} else {
|
||||
lblCreationWarning.setText(accountException(exception));
|
||||
}
|
||||
progressBar.setVisible(false);
|
||||
});
|
||||
}).start();
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -197,17 +215,34 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
|
||||
this.title.set(title);
|
||||
}
|
||||
|
||||
public static String accountException(Exception exception) {
|
||||
if (exception instanceof InvalidCredentialsException) {
|
||||
return Main.i18n("account.failed.invalid_credentials");
|
||||
} else if (exception instanceof NoCharacterException) {
|
||||
return Main.i18n("account.failed.no_charactor");
|
||||
} else if (exception instanceof ServerDisconnectException) {
|
||||
return Main.i18n("account.failed.connect_authentication_server");
|
||||
} else if (exception instanceof InvalidTokenException) {
|
||||
return Main.i18n("account.failed.invalid_token");
|
||||
} else if (exception instanceof InvalidPasswordException) {
|
||||
return Main.i18n("account.failed.invalid_password");
|
||||
} else {
|
||||
return exception.getClass() + ": " + exception.getLocalizedMessage();
|
||||
}
|
||||
}
|
||||
|
||||
public static String accountType(Account account) {
|
||||
if (account instanceof OfflineAccount) return Main.i18n("account.methods.offline");
|
||||
else if (account instanceof AuthlibInjectorAccount) return Main.i18n("account.methods.authlib_injector");
|
||||
else if (account instanceof YggdrasilAccount) return Main.i18n("account.methods.yggdrasil");
|
||||
else throw new Error(Main.i18n("account.methods.no_method") + ": " + account);
|
||||
}
|
||||
|
||||
class CharacterSelector extends BorderPane implements MultiCharacterSelector {
|
||||
private AdvancedListBox listBox = new AdvancedListBox();
|
||||
private JFXButton cancel = new JFXButton();
|
||||
private static class Selector extends BorderPane implements CharacterSelector {
|
||||
private final AdvancedListBox listBox = new AdvancedListBox();
|
||||
private final JFXButton cancel = new JFXButton();
|
||||
|
||||
private CountDownLatch latch = new CountDownLatch(1);
|
||||
private final CountDownLatch latch = new CountDownLatch(1);
|
||||
private GameProfile selectedProfile = null;
|
||||
|
||||
{
|
||||
@@ -230,7 +265,7 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
|
||||
@Override
|
||||
public GameProfile select(Account account, List<GameProfile> names) throws NoSelectedCharacterException {
|
||||
if (!(account instanceof YggdrasilAccount))
|
||||
return MultiCharacterSelector.DEFAULT.select(account, names);
|
||||
return CharacterSelector.DEFAULT.select(account, names);
|
||||
YggdrasilAccount yggdrasilAccount = (YggdrasilAccount) account;
|
||||
|
||||
for (GameProfile profile : names) {
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2018 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;
|
||||
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import com.jfoenix.effects.JFXDepthManager;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServerInfo;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public final class AuthlibInjectorServerItem extends BorderPane {
|
||||
private final AuthlibInjectorServerInfo info;
|
||||
|
||||
private final Label lblServerName = new Label();
|
||||
private final Label lblServerIp = new Label();
|
||||
|
||||
public AuthlibInjectorServerItem(AuthlibInjectorServerInfo info, Consumer<AuthlibInjectorServerItem> deleteCallback) {
|
||||
this.info = info;
|
||||
|
||||
lblServerName.setStyle("-fx-font-size: 15;");
|
||||
lblServerIp.setStyle("-fx-font-size: 10;");
|
||||
|
||||
VBox center = new VBox();
|
||||
BorderPane.setAlignment(center, Pos.CENTER);
|
||||
center.getChildren().addAll(lblServerName, lblServerIp);
|
||||
setCenter(center);
|
||||
|
||||
JFXButton right = new JFXButton();
|
||||
right.setOnMouseClicked(e -> deleteCallback.accept(this));
|
||||
right.getStyleClass().add("toggle-icon4");
|
||||
BorderPane.setAlignment(right, Pos.CENTER);
|
||||
right.setGraphic(SVG.close(Theme.blackFillBinding(), 15, 15));
|
||||
setRight(right);
|
||||
|
||||
setStyle("-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;");
|
||||
JFXDepthManager.setDepth(this, 1);
|
||||
lblServerName.setText(info.getServerName());
|
||||
lblServerIp.setText(info.getServerIp());
|
||||
}
|
||||
|
||||
public AuthlibInjectorServerInfo getInfo() {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
package org.jackhuang.hmcl.ui;
|
||||
|
||||
import com.jfoenix.controls.*;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServerInfo;
|
||||
import org.jackhuang.hmcl.setting.Accounts;
|
||||
import org.jackhuang.hmcl.setting.Settings;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
||||
import org.jackhuang.hmcl.ui.animation.TransitionHandler;
|
||||
import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
|
||||
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AuthlibInjectorServersPage extends StackPane implements DecoratorPage {
|
||||
private final StringProperty title = new SimpleStringProperty(this, "title", Main.i18n("account.injector.server"));
|
||||
|
||||
@FXML private ScrollPane scrollPane;
|
||||
@FXML private StackPane addServerContainer;
|
||||
@FXML private Label lblServerIp;
|
||||
@FXML private Label lblServerName;
|
||||
@FXML private Label lblCreationWarning;
|
||||
@FXML private Label lblServerWarning;
|
||||
@FXML private VBox listPane;
|
||||
@FXML private JFXTextField txtServerIp;
|
||||
@FXML private JFXDialogLayout addServerPane;
|
||||
@FXML private JFXDialogLayout confirmServerPane;
|
||||
@FXML private JFXDialog dialog;
|
||||
@FXML private StackPane contentPane;
|
||||
@FXML private JFXSpinner spinner;
|
||||
@FXML private JFXProgressBar progressBar;
|
||||
@FXML private JFXButton btnAddNext;
|
||||
|
||||
private final TransitionHandler transitionHandler;
|
||||
|
||||
{
|
||||
FXUtils.loadFXML(this, "/assets/fxml/authlib-injector-servers.fxml");
|
||||
FXUtils.smoothScrolling(scrollPane);
|
||||
transitionHandler = new TransitionHandler(addServerContainer);
|
||||
|
||||
getChildren().remove(dialog);
|
||||
dialog.setDialogContainer(this);
|
||||
|
||||
txtServerIp.textProperty().addListener((a, b, newValue) ->
|
||||
btnAddNext.setDisable(!txtServerIp.validate()));
|
||||
|
||||
loading();
|
||||
}
|
||||
|
||||
private void removeServer(AuthlibInjectorServerItem item) {
|
||||
Settings.INSTANCE.removeAuthlibInjectorServerURL(item.getInfo().getServerIp());
|
||||
loading();
|
||||
}
|
||||
|
||||
private void loading() {
|
||||
getChildren().remove(contentPane);
|
||||
spinner.setVisible(true);
|
||||
|
||||
Task.ofResult("list", () -> Settings.INSTANCE.getAuthlibInjectorServerURLs().parallelStream()
|
||||
.map(serverURL -> new AuthlibInjectorServerItem(new AuthlibInjectorServerInfo(serverURL, Accounts.getAuthlibInjectorServerName(serverURL)), this::removeServer))
|
||||
.collect(Collectors.toList()))
|
||||
.subscribe(Task.of(Schedulers.javafx(), variables -> {
|
||||
listPane.getChildren().setAll(variables.<Collection<? extends Node>>get("list"));
|
||||
loadingCompleted();
|
||||
}));
|
||||
}
|
||||
|
||||
private void loadingCompleted() {
|
||||
getChildren().add(contentPane);
|
||||
spinner.setVisible(false);
|
||||
|
||||
if (Settings.INSTANCE.getAuthlibInjectorServerURLs().isEmpty())
|
||||
onAdd();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onAdd() {
|
||||
transitionHandler.setContent(addServerPane, ContainerAnimations.NONE.getAnimationProducer());
|
||||
txtServerIp.setText("");
|
||||
addServerPane.setDisable(false);
|
||||
progressBar.setVisible(false);
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onAddCancel() {
|
||||
dialog.close();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onAddNext() {
|
||||
String serverIp = txtServerIp.getText();
|
||||
progressBar.setVisible(true);
|
||||
addServerPane.setDisable(true);
|
||||
|
||||
Task.ofResult("serverName", () -> Objects.requireNonNull(Accounts.getAuthlibInjectorServerName(serverIp)))
|
||||
.finalized(Schedulers.javafx(), (variables, isDependentsSucceeded) -> {
|
||||
progressBar.setVisible(false);
|
||||
addServerPane.setDisable(false);
|
||||
|
||||
if (isDependentsSucceeded) {
|
||||
lblServerName.setText(variables.get("serverName"));
|
||||
lblServerIp.setText(txtServerIp.getText());
|
||||
|
||||
lblServerWarning.setVisible("http".equals(NetworkUtils.toURL(serverIp).getProtocol()));
|
||||
|
||||
transitionHandler.setContent(confirmServerPane, ContainerAnimations.SWIPE_LEFT.getAnimationProducer());
|
||||
} else
|
||||
lblCreationWarning.setText(variables.<Exception>get("lastException").getLocalizedMessage());
|
||||
}).start();
|
||||
|
||||
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onAddPrev() {
|
||||
transitionHandler.setContent(addServerPane, ContainerAnimations.SWIPE_RIGHT.getAnimationProducer());
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onAddFinish() {
|
||||
String ip = txtServerIp.getText();
|
||||
if (!ip.endsWith("/"))
|
||||
ip += "/";
|
||||
Settings.INSTANCE.addAuthlibInjectorServerURL(ip);
|
||||
loading();
|
||||
dialog.close();
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringProperty titleProperty() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title.set(title);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -39,9 +39,10 @@ public final class Controllers {
|
||||
|
||||
private static Scene scene;
|
||||
private static Stage stage;
|
||||
private static MainPage mainPage = new MainPage();
|
||||
private static MainPage mainPage = null;
|
||||
private static SettingsPage settingsPage = null;
|
||||
private static VersionPage versionPage = null;
|
||||
private static AuthlibInjectorServersPage serversPage = null;
|
||||
private static LeftPaneController leftPaneController;
|
||||
private static Decorator decorator;
|
||||
|
||||
@@ -67,11 +68,21 @@ public final class Controllers {
|
||||
return versionPage;
|
||||
}
|
||||
|
||||
// FXThread
|
||||
public static AuthlibInjectorServersPage getServersPage() {
|
||||
if (serversPage == null)
|
||||
serversPage = new AuthlibInjectorServersPage();
|
||||
return serversPage;
|
||||
}
|
||||
|
||||
// FXThread
|
||||
public static Decorator getDecorator() {
|
||||
return decorator;
|
||||
}
|
||||
|
||||
public static MainPage getMainPage() {
|
||||
if (mainPage == null)
|
||||
mainPage = new MainPage();
|
||||
return mainPage;
|
||||
}
|
||||
|
||||
@@ -82,7 +93,7 @@ public final class Controllers {
|
||||
public static void initialize(Stage stage) {
|
||||
Controllers.stage = stage;
|
||||
|
||||
decorator = new Decorator(stage, mainPage, Main.TITLE, false, true);
|
||||
decorator = new Decorator(stage, getMainPage(), Main.TITLE, false, true);
|
||||
decorator.showPage(null);
|
||||
leftPaneController = new LeftPaneController(decorator.getLeftPane());
|
||||
|
||||
@@ -92,7 +103,7 @@ public final class Controllers {
|
||||
decorator.setCustomMaximize(false);
|
||||
|
||||
scene = new Scene(decorator, 804, 521);
|
||||
scene.getStylesheets().addAll(Settings.INSTANCE.getTheme().getStylesheets());
|
||||
scene.getStylesheets().setAll(Settings.INSTANCE.getTheme().getStylesheets());
|
||||
stage.setMinWidth(804);
|
||||
stage.setMaxWidth(804);
|
||||
stage.setMinHeight(521);
|
||||
@@ -159,4 +170,14 @@ public final class Controllers {
|
||||
public static void showUpdate() {
|
||||
getDecorator().showUpdate();
|
||||
}
|
||||
|
||||
public static void shutdown() {
|
||||
mainPage = null;
|
||||
settingsPage = null;
|
||||
versionPage = null;
|
||||
serversPage = null;
|
||||
decorator = null;
|
||||
stage = null;
|
||||
scene = null;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -34,20 +34,19 @@ import org.jackhuang.hmcl.Main;
|
||||
*/
|
||||
public class CrashWindow extends Stage {
|
||||
|
||||
private Label lblCrash = new Label();
|
||||
private Button btnContact = new Button();
|
||||
private TextArea textArea = new TextArea();
|
||||
|
||||
public CrashWindow(String text) {
|
||||
Label lblCrash = new Label();
|
||||
if (Main.UPDATE_CHECKER.isOutOfDate())
|
||||
lblCrash.setText(Main.i18n("launcher.crash_out_dated"));
|
||||
else
|
||||
lblCrash.setText(Main.i18n("launcher.crash"));
|
||||
lblCrash.setWrapText(true);
|
||||
|
||||
TextArea textArea = new TextArea();
|
||||
textArea.setText(text);
|
||||
textArea.setEditable(false);
|
||||
|
||||
Button btnContact = new Button();
|
||||
btnContact.setText(Main.i18n("launcher.contact"));
|
||||
btnContact.setOnMouseClicked(event -> FXUtils.openLink(Main.CONTACT));
|
||||
HBox box = new HBox();
|
||||
@@ -68,9 +67,7 @@ public class CrashWindow extends Stage {
|
||||
getIcons().add(new Image("/assets/img/icon.png"));
|
||||
setTitle(Main.i18n("message.error"));
|
||||
|
||||
setOnCloseRequest(e -> {
|
||||
System.exit(1);
|
||||
});
|
||||
setOnCloseRequest(e -> System.exit(1));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -21,7 +21,6 @@ import com.jfoenix.controls.JFXButton;
|
||||
import com.jfoenix.controls.JFXDialog;
|
||||
import com.jfoenix.controls.JFXDrawer;
|
||||
import com.jfoenix.controls.JFXHamburger;
|
||||
import com.jfoenix.effects.JFXDepthManager;
|
||||
import com.jfoenix.svg.SVGGlyph;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
@@ -49,6 +48,7 @@ import javafx.stage.StageStyle;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.setting.EnumBackgroundImage;
|
||||
import org.jackhuang.hmcl.setting.Settings;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
import org.jackhuang.hmcl.ui.animation.AnimationProducer;
|
||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
||||
import org.jackhuang.hmcl.ui.animation.TransitionHandler;
|
||||
@@ -60,8 +60,6 @@ import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
@@ -88,7 +86,7 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
|
||||
private double xOffset, yOffset, newX, newY, initX, initY;
|
||||
private boolean allowMove, isDragging, dialogShown, maximized;
|
||||
private BoundingBox originalBox, maximizedBox;
|
||||
private TransitionHandler animationHandler;
|
||||
private final TransitionHandler animationHandler;
|
||||
|
||||
@FXML
|
||||
private StackPane contentPlaceHolder;
|
||||
@@ -153,6 +151,15 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
|
||||
btnMin.setGraphic(minus);
|
||||
btnMax.setGraphic(resizeMax);
|
||||
|
||||
close.fillProperty().bind(Theme.foregroundFillBinding());
|
||||
minus.fillProperty().bind(Theme.foregroundFillBinding());
|
||||
resizeMax.fillProperty().bind(Theme.foregroundFillBinding());
|
||||
resizeMin.fillProperty().bind(Theme.foregroundFillBinding());
|
||||
|
||||
refreshNavButton.setGraphic(SVG.refresh(Theme.foregroundFillBinding(), 15, 15));
|
||||
closeNavButton.setGraphic(SVG.close(Theme.foregroundFillBinding(), 15, 15));
|
||||
backNavButton.setGraphic(SVG.back(Theme.foregroundFillBinding(), 15, 15));
|
||||
|
||||
lblTitle.setText(title);
|
||||
|
||||
buttonsContainer.setBackground(new Background(new BackgroundFill(Color.BLACK, CornerRadii.EMPTY, Insets.EMPTY)));
|
||||
@@ -180,9 +187,6 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
|
||||
|
||||
animationHandler = new TransitionHandler(contentPlaceHolder);
|
||||
|
||||
FXUtils.setOverflowHidden((Pane) lookup("#contentPlaceHolderRoot"));
|
||||
FXUtils.setOverflowHidden(drawerWrapper);
|
||||
|
||||
loadBackground();
|
||||
}
|
||||
|
||||
@@ -211,7 +215,7 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
|
||||
}
|
||||
}
|
||||
|
||||
// bgskin
|
||||
// images in ./bg
|
||||
if (!loaded) {
|
||||
File backgroundImageFile = new File("bg");
|
||||
if (backgroundImageFile.isDirectory()) {
|
||||
@@ -308,52 +312,52 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
|
||||
if (!this.primaryStage.isFullScreen() && !mouseEvent.isStillSincePress() && !this.primaryStage.isMaximized() && !this.maximized) {
|
||||
this.newX = mouseEvent.getScreenX();
|
||||
this.newY = mouseEvent.getScreenY();
|
||||
double deltax = this.newX - this.initX;
|
||||
double deltay = this.newY - this.initY;
|
||||
double deltaX = this.newX - this.initX;
|
||||
double deltaY = this.newY - this.initY;
|
||||
Cursor cursor = this.getCursor();
|
||||
if (Cursor.E_RESIZE == cursor) {
|
||||
this.setStageWidth(this.primaryStage.getWidth() + deltax);
|
||||
this.setStageWidth(this.primaryStage.getWidth() + deltaX);
|
||||
mouseEvent.consume();
|
||||
} else if (Cursor.NE_RESIZE == cursor) {
|
||||
if (this.setStageHeight(this.primaryStage.getHeight() - deltay)) {
|
||||
this.primaryStage.setY(this.primaryStage.getY() + deltay);
|
||||
if (this.setStageHeight(this.primaryStage.getHeight() - deltaY)) {
|
||||
this.primaryStage.setY(this.primaryStage.getY() + deltaY);
|
||||
}
|
||||
|
||||
this.setStageWidth(this.primaryStage.getWidth() + deltax);
|
||||
this.setStageWidth(this.primaryStage.getWidth() + deltaX);
|
||||
mouseEvent.consume();
|
||||
} else if (Cursor.SE_RESIZE == cursor) {
|
||||
this.setStageWidth(this.primaryStage.getWidth() + deltax);
|
||||
this.setStageHeight(this.primaryStage.getHeight() + deltay);
|
||||
this.setStageWidth(this.primaryStage.getWidth() + deltaX);
|
||||
this.setStageHeight(this.primaryStage.getHeight() + deltaY);
|
||||
mouseEvent.consume();
|
||||
} else if (Cursor.S_RESIZE == cursor) {
|
||||
this.setStageHeight(this.primaryStage.getHeight() + deltay);
|
||||
this.setStageHeight(this.primaryStage.getHeight() + deltaY);
|
||||
mouseEvent.consume();
|
||||
} else if (Cursor.W_RESIZE == cursor) {
|
||||
if (this.setStageWidth(this.primaryStage.getWidth() - deltax)) {
|
||||
this.primaryStage.setX(this.primaryStage.getX() + deltax);
|
||||
if (this.setStageWidth(this.primaryStage.getWidth() - deltaX)) {
|
||||
this.primaryStage.setX(this.primaryStage.getX() + deltaX);
|
||||
}
|
||||
|
||||
mouseEvent.consume();
|
||||
} else if (Cursor.SW_RESIZE == cursor) {
|
||||
if (this.setStageWidth(this.primaryStage.getWidth() - deltax)) {
|
||||
this.primaryStage.setX(this.primaryStage.getX() + deltax);
|
||||
if (this.setStageWidth(this.primaryStage.getWidth() - deltaX)) {
|
||||
this.primaryStage.setX(this.primaryStage.getX() + deltaX);
|
||||
}
|
||||
|
||||
this.setStageHeight(this.primaryStage.getHeight() + deltay);
|
||||
this.setStageHeight(this.primaryStage.getHeight() + deltaY);
|
||||
mouseEvent.consume();
|
||||
} else if (Cursor.NW_RESIZE == cursor) {
|
||||
if (this.setStageWidth(this.primaryStage.getWidth() - deltax)) {
|
||||
this.primaryStage.setX(this.primaryStage.getX() + deltax);
|
||||
if (this.setStageWidth(this.primaryStage.getWidth() - deltaX)) {
|
||||
this.primaryStage.setX(this.primaryStage.getX() + deltaX);
|
||||
}
|
||||
|
||||
if (this.setStageHeight(this.primaryStage.getHeight() - deltay)) {
|
||||
this.primaryStage.setY(this.primaryStage.getY() + deltay);
|
||||
if (this.setStageHeight(this.primaryStage.getHeight() - deltaY)) {
|
||||
this.primaryStage.setY(this.primaryStage.getY() + deltaY);
|
||||
}
|
||||
|
||||
mouseEvent.consume();
|
||||
} else if (Cursor.N_RESIZE == cursor) {
|
||||
if (this.setStageHeight(this.primaryStage.getHeight() - deltay)) {
|
||||
this.primaryStage.setY(this.primaryStage.getY() + deltay);
|
||||
if (this.setStageHeight(this.primaryStage.getHeight() - deltaY)) {
|
||||
this.primaryStage.setY(this.primaryStage.getY() + deltaY);
|
||||
}
|
||||
|
||||
mouseEvent.consume();
|
||||
@@ -487,14 +491,11 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
|
||||
|
||||
if (content instanceof Region) {
|
||||
((Region) content).setMinSize(0, 0);
|
||||
FXUtils.setOverflowHidden((Region) content);
|
||||
FXUtils.setOverflowHidden((Region) content, true);
|
||||
}
|
||||
|
||||
if (content instanceof Refreshable)
|
||||
refreshNavButton.setVisible(true);
|
||||
|
||||
if (content != mainPage)
|
||||
backNavButton.setVisible(true);
|
||||
refreshNavButton.setVisible(content instanceof Refreshable);
|
||||
backNavButton.setVisible(content != mainPage);
|
||||
|
||||
String prefix = category == null ? "" : category + " - ";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -20,7 +20,6 @@ package org.jackhuang.hmcl.ui;
|
||||
import com.jfoenix.concurrency.JFXUtilities;
|
||||
import org.jackhuang.hmcl.auth.Account;
|
||||
import org.jackhuang.hmcl.auth.AuthInfo;
|
||||
import org.jackhuang.hmcl.auth.MultiCharacterSelector;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
|
||||
import org.jackhuang.hmcl.task.SilentException;
|
||||
|
||||
@@ -35,7 +34,7 @@ public final class DialogController {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
AtomicReference<AuthInfo> res = new AtomicReference<>(null);
|
||||
JFXUtilities.runInFX(() -> {
|
||||
YggdrasilAccountLoginPane pane = new YggdrasilAccountLoginPane((YggdrasilAccount) account, it -> {
|
||||
AccountLoginPane pane = new AccountLoginPane(account, it -> {
|
||||
res.set(it);
|
||||
latch.countDown();
|
||||
Controllers.closeDialog();
|
||||
@@ -48,6 +47,6 @@ public final class DialogController {
|
||||
latch.await();
|
||||
return Optional.ofNullable(res.get()).orElseThrow(SilentException::new);
|
||||
}
|
||||
return account.logIn(MultiCharacterSelector.DEFAULT);
|
||||
return account.logIn();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -30,27 +30,24 @@ import javafx.beans.value.WeakChangeListener;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.control.ScrollBar;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.image.WritableImage;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.input.ScrollEvent;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
import javafx.util.Duration;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.OperatingSystem;
|
||||
import org.jackhuang.hmcl.util.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@@ -92,35 +89,96 @@ public final class FXUtils {
|
||||
});
|
||||
}
|
||||
|
||||
public static void setValidateWhileTextChanged(JFXTextField field) {
|
||||
field.textProperty().addListener(o -> field.validate());
|
||||
field.validate();
|
||||
private static class ListenerPair<T> {
|
||||
private final ObservableValue<T> value;
|
||||
private final ChangeListener<? super T> listener;
|
||||
|
||||
ListenerPair(ObservableValue<T> value, ChangeListener<? super T> listener) {
|
||||
this.value = value;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
void bind() {
|
||||
value.addListener(listener);
|
||||
}
|
||||
|
||||
void unbind() {
|
||||
value.removeListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setValidateWhileTextChanged(JFXPasswordField field) {
|
||||
field.textProperty().addListener(o -> field.validate());
|
||||
field.validate();
|
||||
public static <T> void addListener(Node node, String key, ObservableValue<T> value, Consumer<? super T> callback) {
|
||||
ListenerPair<T> pair = new ListenerPair<>(value, (a, b, newValue) -> callback.accept(newValue));
|
||||
node.getProperties().put(key, pair);
|
||||
pair.bind();
|
||||
}
|
||||
|
||||
public static void setOverflowHidden(Region region) {
|
||||
Rectangle rectangle = new Rectangle();
|
||||
rectangle.widthProperty().bind(region.widthProperty());
|
||||
rectangle.heightProperty().bind(region.heightProperty());
|
||||
region.setClip(rectangle);
|
||||
public static void removeListener(Node node, String key) {
|
||||
Lang.cast(node.getProperties().get(key), ListenerPair.class)
|
||||
.ifPresent(info -> {
|
||||
info.unbind();
|
||||
node.getProperties().remove(key);
|
||||
});
|
||||
}
|
||||
|
||||
public static void limitWidth(Region region, double width) {
|
||||
public static void setValidateWhileTextChanged(Node field, boolean validate) {
|
||||
if (field instanceof JFXTextField) {
|
||||
if (validate) {
|
||||
addListener(field, "FXUtils.validation", ((JFXTextField) field).textProperty(), o -> ((JFXTextField) field).validate());
|
||||
} else {
|
||||
removeListener(field, "FXUtils.validation");
|
||||
}
|
||||
((JFXTextField) field).validate();
|
||||
} else if (field instanceof JFXPasswordField) {
|
||||
if (validate) {
|
||||
addListener(field, "FXUtils.validation", ((JFXPasswordField) field).textProperty(), o -> ((JFXPasswordField) field).validate());
|
||||
} else {
|
||||
removeListener(field, "FXUtils.validation");
|
||||
}
|
||||
((JFXPasswordField) field).validate();
|
||||
} else
|
||||
throw new IllegalArgumentException("Only JFXTextField and JFXPasswordField allowed");
|
||||
}
|
||||
|
||||
public static boolean getValidateWhileTextChanged(Node field) {
|
||||
return field.getProperties().containsKey("FXUtils.validation");
|
||||
}
|
||||
|
||||
public static void setOverflowHidden(Region region, boolean hidden) {
|
||||
if (hidden) {
|
||||
Rectangle rectangle = new Rectangle();
|
||||
rectangle.widthProperty().bind(region.widthProperty());
|
||||
rectangle.heightProperty().bind(region.heightProperty());
|
||||
region.setClip(rectangle);
|
||||
} else {
|
||||
region.setClip(null);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean getOverflowHidden(Region region) {
|
||||
return region.getClip() != null;
|
||||
}
|
||||
|
||||
public static void setLimitWidth(Region region, double width) {
|
||||
region.setMaxWidth(width);
|
||||
region.setMinWidth(width);
|
||||
region.setPrefWidth(width);
|
||||
}
|
||||
|
||||
public static void limitHeight(Region region, double height) {
|
||||
public static double getLimitWidth(Region region) {
|
||||
return region.getMaxWidth();
|
||||
}
|
||||
|
||||
public static void setLimitHeight(Region region, double height) {
|
||||
region.setMaxHeight(height);
|
||||
region.setMinHeight(height);
|
||||
region.setPrefHeight(height);
|
||||
}
|
||||
|
||||
public static double getLimitHeight(Region region) {
|
||||
return region.getMaxHeight();
|
||||
}
|
||||
|
||||
public static void smoothScrolling(ScrollPane scrollPane) {
|
||||
JFXScrollPane.smoothScrolling(scrollPane);
|
||||
}
|
||||
@@ -129,7 +187,7 @@ public final class FXUtils {
|
||||
FXMLLoader loader = new FXMLLoader(node.getClass().getResource(absolutePath), Main.RESOURCE_BUNDLE);
|
||||
loader.setRoot(node);
|
||||
loader.setController(node);
|
||||
Lang.invoke(() -> loader.load());
|
||||
Lang.invoke((ExceptionalSupplier<Object, IOException>) loader::load);
|
||||
}
|
||||
|
||||
public static void resetChildren(JFXMasonryPane pane, List<Node> children) {
|
||||
@@ -138,6 +196,10 @@ public final class FXUtils {
|
||||
pane.getChildren().setAll(children);
|
||||
}
|
||||
|
||||
public static void installTooltip(Node node, String tooltip) {
|
||||
installTooltip(node, 0, 5000, 0, new Tooltip(tooltip));
|
||||
}
|
||||
|
||||
public static void installTooltip(Node node, double openDelay, double visibleDelay, double closeDelay, Tooltip tooltip) {
|
||||
try {
|
||||
// Java 8
|
||||
@@ -157,37 +219,12 @@ public final class FXUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean alert(Alert.AlertType type, String title, String contentText) {
|
||||
return alert(type, title, contentText, null);
|
||||
}
|
||||
|
||||
public static boolean alert(Alert.AlertType type, String title, String contentText, String headerText) {
|
||||
Alert alert = new Alert(type);
|
||||
alert.setTitle(title);
|
||||
alert.setHeaderText(headerText);
|
||||
alert.setContentText(contentText);
|
||||
Optional<ButtonType> result = alert.showAndWait();
|
||||
return result.isPresent() && result.get() == ButtonType.OK;
|
||||
}
|
||||
|
||||
public static Optional<String> inputDialog(String title, String contentText) {
|
||||
return inputDialog(title, contentText, null);
|
||||
}
|
||||
|
||||
public static Optional<String> inputDialog(String title, String contentText, String headerText) {
|
||||
return inputDialog(title, contentText, headerText, "");
|
||||
}
|
||||
|
||||
public static Optional<String> inputDialog(String title, String contentText, String headerText, String defaultValue) {
|
||||
TextInputDialog dialog = new TextInputDialog(defaultValue);
|
||||
dialog.setTitle(title);
|
||||
dialog.setHeaderText(headerText);
|
||||
dialog.setContentText(contentText);
|
||||
return dialog.showAndWait();
|
||||
}
|
||||
|
||||
public static void openFolder(File file) {
|
||||
file.mkdirs();
|
||||
if (!FileUtils.makeDirectory(file)) {
|
||||
Logging.LOG.log(Level.SEVERE, "Unable to make directory " + file);
|
||||
return;
|
||||
}
|
||||
|
||||
String path = file.getAbsolutePath();
|
||||
|
||||
switch (OperatingSystem.CURRENT_OS) {
|
||||
@@ -260,18 +297,32 @@ public final class FXUtils {
|
||||
checkBox.selectedProperty().unbindBidirectional(property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind combo box selection with given enum property bidirectionally.
|
||||
* You should <b>only and always</b> use {@code bindEnum} as well as {@code unbindEnum} at the same time.
|
||||
* @param comboBox the combo box being bound with {@code property}.
|
||||
* @param property the property being bound with {@code combo box}.
|
||||
* @see #unbindEnum(JFXComboBox)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void bindEnum(JFXComboBox<?> comboBox, Property<? extends Enum> property) {
|
||||
unbindEnum(comboBox);
|
||||
ChangeListener<Number> listener = (a, b, newValue) -> {
|
||||
((Property) property).setValue(property.getValue().getClass().getEnumConstants()[newValue.intValue()]);
|
||||
};
|
||||
ChangeListener<Number> listener = (a, b, newValue) ->
|
||||
((Property) property).setValue(property.getValue().getClass().getEnumConstants()[newValue.intValue()]);
|
||||
comboBox.getSelectionModel().select(property.getValue().ordinal());
|
||||
comboBox.getProperties().put("listener", listener);
|
||||
comboBox.getProperties().put("FXUtils.bindEnum.listener", listener);
|
||||
comboBox.getSelectionModel().selectedIndexProperty().addListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbind combo box selection with given enum property bidirectionally.
|
||||
* You should <b>only and always</b> use {@code bindEnum} as well as {@code unbindEnum} at the same time.
|
||||
* @param comboBox the combo box being bound with the property which can be inferred by {@code bindEnum}.
|
||||
* @see #bindEnum(JFXComboBox, Property)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void unbindEnum(JFXComboBox<?> comboBox) {
|
||||
ChangeListener listener = Lang.get(comboBox.getProperties(), "listener", ChangeListener.class, null);
|
||||
ChangeListener listener = Lang.get(comboBox.getProperties(), "FXUtils.bindEnum.listener", ChangeListener.class).orElse(null);
|
||||
if (listener == null) return;
|
||||
comboBox.getSelectionModel().selectedIndexProperty().removeListener(listener);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -53,7 +53,7 @@ public class InstallerController {
|
||||
public void loadVersion(Profile profile, String versionId) {
|
||||
this.profile = profile;
|
||||
this.versionId = versionId;
|
||||
this.version = profile.getRepository().getVersion(versionId).resolve(profile.getRepository());
|
||||
this.version = profile.getRepository().getResolvedVersion(versionId);
|
||||
|
||||
contentPane.getChildren().clear();
|
||||
forge = liteLoader = optiFine = null;
|
||||
@@ -62,7 +62,7 @@ public class InstallerController {
|
||||
Consumer<InstallerItem> removeAction = x -> {
|
||||
LinkedList<Library> newList = new LinkedList<>(version.getLibraries());
|
||||
newList.remove(library);
|
||||
new MaintainTask(profile.getRepository(), version.setLibraries(newList))
|
||||
new MaintainTask(version.setLibraries(newList))
|
||||
.then(variables -> new VersionJsonSaveTask(profile.getRepository(), variables.get(MaintainTask.ID)))
|
||||
.with(profile.getRepository().refreshVersionsAsync())
|
||||
.with(Task.of(Schedulers.javafx(), () -> loadVersion(this.profile, this.versionId)))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -24,7 +24,6 @@ import javafx.scene.Node;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.paint.Paint;
|
||||
import javafx.scene.text.Text;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
|
||||
@@ -37,9 +36,10 @@ import org.jackhuang.hmcl.game.HMCLGameRepository;
|
||||
import org.jackhuang.hmcl.game.ModpackHelper;
|
||||
import org.jackhuang.hmcl.mod.Modpack;
|
||||
import org.jackhuang.hmcl.mod.UnsupportedModpackException;
|
||||
import org.jackhuang.hmcl.setting.Accounts;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
import org.jackhuang.hmcl.setting.Profiles;
|
||||
import org.jackhuang.hmcl.setting.Settings;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.construct.AdvancedListBox;
|
||||
@@ -47,7 +47,6 @@ import org.jackhuang.hmcl.ui.construct.ClassTitle;
|
||||
import org.jackhuang.hmcl.ui.construct.IconedItem;
|
||||
import org.jackhuang.hmcl.ui.construct.RipplerContainer;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.Pair;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.LinkedList;
|
||||
@@ -67,16 +66,14 @@ public final class LeftPaneController {
|
||||
accountItem.setOnSettingsButtonClicked(() -> Controllers.navigate(new AccountsPage()));
|
||||
}))
|
||||
.startCategory(Main.i18n("launcher").toUpperCase())
|
||||
.add(Lang.apply(new IconedItem(SVG.gear("black", 20, 20), Main.i18n("settings.launcher")), iconedItem -> {
|
||||
.add(Lang.apply(new IconedItem(SVG.gear(Theme.blackFillBinding(), 20, 20), Main.i18n("settings.launcher")), iconedItem -> {
|
||||
iconedItem.prefWidthProperty().bind(leftPane.widthProperty());
|
||||
iconedItem.setOnMouseClicked(e -> Controllers.navigate(Controllers.getSettingsPage()));
|
||||
}))
|
||||
.add(new ClassTitle(Lang.apply(new BorderPane(), borderPane -> {
|
||||
borderPane.setLeft(Lang.apply(new VBox(), vBox -> {
|
||||
vBox.getChildren().setAll(new Text(Main.i18n("profile.title").toUpperCase()));
|
||||
}));
|
||||
borderPane.setLeft(Lang.apply(new VBox(), vBox -> vBox.getChildren().setAll(new Text(Main.i18n("profile.title").toUpperCase()))));
|
||||
JFXButton addProfileButton = new JFXButton();
|
||||
addProfileButton.setGraphic(SVG.plus("black", 10, 10));
|
||||
addProfileButton.setGraphic(SVG.plus(Theme.blackFillBinding(), 10, 10));
|
||||
addProfileButton.getStyleClass().add("toggle-icon-tiny");
|
||||
addProfileButton.setOnMouseClicked(e ->
|
||||
Controllers.getDecorator().showPage(new ProfilePage(null)));
|
||||
@@ -93,7 +90,7 @@ public final class LeftPaneController {
|
||||
accountItem.setVersionName(Main.i18n("account.missing"));
|
||||
accountItem.setGameVersion(Main.i18n("message.unknown"));
|
||||
} else {
|
||||
accountItem.setVersionName(Accounts.getCurrentCharacter(it));
|
||||
accountItem.setVersionName(it.getCharacter());
|
||||
accountItem.setGameVersion(AccountsPage.accountType(it));
|
||||
}
|
||||
|
||||
@@ -110,8 +107,8 @@ public final class LeftPaneController {
|
||||
|
||||
Platform.runLater(() -> {
|
||||
for (Node node : profilePane.getChildren()) {
|
||||
if (node instanceof RipplerContainer && node.getProperties().get("profile") instanceof Pair<?, ?>) {
|
||||
boolean current = Objects.equals(((Pair) node.getProperties().get("profile")).getKey(), profile.getName());
|
||||
if (node instanceof RipplerContainer && node.getProperties().get("profile") instanceof String) {
|
||||
boolean current = Objects.equals(node.getProperties().get("profile"), profile.getName());
|
||||
((RipplerContainer) node).setSelected(current);
|
||||
((VersionListItem) ((RipplerContainer) node).getContainer()).setGameVersion(current ? Main.i18n("profile.selected") : "");
|
||||
}
|
||||
@@ -122,11 +119,11 @@ public final class LeftPaneController {
|
||||
private void onProfilesLoading() {
|
||||
LinkedList<RipplerContainer> list = new LinkedList<>();
|
||||
for (Profile profile : Settings.INSTANCE.getProfiles()) {
|
||||
VersionListItem item = new VersionListItem(profile.getName());
|
||||
VersionListItem item = new VersionListItem(Profiles.getProfileDisplayName(profile));
|
||||
RipplerContainer ripplerContainer = new RipplerContainer(item);
|
||||
item.setOnSettingsButtonClicked(() -> Controllers.getDecorator().showPage(new ProfilePage(profile)));
|
||||
ripplerContainer.setOnMouseClicked(e -> Settings.INSTANCE.setSelectedProfile(profile));
|
||||
ripplerContainer.getProperties().put("profile", new Pair<>(profile.getName(), item));
|
||||
ripplerContainer.getProperties().put("profile", profile.getName());
|
||||
ripplerContainer.maxWidthProperty().bind(leftPane.widthProperty());
|
||||
list.add(ripplerContainer);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2013 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -70,9 +70,7 @@ public final class LogWindow extends Stage {
|
||||
public LogWindow(String text) {
|
||||
this();
|
||||
|
||||
onDone.register(() -> {
|
||||
logLine(text, Log4jLevel.INFO);
|
||||
});
|
||||
onDone.register(() -> logLine(text, Log4jLevel.INFO));
|
||||
}
|
||||
|
||||
public ReadOnlyIntegerProperty fatalProperty() {
|
||||
@@ -140,6 +138,9 @@ public final class LogWindow extends Stage {
|
||||
case DEBUG:
|
||||
debug.set(debug.get() + 1);
|
||||
break;
|
||||
default:
|
||||
// ignore
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +165,7 @@ public final class LogWindow extends Stage {
|
||||
@FXML
|
||||
private ComboBox<String> cboLines;
|
||||
|
||||
WebEngine engine;
|
||||
final WebEngine engine;
|
||||
Node body;
|
||||
Document document;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -26,7 +26,6 @@ import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import javafx.scene.layout.StackPane;
|
||||
@@ -36,10 +35,7 @@ import org.jackhuang.hmcl.event.EventBus;
|
||||
import org.jackhuang.hmcl.event.ProfileChangedEvent;
|
||||
import org.jackhuang.hmcl.event.ProfileLoadingEvent;
|
||||
import org.jackhuang.hmcl.event.RefreshedVersionsEvent;
|
||||
import org.jackhuang.hmcl.game.GameVersion;
|
||||
import org.jackhuang.hmcl.game.LauncherHelper;
|
||||
import org.jackhuang.hmcl.game.ModpackHelper;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.game.*;
|
||||
import org.jackhuang.hmcl.mod.MismatchedModpackTypeException;
|
||||
import org.jackhuang.hmcl.mod.UnsupportedModpackException;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
@@ -51,8 +47,8 @@ import org.jackhuang.hmcl.ui.construct.MessageBox;
|
||||
import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane;
|
||||
import org.jackhuang.hmcl.ui.download.DownloadWizardProvider;
|
||||
import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.OperatingSystem;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -78,22 +74,20 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
@FXML
|
||||
private JFXListView versionList;
|
||||
|
||||
private JFXPopup versionPopup;
|
||||
private final JFXPopup versionPopup;
|
||||
|
||||
{
|
||||
FXUtils.loadFXML(this, "/assets/fxml/main.fxml");
|
||||
|
||||
EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).register(() -> Platform.runLater(this::loadVersions));
|
||||
EventBus.EVENT_BUS.channel(ProfileLoadingEvent.class).register(this::onProfilesLoading);
|
||||
EventBus.EVENT_BUS.channel(ProfileChangedEvent.class).register(this::onProfileChanged);
|
||||
|
||||
versionPopup = new JFXPopup(versionList);
|
||||
getChildren().remove(versionList);
|
||||
|
||||
btnAdd.setOnMouseClicked(e -> Controllers.getDecorator().startWizard(new DownloadWizardProvider(), Main.i18n("install")));
|
||||
FXUtils.installTooltip(btnAdd, 0, 5000, 0, new Tooltip(Main.i18n("install")));
|
||||
FXUtils.installTooltip(btnAdd, Main.i18n("install"));
|
||||
btnRefresh.setOnMouseClicked(e -> Settings.INSTANCE.getSelectedProfile().getRepository().refreshVersionsAsync().start());
|
||||
FXUtils.installTooltip(btnRefresh, 0, 5000, 0, new Tooltip(Main.i18n("button.refresh")));
|
||||
FXUtils.installTooltip(btnRefresh, Main.i18n("button.refresh"));
|
||||
}
|
||||
|
||||
private Node buildNode(Profile profile, String version, String game) {
|
||||
@@ -101,6 +95,21 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
item.setUpdate(profile.getRepository().isModpack(version));
|
||||
item.setGameVersion(game);
|
||||
item.setVersionName(version);
|
||||
|
||||
StringBuilder libraries = new StringBuilder();
|
||||
for (Library library : profile.getRepository().getVersion(version).getLibraries()) {
|
||||
if (library.getGroupId().equalsIgnoreCase("net.minecraftforge") && library.getArtifactId().equalsIgnoreCase("forge")) {
|
||||
libraries.append(Main.i18n("install.installer.forge")).append(": ").append(StringUtils.removeSuffix(StringUtils.removePrefix(library.getVersion().replaceAll("(?i)forge", "").replace(game, "").trim(), "-"), "-")).append("\n");
|
||||
}
|
||||
if (library.getGroupId().equalsIgnoreCase("com.mumfrey") && library.getArtifactId().equalsIgnoreCase("liteloader")) {
|
||||
libraries.append(Main.i18n("install.installer.liteloader")).append(": ").append(StringUtils.removeSuffix(StringUtils.removePrefix(library.getVersion().replaceAll("(?i)liteloader", "").replace(game, "").trim(), "-"), "-")).append("\n");
|
||||
}
|
||||
if (library.getGroupId().equalsIgnoreCase("net.optifine") && library.getArtifactId().equalsIgnoreCase("optifine")) {
|
||||
libraries.append(Main.i18n("install.installer.optifine")).append(": ").append(StringUtils.removeSuffix(StringUtils.removePrefix(library.getVersion().replaceAll("(?i)optifine", "").replace(game, "").trim(), "-"), "-")).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
item.setLibraries(libraries.toString());
|
||||
item.setOnLaunchButtonClicked(e -> {
|
||||
if (Settings.INSTANCE.getSelectedAccount() == null)
|
||||
Controllers.dialog(Main.i18n("login.empty_username"));
|
||||
@@ -167,14 +176,6 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
return item;
|
||||
}
|
||||
|
||||
public void onProfilesLoading() {
|
||||
// TODO: Profiles
|
||||
}
|
||||
|
||||
public void onProfileChanged(ProfileChangedEvent event) {
|
||||
Platform.runLater(() -> loadVersions(event.getProfile()));
|
||||
}
|
||||
|
||||
private void loadVersions() {
|
||||
loadVersions(Settings.INSTANCE.getSelectedProfile());
|
||||
}
|
||||
@@ -182,8 +183,7 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
private void loadVersions(Profile profile) {
|
||||
this.profile = profile;
|
||||
List<Node> children = new LinkedList<>();
|
||||
List<Version> versions = new LinkedList<>(profile.getRepository().getVersions());
|
||||
for (Version version : versions) {
|
||||
for (Version version : profile.getRepository().getVersions()) {
|
||||
children.add(buildNode(profile, version.getId(), GameVersion.minecraftVersion(profile.getRepository().getVersionJar(version.getId())).orElse("Unknown")));
|
||||
}
|
||||
FXUtils.resetChildren(masonryPane, children);
|
||||
@@ -205,6 +205,8 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
case 3:
|
||||
FXUtils.openFolder(profile.getRepository().getRunDirectory(rightClickedVersion));
|
||||
break;
|
||||
default:
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -127,13 +127,12 @@ public final class ModController {
|
||||
});
|
||||
variables.set("list", list);
|
||||
}
|
||||
}).subscribe(Schedulers.javafx(), variables -> {
|
||||
}).finalized(Schedulers.javafx(), variables -> {
|
||||
FXUtils.onWeakChangeAndOperate(parentTab.getSelectionModel().selectedItemProperty(), newValue -> {
|
||||
if (newValue != null && newValue.getUserData() == ModController.this)
|
||||
modPane.getChildren().setAll(variables.<List<ModItem>>get("list"));
|
||||
});
|
||||
});
|
||||
|
||||
}, null).start();
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -141,10 +140,33 @@ public final class ModController {
|
||||
FileChooser chooser = new FileChooser();
|
||||
chooser.setTitle(Main.i18n("mods.choose_mod"));
|
||||
chooser.getExtensionFilters().setAll(new FileChooser.ExtensionFilter(Main.i18n("extension.mod"), "*.jar", "*.zip", "*.litemod"));
|
||||
File res = chooser.showOpenDialog(Controllers.getStage());
|
||||
List<File> res = chooser.showOpenMultipleDialog(Controllers.getStage());
|
||||
|
||||
// It's guaranteed that succeeded and failed are thread safe here.
|
||||
List<String> succeeded = new LinkedList<>();
|
||||
List<String> failed = new LinkedList<>();
|
||||
if (res == null) return;
|
||||
Task.of(() -> modManager.addMod(versionId, res))
|
||||
.subscribe(Task.of(Schedulers.javafx(), () -> loadMods(modManager, versionId)));
|
||||
Task.of(variables -> {
|
||||
for (File file : res) {
|
||||
try {
|
||||
modManager.addMod(versionId, file);
|
||||
succeeded.add(file.getName());
|
||||
} catch (Exception e) {
|
||||
Logging.LOG.log(Level.WARNING, "Unable to add mod " + file, e);
|
||||
failed.add(file.getName());
|
||||
|
||||
// Actually addMod will not throw exceptions because FileChooser has already filtered files.
|
||||
}
|
||||
}
|
||||
}).with(Task.of(Schedulers.javafx(), variables -> {
|
||||
List<String> prompt = new LinkedList<>();
|
||||
if (!succeeded.isEmpty())
|
||||
prompt.add(Main.i18n("mods.add.success", String.join(", ", succeeded)));
|
||||
if (!failed.isEmpty())
|
||||
prompt.add(Main.i18n("mods.add.failed", String.join(", ", failed)));
|
||||
Controllers.dialog(String.join("\n", prompt), Main.i18n("mods.add"));
|
||||
loadMods(modManager, versionId);
|
||||
})).start();
|
||||
}
|
||||
|
||||
public void setParentTab(JFXTabPane parentTab) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -21,44 +21,38 @@ import com.jfoenix.controls.JFXButton;
|
||||
import com.jfoenix.controls.JFXCheckBox;
|
||||
import com.jfoenix.effects.JFXDepthManager;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.mod.ModInfo;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public final class ModItem extends BorderPane {
|
||||
private final Label lblModFileName = new Label();
|
||||
private final Label lblModAuthor = new Label();
|
||||
private final JFXCheckBox chkEnabled = new JFXCheckBox();
|
||||
|
||||
public ModItem(ModInfo info, Consumer<ModItem> deleteCallback) {
|
||||
lblModFileName.setStyle("-fx-font-size: 15;");
|
||||
lblModAuthor.setStyle("-fx-font-size: 10;");
|
||||
JFXCheckBox chkEnabled = new JFXCheckBox();
|
||||
BorderPane.setAlignment(chkEnabled, Pos.CENTER);
|
||||
setLeft(chkEnabled);
|
||||
|
||||
VBox center = new VBox();
|
||||
BorderPane.setAlignment(center, Pos.CENTER);
|
||||
center.getChildren().addAll(lblModFileName, lblModAuthor);
|
||||
setCenter(center);
|
||||
TwoLineListItem modItem = new TwoLineListItem();
|
||||
BorderPane.setAlignment(modItem, Pos.CENTER);
|
||||
setCenter(modItem);
|
||||
|
||||
JFXButton right = new JFXButton();
|
||||
right.setOnMouseClicked(e -> deleteCallback.accept(this));
|
||||
right.getStyleClass().add("toggle-icon4");
|
||||
BorderPane.setAlignment(right, Pos.CENTER);
|
||||
right.setGraphic(SVG.close("black", 15, 15));
|
||||
setRight(right);
|
||||
JFXButton btnRemove = new JFXButton();
|
||||
FXUtils.installTooltip(btnRemove, Main.i18n("mods.remove"));
|
||||
btnRemove.setOnMouseClicked(e -> deleteCallback.accept(this));
|
||||
btnRemove.getStyleClass().add("toggle-icon4");
|
||||
BorderPane.setAlignment(btnRemove, Pos.CENTER);
|
||||
btnRemove.setGraphic(SVG.close(Theme.blackFillBinding(), 15, 15));
|
||||
setRight(btnRemove);
|
||||
|
||||
setStyle("-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;");
|
||||
JFXDepthManager.setDepth(this, 1);
|
||||
lblModFileName.setText(info.getFileName());
|
||||
lblModAuthor.setText(info.getName() + ", " + Main.i18n("archive.version") + ": " + info.getVersion() + ", " + Main.i18n("archive.game_version") + ": " + info.getGameVersion() + ", " + Main.i18n("archive.author") + ": " + info.getAuthors());
|
||||
modItem.setTitle(info.getFileName());
|
||||
modItem.setSubtitle(info.getName() + ", " + Main.i18n("archive.version") + ": " + info.getVersion() + ", " + Main.i18n("archive.game_version") + ": " + info.getGameVersion() + ", " + Main.i18n("archive.author") + ": " + info.getAuthors());
|
||||
chkEnabled.setSelected(info.isActive());
|
||||
chkEnabled.selectedProperty().addListener((a, b, newValue) -> {
|
||||
info.activeProperty().set(newValue);
|
||||
});
|
||||
chkEnabled.selectedProperty().addListener((a, b, newValue) ->
|
||||
info.activeProperty().set(newValue));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -25,6 +25,7 @@ import javafx.fxml.FXML;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
import org.jackhuang.hmcl.setting.Profiles;
|
||||
import org.jackhuang.hmcl.setting.Settings;
|
||||
import org.jackhuang.hmcl.ui.construct.FileItem;
|
||||
import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
|
||||
@@ -58,7 +59,7 @@ public final class ProfilePage extends StackPane implements DecoratorPage {
|
||||
|
||||
FXUtils.loadFXML(this, "/assets/fxml/profile.fxml");
|
||||
|
||||
txtProfileName.setText(Optional.ofNullable(profile).map(Profile::getName).orElse(""));
|
||||
txtProfileName.setText(Optional.ofNullable(profile).map(Profiles::getProfileDisplayName).orElse(""));
|
||||
FXUtils.onChangeAndOperate(txtProfileName.textProperty(), it -> {
|
||||
btnSave.setDisable(!txtProfileName.validate() || StringUtils.isBlank(getLocation()));
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -17,19 +17,21 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui;
|
||||
|
||||
import javafx.beans.binding.ObjectBinding;
|
||||
import javafx.scene.Group;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.paint.Paint;
|
||||
import javafx.scene.shape.SVGPath;
|
||||
|
||||
public final class SVG {
|
||||
private SVG() {
|
||||
}
|
||||
|
||||
private static Node createSVGPath(String d, String fill, double width, double height) {
|
||||
private static Node createSVGPath(String d, ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
SVGPath path = new SVGPath();
|
||||
path.getStyleClass().add("svg");
|
||||
path.setContent(d);
|
||||
path.setStyle("-fx-fill: " + fill + ";");
|
||||
path.fillProperty().bind(fill);
|
||||
|
||||
Group svg = new Group(path);
|
||||
double scale = Math.min(width / svg.getBoundsInParent().getWidth(), height / svg.getBoundsInParent().getHeight());
|
||||
@@ -41,87 +43,87 @@ public final class SVG {
|
||||
|
||||
// default fill: white, width: 20, height 20
|
||||
|
||||
public static Node gear(String fill, double width, double height) {
|
||||
public static Node gear(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath("M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.67 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z", fill, width, height);
|
||||
}
|
||||
|
||||
public static Node back(String fill, double width, double height) {
|
||||
public static Node back(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath("M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z", fill, width, height);
|
||||
}
|
||||
|
||||
public static Node close(String fill, double width, double height) {
|
||||
public static Node close(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath("M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z", fill, width, height);
|
||||
}
|
||||
|
||||
public static Node dotsVertical(String fill, double width, double height) {
|
||||
public static Node dotsVertical(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath("M12,16A2,2 0 0,1 14,18A2,2 0 0,1 12,20A2,2 0 0,1 10,18A2,2 0 0,1 12,16M12,10A2,2 0 0,1 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12A2,2 0 0,1 12,10M12,4A2,2 0 0,1 14,6A2,2 0 0,1 12,8A2,2 0 0,1 10,6A2,2 0 0,1 12,4Z", fill, width, height);
|
||||
}
|
||||
|
||||
public static Node delete(String fill, double width, double height) {
|
||||
public static Node delete(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath("M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z", fill, width, height);
|
||||
}
|
||||
|
||||
public static Node accountEdit(String fill, double width, double height) {
|
||||
public static Node accountEdit(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath("M21.7,13.35L20.7,14.35L18.65,12.3L19.65,11.3C19.86,11.09 20.21,11.09 20.42,11.3L21.7,12.58C21.91,12.79 21.91,13.14 21.7,13.35M12,18.94L18.06,12.88L20.11,14.93L14.06,21H12V18.94M12,14C7.58,14 4,15.79 4,18V20H10V18.11L14,14.11C13.34,14.03 12.67,14 12,14M12,4A4,4 0 0,0 8,8A4,4 0 0,0 12,12A4,4 0 0,0 16,8A4,4 0 0,0 12,4Z", fill, width, height);
|
||||
}
|
||||
|
||||
public static Node expand(String fill, double width, double height) {
|
||||
public static Node expand(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath("M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z", fill, width, height);
|
||||
}
|
||||
|
||||
public static Node collapse(String fill, double width, double height) {
|
||||
public static Node collapse(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath("M12 8l-6 6 1.41 1.41L12 10.83l4.59 4.58L18 14z", fill, width, height);
|
||||
}
|
||||
|
||||
public static Node navigate(String fill, double width, double height) {
|
||||
public static Node navigate(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath("M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z", fill, width, height);
|
||||
}
|
||||
|
||||
public static Node launch(String fill, double width, double height) {
|
||||
public static Node launch(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath("M1008 6.286q18.857 13.714 15.429 36.571l-146.286 877.714q-2.857 16.571-18.286 25.714-8 4.571-17.714 4.571-6.286 0-13.714-2.857l-258.857-105.714-138.286 168.571q-10.286 13.143-28 13.143-7.429 0-12.571-2.286-10.857-4-17.429-13.429t-6.571-20.857v-199.429l493.714-605.143-610.857 528.571-225.714-92.571q-21.143-8-22.857-31.429-1.143-22.857 18.286-33.714l950.857-548.571q8.571-5.143 18.286-5.14311.429 0 20.571 6.286z", fill, width, height);
|
||||
}
|
||||
|
||||
public static Node script(String fill, double width, double height) {
|
||||
public static Node script(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath("M14,20A2,2 0 0,0 16,18V5H9A1,1 0 0,0 8,6V16H5V5A3,3 0 0,1 8,2H19A3,3 0 0,1 22,5V6H18V18L18,19A3,3 0 0,1 15,22H5A3,3 0 0,1 2,19V18H12A2,2 0 0,0 14,20Z", fill, width, height);
|
||||
}
|
||||
|
||||
public static Node pencil(String fill, double width, double height) {
|
||||
public static Node pencil(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath("M20.71,4.04C21.1,3.65 21.1,3 20.71,2.63L18.37,0.29C18,-0.1 17.35,-0.1 16.96,0.29L15,2.25L18.75,6M17.75,7L14,3.25L4,13.25V17H7.75L17.75,7Z", fill, width, height);
|
||||
}
|
||||
|
||||
public static Node refresh(String fill, double width, double height) {
|
||||
public static Node refresh(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath("M17.65,6.35C16.2,4.9 14.21,4 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20C15.73,20 18.84,17.45 19.73,14H17.65C16.83,16.33 14.61,18 12,18A6,6 0 0,1 6,12A6,6 0 0,1 12,6C13.66,6 15.14,6.69 16.22,7.78L13,11H20V4L17.65,6.35Z", fill, width, height);
|
||||
}
|
||||
|
||||
public static Node folderOpen(String fill, double width, double height) {
|
||||
public static Node folderOpen(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath("M19,20H4C2.89,20 2,19.1 2,18V6C2,4.89 2.89,4 4,4H10L12,6H19A2,2 0 0,1 21,8H21L4,8V18L6.14,10H23.21L20.93,18.5C20.7,19.37 19.92,20 19,20Z", fill, width, height);
|
||||
}
|
||||
|
||||
public static Node update(String fill, double width, double height) {
|
||||
public static Node update(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath("M21,10.12H14.22L16.96,7.3C14.23,4.6 9.81,4.5 7.08,7.2C4.35,9.91 4.35,14.28 7.08,17C9.81,19.7 14.23,19.7 16.96,17C18.32,15.65 19,14.08 19,12.1H21C21,14.08 20.12,16.65 18.36,18.39C14.85,21.87 9.15,21.87 5.64,18.39C2.14,14.92 2.11,9.28 5.62,5.81C9.13,2.34 14.76,2.34 18.27,5.81L21,3V10.12M12.5,8V12.25L16,14.33L15.28,15.54L11,13V8H12.5Z", fill, width, height);
|
||||
}
|
||||
|
||||
public static Node closeCircle(String fill, double width, double height) {
|
||||
public static Node closeCircle(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath("M12,2C17.53,2 22,6.47 22,12C22,17.53 17.53,22 12,22C6.47,22 2,17.53 2,12C2,6.47 6.47,2 12,2M15.59,7L12,10.59L8.41,7L7,8.41L10.59,12L7,15.59L8.41,17L12,13.41L15.59,17L17,15.59L13.41,12L17,8.41L15.59,7Z", fill, width, height);
|
||||
}
|
||||
|
||||
public static Node checkCircle(String fill, double width, double height) {
|
||||
public static Node checkCircle(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath("M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M11,16.5L18,9.5L16.59,8.09L11,13.67L7.91,10.59L6.5,12L11,16.5Z", fill, width, height);
|
||||
}
|
||||
|
||||
public static Node infoCircle(String fill, double width, double height) {
|
||||
public static Node infoCircle(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath("M13,9H11V7H13M13,17H11V11H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z", fill, width, height);
|
||||
}
|
||||
|
||||
public static Node helpCircle(String fill, double width, double height) {
|
||||
public static Node helpCircle(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath("M15.07,11.25L14.17,12.17C13.45,12.89 13,13.5 13,15H11V14.5C11,13.39 11.45,12.39 12.17,11.67L13.41,10.41C13.78,10.05 14,9.55 14,9C14,7.89 13.1,7 12,7A2,2 0 0,0 10,9H8A4,4 0 0,1 12,5A4,4 0 0,1 16,9C16,9.88 15.64,10.67 15.07,11.25M13,19H11V17H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12C22,6.47 17.5,2 12,2Z", fill, width, height);
|
||||
}
|
||||
|
||||
public static Node alert(String fill, double width, double height) {
|
||||
public static Node alert(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath("M13,14H11V10H13M13,18H11V16H13M1,21H23L12,2L1,21Z", fill, width, height);
|
||||
}
|
||||
|
||||
public static Node plus(String fill, double width, double height) {
|
||||
public static Node plus(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath("M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z", fill, width, height);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -17,9 +17,8 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui;
|
||||
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import com.jfoenix.controls.JFXComboBox;
|
||||
import com.jfoenix.controls.JFXTextField;
|
||||
import com.jfoenix.controls.*;
|
||||
import com.jfoenix.effects.JFXDepthManager;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
@@ -28,20 +27,23 @@ import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.control.Toggle;
|
||||
import javafx.scene.control.ToggleGroup;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.text.Font;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.setting.*;
|
||||
import org.jackhuang.hmcl.ui.construct.FileItem;
|
||||
import org.jackhuang.hmcl.ui.construct.FontComboBox;
|
||||
import org.jackhuang.hmcl.ui.construct.MultiFileItem;
|
||||
import org.jackhuang.hmcl.ui.construct.Validator;
|
||||
import org.jackhuang.hmcl.ui.construct.*;
|
||||
import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
|
||||
import java.net.Proxy;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class SettingsPage extends StackPane implements DecoratorPage {
|
||||
private final StringProperty title = new SimpleStringProperty(this, "title", Main.i18n("settings.launcher"));
|
||||
@@ -53,12 +55,10 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
|
||||
@FXML
|
||||
private JFXTextField txtProxyUsername;
|
||||
@FXML
|
||||
private JFXTextField txtProxyPassword;
|
||||
private JFXPasswordField txtProxyPassword;
|
||||
@FXML
|
||||
private JFXTextField txtFontSize;
|
||||
@FXML
|
||||
private JFXComboBox<?> cboProxyType;
|
||||
@FXML
|
||||
private JFXComboBox<Label> cboLanguage;
|
||||
@FXML
|
||||
private JFXComboBox<?> cboDownloadSource;
|
||||
@@ -79,14 +79,25 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
|
||||
@FXML
|
||||
private MultiFileItem backgroundItem;
|
||||
@FXML
|
||||
private MultiFileItem themeItem;
|
||||
private StackPane themeColorPickerContainer;
|
||||
@FXML
|
||||
private JFXRadioButton chkNoProxy;
|
||||
@FXML
|
||||
private JFXRadioButton chkManualProxy;
|
||||
@FXML
|
||||
private JFXRadioButton chkProxyHttp;
|
||||
@FXML
|
||||
private JFXRadioButton chkProxySocks;
|
||||
@FXML
|
||||
private JFXCheckBox chkProxyAuthentication;
|
||||
@FXML
|
||||
private GridPane authPane;
|
||||
@FXML
|
||||
private Pane proxyPane;
|
||||
|
||||
{
|
||||
FXUtils.loadFXML(this, "/assets/fxml/setting.fxml");
|
||||
|
||||
FXUtils.limitWidth(cboLanguage, 400);
|
||||
FXUtils.limitWidth(cboDownloadSource, 400);
|
||||
|
||||
FXUtils.smoothScrolling(scroll);
|
||||
|
||||
txtProxyHost.setText(Settings.INSTANCE.getProxyHost());
|
||||
@@ -131,12 +142,39 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
|
||||
cboLanguage.getSelectionModel().select(Locales.LOCALES.indexOf(Settings.INSTANCE.getLocale()));
|
||||
cboLanguage.getSelectionModel().selectedIndexProperty().addListener((a, b, newValue) -> Settings.INSTANCE.setLocale(Locales.getLocale(newValue.intValue())));
|
||||
|
||||
cboProxyType.getSelectionModel().select(Proxies.PROXIES.indexOf(Settings.INSTANCE.getProxyType()));
|
||||
cboProxyType.getSelectionModel().selectedIndexProperty().addListener((a, b, newValue) -> Settings.INSTANCE.setProxyType(Proxies.getProxyType(newValue.intValue())));
|
||||
|
||||
ToggleGroup proxyConfigurationGroup = new ToggleGroup();
|
||||
chkProxyHttp.setUserData(Proxy.Type.HTTP);
|
||||
chkProxyHttp.setToggleGroup(proxyConfigurationGroup);
|
||||
chkProxySocks.setUserData(Proxy.Type.SOCKS);
|
||||
chkProxySocks.setToggleGroup(proxyConfigurationGroup);
|
||||
|
||||
for (Toggle toggle : proxyConfigurationGroup.getToggles())
|
||||
if (toggle.getUserData() == Settings.INSTANCE.getProxyType())
|
||||
toggle.setSelected(true);
|
||||
|
||||
ToggleGroup hasProxyGroup = new ToggleGroup();
|
||||
chkNoProxy.setToggleGroup(hasProxyGroup);
|
||||
chkManualProxy.setToggleGroup(hasProxyGroup);
|
||||
if (!Settings.INSTANCE.hasProxy())
|
||||
chkNoProxy.setSelected(true);
|
||||
else
|
||||
chkManualProxy.setSelected(true);
|
||||
proxyPane.disableProperty().bind(chkNoProxy.selectedProperty());
|
||||
|
||||
hasProxyGroup.selectedToggleProperty().addListener((a, b, newValue) ->
|
||||
Settings.INSTANCE.setHasProxy(newValue != chkNoProxy));
|
||||
|
||||
proxyConfigurationGroup.selectedToggleProperty().addListener((a, b, newValue) ->
|
||||
Settings.INSTANCE.setProxyType((Proxy.Type) newValue.getUserData()));
|
||||
|
||||
chkProxyAuthentication.setSelected(Settings.INSTANCE.hasProxyAuth());
|
||||
chkProxyAuthentication.selectedProperty().addListener((a, b, newValue) -> Settings.INSTANCE.setHasProxyAuth(newValue));
|
||||
authPane.disableProperty().bind(chkProxyAuthentication.selectedProperty().not());
|
||||
|
||||
fileCommonLocation.setProperty(Settings.INSTANCE.commonPathProperty());
|
||||
|
||||
FXUtils.installTooltip(btnUpdate, 0, 5000, 0, new Tooltip(Main.i18n("update.tooltip")));
|
||||
FXUtils.installTooltip(btnUpdate, Main.i18n("update.tooltip"));
|
||||
checkUpdate();
|
||||
|
||||
// background
|
||||
@@ -153,24 +191,21 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
|
||||
Settings.INSTANCE.backgroundImageTypeProperty().setChangedListener(it -> initBackgroundItemSubtitle());
|
||||
initBackgroundItemSubtitle();
|
||||
|
||||
backgroundItem.setToggleSelectedListener(newValue -> {
|
||||
Settings.INSTANCE.setBackgroundImageType((EnumBackgroundImage) newValue.getUserData());
|
||||
});
|
||||
backgroundItem.setToggleSelectedListener(newValue ->
|
||||
Settings.INSTANCE.setBackgroundImageType((EnumBackgroundImage) newValue.getUserData()));
|
||||
|
||||
// theme
|
||||
themeItem.loadChildren(Arrays.asList(
|
||||
themeItem.createChildren(Main.i18n("color.blue"), Theme.BLUE),
|
||||
themeItem.createChildren(Main.i18n("color.dark_blue"), Theme.DARK_BLUE),
|
||||
themeItem.createChildren(Main.i18n("color.green"), Theme.GREEN),
|
||||
themeItem.createChildren(Main.i18n("color.orange"), Theme.ORANGE),
|
||||
themeItem.createChildren(Main.i18n("color.purple"), Theme.PURPLE),
|
||||
themeItem.createChildren(Main.i18n("color.red"), Theme.RED)
|
||||
));
|
||||
|
||||
themeItem.getGroup().getToggles().stream().filter(it -> Settings.INSTANCE.getTheme() == it.getUserData()).findFirst().ifPresent(it -> it.setSelected(true));
|
||||
themeItem.setToggleSelectedListener(newValue -> Settings.INSTANCE.setTheme((Theme) newValue.getUserData()));
|
||||
Settings.INSTANCE.themeProperty().setChangedListenerAndOperate(it ->
|
||||
themeItem.setSubtitle(Main.i18n("color." + it.name().toLowerCase())));
|
||||
JFXColorPicker picker = new JFXColorPicker(Color.web(Settings.INSTANCE.getTheme().getColor()), null);
|
||||
picker.setCustomColorText(Main.i18n("color.custom"));
|
||||
picker.setRecentColorsText(Main.i18n("color.recent"));
|
||||
picker.getCustomColors().setAll(Arrays.stream(Theme.VALUES).map(Theme::getColor).map(Color::web).collect(Collectors.toList()));
|
||||
picker.setOnAction(e -> {
|
||||
Theme theme = Theme.custom(Theme.getColorDisplayName(picker.getValue()));
|
||||
Settings.INSTANCE.setTheme(theme);
|
||||
Controllers.getScene().getStylesheets().setAll(theme.getStylesheets());
|
||||
});
|
||||
themeColorPickerContainer.getChildren().setAll(picker);
|
||||
Platform.runLater(() -> JFXDepthManager.setDepth(picker, 0));
|
||||
}
|
||||
|
||||
private void initBackgroundItemSubtitle() {
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
package org.jackhuang.hmcl.ui;
|
||||
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.css.*;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.paint.Paint;
|
||||
import javafx.scene.text.Font;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class TwoLineListItem extends StackPane {
|
||||
|
||||
private final StringProperty title = new SimpleStringProperty(this, "title");
|
||||
private final StringProperty subtitle = new SimpleStringProperty(this, "subtitle");
|
||||
|
||||
private final StyleableObjectProperty<Font> titleFont = new SimpleStyleableObjectProperty<>(StyleableProperties.TITLE_FONT, this, "title-font", Font.font(15));
|
||||
private final StyleableObjectProperty<Font> subtitleFont = new SimpleStyleableObjectProperty<>(StyleableProperties.SUBTITLE_FONT, this, "subtitle-font", Font.getDefault());
|
||||
|
||||
private final StyleableObjectProperty<Paint> titleFill = new SimpleStyleableObjectProperty<>(StyleableProperties.TITLE_FILL, this, "title-fill", Color.BLACK);
|
||||
private final StyleableObjectProperty<Paint> subtitleFill = new SimpleStyleableObjectProperty<>(StyleableProperties.SUBTITLE_FILL, this, "subtitle-fill", Color.GRAY);
|
||||
|
||||
public TwoLineListItem(String titleString, String subtitleString) {
|
||||
this();
|
||||
|
||||
title.set(titleString);
|
||||
subtitle.set(subtitleString);
|
||||
}
|
||||
|
||||
public TwoLineListItem() {
|
||||
Label lblTitle = new Label();
|
||||
lblTitle.textFillProperty().bind(titleFill);
|
||||
lblTitle.fontProperty().bind(titleFont);
|
||||
lblTitle.textProperty().bind(title);
|
||||
|
||||
Label lblSubtitle = new Label();
|
||||
lblSubtitle.textFillProperty().bind(subtitleFill);
|
||||
lblSubtitle.fontProperty().bind(subtitleFont);
|
||||
lblSubtitle.textProperty().bind(subtitle);
|
||||
|
||||
VBox vbox = new VBox();
|
||||
vbox.getChildren().setAll(lblTitle, lblSubtitle);
|
||||
getChildren().setAll(vbox);
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title.get();
|
||||
}
|
||||
|
||||
public StringProperty titleProperty() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title.set(title);
|
||||
}
|
||||
|
||||
public String getSubtitle() {
|
||||
return subtitle.get();
|
||||
}
|
||||
|
||||
public StringProperty subtitleProperty() {
|
||||
return subtitle;
|
||||
}
|
||||
|
||||
public void setSubtitle(String subtitle) {
|
||||
this.subtitle.set(subtitle);
|
||||
}
|
||||
|
||||
public Font getTitleFont() {
|
||||
return titleFont.get();
|
||||
}
|
||||
|
||||
public StyleableObjectProperty<Font> titleFontProperty() {
|
||||
return titleFont;
|
||||
}
|
||||
|
||||
public void setTitleFont(Font titleFont) {
|
||||
this.titleFont.set(titleFont);
|
||||
}
|
||||
|
||||
public Font getSubtitleFont() {
|
||||
return subtitleFont.get();
|
||||
}
|
||||
|
||||
public StyleableObjectProperty<Font> subtitleFontProperty() {
|
||||
return subtitleFont;
|
||||
}
|
||||
|
||||
public void setSubtitleFont(Font subtitleFont) {
|
||||
this.subtitleFont.set(subtitleFont);
|
||||
}
|
||||
|
||||
public Paint getTitleFill() {
|
||||
return titleFill.get();
|
||||
}
|
||||
|
||||
public StyleableObjectProperty<Paint> titleFillProperty() {
|
||||
return titleFill;
|
||||
}
|
||||
|
||||
public void setTitleFill(Paint titleFill) {
|
||||
this.titleFill.set(titleFill);
|
||||
}
|
||||
|
||||
public Paint getSubtitleFill() {
|
||||
return subtitleFill.get();
|
||||
}
|
||||
|
||||
public StyleableObjectProperty<Paint> subtitleFillProperty() {
|
||||
return subtitleFill;
|
||||
}
|
||||
|
||||
public void setSubtitleFill(Paint subtitleFill) {
|
||||
this.subtitleFill.set(subtitleFill);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
|
||||
return getClassCssMetaData();
|
||||
}
|
||||
|
||||
public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
|
||||
return StyleableProperties.STYLEABLES;
|
||||
}
|
||||
|
||||
private static class StyleableProperties {
|
||||
|
||||
private static final FontCssMetaData<TwoLineListItem> TITLE_FONT = new FontCssMetaData<TwoLineListItem>("-jfx-title-font", Font.font(15)) {
|
||||
@Override
|
||||
public boolean isSettable(TwoLineListItem control) {
|
||||
return control.title == null || !control.title.isBound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StyleableProperty<Font> getStyleableProperty(TwoLineListItem control) {
|
||||
return control.titleFontProperty();
|
||||
}
|
||||
};
|
||||
|
||||
private static final FontCssMetaData<TwoLineListItem> SUBTITLE_FONT = new FontCssMetaData<TwoLineListItem>("-jfx-subtitle-font", Font.getDefault()) {
|
||||
@Override
|
||||
public boolean isSettable(TwoLineListItem control) {
|
||||
return control.subtitle == null || !control.subtitle.isBound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StyleableProperty<Font> getStyleableProperty(TwoLineListItem control) {
|
||||
return control.subtitleFontProperty();
|
||||
}
|
||||
};
|
||||
|
||||
private static final CssMetaData<TwoLineListItem, Paint> TITLE_FILL = new CssMetaData<TwoLineListItem, Paint>("-jfx-title-fill", StyleConverter.getPaintConverter(), Color.BLACK) {
|
||||
@Override
|
||||
public boolean isSettable(TwoLineListItem control) {
|
||||
return control.titleFill == null || !control.titleFill.isBound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StyleableProperty<Paint> getStyleableProperty(TwoLineListItem control) {
|
||||
return control.titleFillProperty();
|
||||
}
|
||||
};
|
||||
|
||||
private static final CssMetaData<TwoLineListItem, Paint> SUBTITLE_FILL = new CssMetaData<TwoLineListItem, Paint>("-jfx-subtitle-fill", StyleConverter.getPaintConverter(), Color.GRAY) {
|
||||
@Override
|
||||
public boolean isSettable(TwoLineListItem control) {
|
||||
return control.subtitleFill == null || !control.subtitleFill.isBound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StyleableProperty<Paint> getStyleableProperty(TwoLineListItem control) {
|
||||
return control.subtitleFillProperty();
|
||||
}
|
||||
};
|
||||
|
||||
private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
|
||||
|
||||
private StyleableProperties() {
|
||||
}
|
||||
|
||||
static {
|
||||
List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<>(Node.getClassCssMetaData());
|
||||
Collections.addAll(styleables, TITLE_FONT);
|
||||
Collections.addAll(styleables, SUBTITLE_FONT);
|
||||
STYLEABLES = Collections.unmodifiableList(styleables);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -22,7 +22,6 @@ import javafx.beans.binding.Bindings;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.effect.BlurType;
|
||||
import javafx.scene.effect.DropShadow;
|
||||
import javafx.scene.image.Image;
|
||||
@@ -34,6 +33,7 @@ import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.paint.Color;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -59,24 +59,24 @@ public final class VersionItem extends StackPane {
|
||||
@FXML
|
||||
private Label lblGameVersion;
|
||||
@FXML
|
||||
private Label lblLibraries;
|
||||
@FXML
|
||||
private ImageView iconView;
|
||||
|
||||
private EventHandler<? super MouseEvent> launchClickedHandler = null;
|
||||
|
||||
public VersionItem() {
|
||||
FXUtils.loadFXML(this, "/assets/fxml/version-item.fxml");
|
||||
FXUtils.limitWidth(this, 160);
|
||||
FXUtils.limitHeight(this, 156);
|
||||
setEffect(new DropShadow(BlurType.GAUSSIAN, Color.rgb(0, 0, 0, 0.26), 5.0, 0.12, -1.0, 1.0));
|
||||
btnSettings.setGraphic(SVG.gear("black", 15, 15));
|
||||
btnUpdate.setGraphic(SVG.update("black", 15, 15));
|
||||
btnLaunch.setGraphic(SVG.launch("black", 15, 15));
|
||||
btnScript.setGraphic(SVG.script("black", 15, 15));
|
||||
btnSettings.setGraphic(SVG.gear(Theme.blackFillBinding(), 15, 15));
|
||||
btnUpdate.setGraphic(SVG.update(Theme.blackFillBinding(), 15, 15));
|
||||
btnLaunch.setGraphic(SVG.launch(Theme.blackFillBinding(), 15, 15));
|
||||
btnScript.setGraphic(SVG.script(Theme.blackFillBinding(), 15, 15));
|
||||
|
||||
FXUtils.installTooltip(btnSettings, 0, 5000, 0, new Tooltip(Main.i18n("version.settings")));
|
||||
FXUtils.installTooltip(btnUpdate, 0, 5000, 0, new Tooltip(Main.i18n("version.update")));
|
||||
FXUtils.installTooltip(btnLaunch, 0, 5000, 0, new Tooltip(Main.i18n("version.launch")));
|
||||
FXUtils.installTooltip(btnScript, 0, 5000, 0, new Tooltip(Main.i18n("version.launch_script")));
|
||||
FXUtils.installTooltip(btnSettings, Main.i18n("version.settings"));
|
||||
FXUtils.installTooltip(btnUpdate, Main.i18n("version.update"));
|
||||
FXUtils.installTooltip(btnLaunch, Main.i18n("version.launch"));
|
||||
FXUtils.installTooltip(btnScript, Main.i18n("version.launch_script"));
|
||||
|
||||
icon.translateYProperty().bind(Bindings.createDoubleBinding(() -> header.getBoundsInParent().getHeight() - icon.getHeight() / 2 - 16, header.boundsInParentProperty(), icon.heightProperty()));
|
||||
FXUtils.limitSize(iconView, 32, 32);
|
||||
@@ -100,6 +100,10 @@ public final class VersionItem extends StackPane {
|
||||
lblGameVersion.setText(gameVersion);
|
||||
}
|
||||
|
||||
public void setLibraries(String libraries) {
|
||||
lblLibraries.setText(libraries);
|
||||
}
|
||||
|
||||
public void setImage(Image image) {
|
||||
iconView.setImage(image);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -46,8 +46,6 @@ public final class VersionListItem extends StackPane {
|
||||
lblGameVersion.setText(gameVersion);
|
||||
|
||||
FXUtils.limitSize(imageView, 32, 32);
|
||||
FXUtils.limitWidth(imageViewContainer, 32);
|
||||
FXUtils.limitHeight(imageViewContainer, 32);
|
||||
}
|
||||
|
||||
@FXML
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -25,7 +25,6 @@ import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Tab;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.download.game.GameAssetIndexDownloadTask;
|
||||
@@ -66,8 +65,8 @@ public final class VersionPage extends StackPane implements DecoratorPage {
|
||||
@FXML
|
||||
private JFXTabPane tabPane;
|
||||
|
||||
private JFXPopup browsePopup;
|
||||
private JFXPopup managementPopup;
|
||||
private final JFXPopup browsePopup;
|
||||
private final JFXPopup managementPopup;
|
||||
|
||||
private Profile profile;
|
||||
private String version;
|
||||
@@ -80,10 +79,10 @@ public final class VersionPage extends StackPane implements DecoratorPage {
|
||||
browsePopup = new JFXPopup(browseList);
|
||||
managementPopup = new JFXPopup(managementList);
|
||||
|
||||
FXUtils.installTooltip(btnDelete, 0, 5000, 0, new Tooltip(Main.i18n("version.manage.remove")));
|
||||
FXUtils.installTooltip(btnBrowseMenu, 0, 5000, 0, new Tooltip(Main.i18n("settings.game.exploration")));
|
||||
FXUtils.installTooltip(btnManagementMenu, 0, 5000, 0, new Tooltip(Main.i18n("settings.game.management")));
|
||||
FXUtils.installTooltip(btnExport, 0, 5000, 0, new Tooltip(Main.i18n("modpack.export")));
|
||||
FXUtils.installTooltip(btnDelete, Main.i18n("version.manage.remove"));
|
||||
FXUtils.installTooltip(btnBrowseMenu, Main.i18n("settings.game.exploration"));
|
||||
FXUtils.installTooltip(btnManagementMenu, Main.i18n("settings.game.management"));
|
||||
FXUtils.installTooltip(btnExport, Main.i18n("modpack.export"));
|
||||
}
|
||||
|
||||
public void load(String id, Profile profile) {
|
||||
@@ -162,7 +161,7 @@ public final class VersionPage extends StackPane implements DecoratorPage {
|
||||
deleteVersion(profile, version);
|
||||
break;
|
||||
case 2: // redownload asset index
|
||||
new GameAssetIndexDownloadTask(profile.getDependency(), profile.getRepository().getVersion(version).resolve(profile.getRepository())).start();
|
||||
new GameAssetIndexDownloadTask(profile.getDependency(), profile.getRepository().getResolvedVersion(version)).start();
|
||||
break;
|
||||
case 3: // delete libraries
|
||||
FileUtils.deleteDirectoryQuietly(new File(profile.getRepository().getBaseDirectory(), "libraries"));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -24,11 +24,8 @@ import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.control.Toggle;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.stage.FileChooser;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.setting.EnumGameDirectory;
|
||||
@@ -37,8 +34,8 @@ import org.jackhuang.hmcl.setting.VersionSetting;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.construct.ComponentList;
|
||||
import org.jackhuang.hmcl.ui.construct.ImagePickerItem;
|
||||
import org.jackhuang.hmcl.ui.construct.MultiFileItem;
|
||||
import org.jackhuang.hmcl.ui.construct.NumberValidator;
|
||||
import org.jackhuang.hmcl.util.*;
|
||||
|
||||
import java.io.File;
|
||||
@@ -76,8 +73,7 @@ public final class VersionSettingsController {
|
||||
@FXML private MultiFileItem javaItem;
|
||||
@FXML private MultiFileItem gameDirItem;
|
||||
@FXML private JFXToggleButton chkShowLogs;
|
||||
@FXML private JFXButton btnIconSelection;
|
||||
@FXML private ImageView iconView;
|
||||
@FXML private ImagePickerItem iconPickerItem;
|
||||
|
||||
@FXML
|
||||
private void initialize() {
|
||||
@@ -85,33 +81,9 @@ public final class VersionSettingsController {
|
||||
|
||||
FXUtils.smoothScrolling(scroll);
|
||||
|
||||
double limitWidth = 300;
|
||||
FXUtils.limitWidth(txtMaxMemory, limitWidth);
|
||||
FXUtils.limitWidth(cboLauncherVisibility, limitWidth);
|
||||
|
||||
double limitHeight = 10;
|
||||
FXUtils.limitHeight(chkNoJVMArgs, limitHeight);
|
||||
FXUtils.limitHeight(chkNoCommon, limitHeight);
|
||||
FXUtils.limitHeight(chkNoGameCheck, limitHeight);
|
||||
FXUtils.limitHeight(chkShowLogs, limitHeight);
|
||||
|
||||
NumberValidator nonnull = new NumberValidator("Must be a number.", false);
|
||||
NumberValidator nullable = new NumberValidator("Must be a number.", true);
|
||||
|
||||
txtWidth.setValidators(nonnull);
|
||||
FXUtils.setValidateWhileTextChanged(txtWidth);
|
||||
txtHeight.setValidators(nonnull);
|
||||
FXUtils.setValidateWhileTextChanged(txtHeight);
|
||||
txtMaxMemory.setValidators(nonnull);
|
||||
FXUtils.setValidateWhileTextChanged(txtMaxMemory);
|
||||
txtMetaspace.setValidators(nullable);
|
||||
FXUtils.setValidateWhileTextChanged(txtMetaspace);
|
||||
|
||||
Task.of(variables -> {
|
||||
variables.set("list", JavaVersion.getJREs().values().stream().map(javaVersion ->
|
||||
javaItem.createChildren(javaVersion.getVersion(), javaVersion.getBinary().getAbsolutePath(), javaVersion)
|
||||
).collect(Collectors.toList()));
|
||||
}).subscribe(Schedulers.javafx(), variables ->
|
||||
Task.of(variables -> variables.set("list", JavaVersion.getJREs().values().stream().map(javaVersion ->
|
||||
javaItem.createChildren(javaVersion.getVersion(), javaVersion.getBinary().getAbsolutePath(), javaVersion)
|
||||
).collect(Collectors.toList()))).subscribe(Schedulers.javafx(), variables ->
|
||||
javaItem.loadChildren(variables.<Collection<Node>>get("list"))
|
||||
);
|
||||
|
||||
@@ -127,8 +99,6 @@ public final class VersionSettingsController {
|
||||
globalItem.createChildren(Main.i18n("settings.type.global"), true),
|
||||
globalItem.createChildren(Main.i18n("settings.type.special"), false)
|
||||
));
|
||||
|
||||
FXUtils.installTooltip(btnIconSelection, 0, 5000, 0, new Tooltip(Main.i18n("button.edit")));
|
||||
}
|
||||
|
||||
public void loadVersionSetting(Profile profile, String versionId) {
|
||||
@@ -226,9 +196,7 @@ public final class VersionSettingsController {
|
||||
.findFirst().ifPresent(toggle -> toggle.setSelected(true));
|
||||
|
||||
gameDirItem.setCustomUserData(EnumGameDirectory.CUSTOM);
|
||||
gameDirItem.setToggleSelectedListener(newValue -> {
|
||||
versionSetting.setGameDirType((EnumGameDirectory) newValue.getUserData());
|
||||
});
|
||||
gameDirItem.setToggleSelectedListener(newValue -> versionSetting.setGameDirType((EnumGameDirectory) newValue.getUserData()));
|
||||
|
||||
versionSetting.gameDirProperty().setChangedListener(it -> initGameDirSubtitle(versionSetting));
|
||||
versionSetting.gameDirTypeProperty().setChangedListener(it -> initGameDirSubtitle(versionSetting));
|
||||
@@ -277,9 +245,9 @@ public final class VersionSettingsController {
|
||||
private void loadIcon() {
|
||||
File iconFile = profile.getRepository().getVersionIcon(versionId);
|
||||
if (iconFile.exists())
|
||||
iconView.setImage(new Image("file:" + iconFile.getAbsolutePath()));
|
||||
iconPickerItem.setImage(new Image("file:" + iconFile.getAbsolutePath()));
|
||||
else
|
||||
iconView.setImage(FXUtils.DEFAULT_ICON);
|
||||
FXUtils.limitSize(iconView, 32, 32);
|
||||
iconPickerItem.setImage(FXUtils.DEFAULT_ICON);
|
||||
FXUtils.limitSize(iconPickerItem.getImageView(), 32, 32);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright c 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright c 2018 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
|
||||
@@ -83,7 +83,7 @@ public enum ContainerAnimations {
|
||||
new KeyValue(c.getCurrentNode().translateXProperty(), 0, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getPreviousNode().translateXProperty(), c.getCurrentRoot().getWidth(), Interpolator.EASE_BOTH))));
|
||||
|
||||
private AnimationProducer animationProducer;
|
||||
private final AnimationProducer animationProducer;
|
||||
|
||||
ContainerAnimations(AnimationProducer animationProducer) {
|
||||
this.animationProducer = animationProducer;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -21,12 +21,8 @@ import javafx.animation.KeyFrame;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.SnapshotParameters;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.image.WritableImage;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.util.Duration;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
|
||||
public final class TransitionHandler implements AnimationHandler {
|
||||
private final StackPane view;
|
||||
@@ -77,9 +73,7 @@ public final class TransitionHandler implements AnimationHandler {
|
||||
|
||||
Timeline nowAnimation = new Timeline();
|
||||
nowAnimation.getKeyFrames().addAll(transition.animate(this));
|
||||
nowAnimation.getKeyFrames().add(new KeyFrame(duration, e -> {
|
||||
view.getChildren().remove(previousNode);
|
||||
}));
|
||||
nowAnimation.getKeyFrames().add(new KeyFrame(duration, e -> view.getChildren().remove(previousNode)));
|
||||
nowAnimation.play();
|
||||
animation = nowAnimation;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -31,17 +31,17 @@ import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
import javafx.util.Duration;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.SVG;
|
||||
|
||||
/**
|
||||
* @author
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public class ComponentListCell extends StackPane {
|
||||
private final Node content;
|
||||
private Animation expandAnimation;
|
||||
private Rectangle clipRect;
|
||||
private double animatedHeight;
|
||||
private final BooleanProperty expanded = new SimpleBooleanProperty(this, "expanded", false);
|
||||
|
||||
public ComponentListCell(Node content) {
|
||||
@@ -78,7 +78,7 @@ public class ComponentListCell extends StackPane {
|
||||
StackPane groupNode = new StackPane();
|
||||
groupNode.getStyleClass().add("options-list-item-header");
|
||||
|
||||
Node expandIcon = SVG.expand("black", 10, 10);
|
||||
Node expandIcon = SVG.expand(Theme.blackFillBinding(), 10, 10);
|
||||
JFXButton expandButton = new JFXButton();
|
||||
expandButton.setGraphic(expandIcon);
|
||||
expandButton.getStyleClass().add("options-list-item-expand-button");
|
||||
@@ -103,7 +103,7 @@ public class ComponentListCell extends StackPane {
|
||||
|
||||
VBox container = new VBox();
|
||||
container.setStyle("-fx-padding: 8 0 0 0;");
|
||||
FXUtils.limitHeight(container, 0);
|
||||
FXUtils.setLimitHeight(container, 0);
|
||||
Rectangle clipRect = new Rectangle();
|
||||
clipRect.widthProperty().bind(container.widthProperty());
|
||||
clipRect.heightProperty().bind(container.heightProperty());
|
||||
@@ -129,26 +129,20 @@ public class ComponentListCell extends StackPane {
|
||||
updateClip(newHeight);
|
||||
}
|
||||
|
||||
animatedHeight = newAnimatedHeight;
|
||||
|
||||
expandAnimation = new Timeline(new KeyFrame(new Duration(320.0),
|
||||
new KeyValue(container.minHeightProperty(), contentHeight, FXUtils.SINE),
|
||||
new KeyValue(container.maxHeightProperty(), contentHeight, FXUtils.SINE)
|
||||
));
|
||||
|
||||
if (!isExpanded()) {
|
||||
expandAnimation.setOnFinished(e2 -> {
|
||||
updateClip(newHeight);
|
||||
animatedHeight = 0.0;
|
||||
});
|
||||
expandAnimation.setOnFinished(e2 -> updateClip(newHeight));
|
||||
}
|
||||
|
||||
expandAnimation.play();
|
||||
});
|
||||
|
||||
expandedProperty().addListener((a, b, newValue) -> {
|
||||
expandIcon.setRotate(newValue ? 180 : 0);
|
||||
});
|
||||
expandedProperty().addListener((a, b, newValue) ->
|
||||
expandIcon.setRotate(newValue ? 180 : 0));
|
||||
|
||||
getChildren().setAll(holder);
|
||||
} else
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -27,6 +27,7 @@ import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.stage.DirectoryChooser;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.SVG;
|
||||
@@ -50,10 +51,10 @@ public class FileItem extends BorderPane {
|
||||
setLeft(left);
|
||||
|
||||
JFXButton right = new JFXButton();
|
||||
right.setGraphic(SVG.pencil("black", 15, 15));
|
||||
right.setGraphic(SVG.pencil(Theme.blackFillBinding(), 15, 15));
|
||||
right.getStyleClass().add("toggle-icon4");
|
||||
right.setOnMouseClicked(e -> onExplore());
|
||||
FXUtils.installTooltip(right, 0, 5000, 0, new Tooltip(Main.i18n("button.edit")));
|
||||
FXUtils.installTooltip(right, Main.i18n("button.edit"));
|
||||
setRight(right);
|
||||
|
||||
Tooltip tip = new Tooltip();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
package org.jackhuang.hmcl.ui.construct;
|
||||
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import javafx.beans.DefaultProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.SVG;
|
||||
|
||||
@DefaultProperty("image")
|
||||
public final class ImagePickerItem extends BorderPane {
|
||||
|
||||
private final ImageView imageView;
|
||||
private final JFXButton selectButton;
|
||||
private final Label label;
|
||||
|
||||
private final StringProperty title = new SimpleStringProperty(this, "title");
|
||||
private final ObjectProperty<EventHandler<? super MouseEvent>> onSelectButtonClicked = new SimpleObjectProperty<>(this, "onSelectButtonClicked");
|
||||
private final ObjectProperty<Image> image = new SimpleObjectProperty<>(this, "image");
|
||||
|
||||
public ImagePickerItem() {
|
||||
imageView = new ImageView();
|
||||
imageView.setSmooth(false);
|
||||
imageView.setPreserveRatio(true);
|
||||
|
||||
selectButton = new JFXButton();
|
||||
selectButton.setGraphic(SVG.pencil(Theme.blackFillBinding(), 15, 15));
|
||||
selectButton.onMouseClickedProperty().bind(onSelectButtonClicked);
|
||||
selectButton.getStyleClass().add("toggle-icon4");
|
||||
|
||||
FXUtils.installTooltip(selectButton, Main.i18n("button.edit"));
|
||||
|
||||
HBox hBox = new HBox();
|
||||
hBox.getChildren().setAll(imageView, selectButton);
|
||||
hBox.setAlignment(Pos.CENTER_RIGHT);
|
||||
hBox.setSpacing(8);
|
||||
setRight(hBox);
|
||||
|
||||
VBox vBox = new VBox();
|
||||
label = new Label();
|
||||
label.textProperty().bind(title);
|
||||
vBox.getChildren().setAll(label);
|
||||
vBox.setAlignment(Pos.CENTER_LEFT);
|
||||
setLeft(vBox);
|
||||
|
||||
imageView.imageProperty().bind(image);
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title.get();
|
||||
}
|
||||
|
||||
public StringProperty titleProperty() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title.set(title);
|
||||
}
|
||||
|
||||
public EventHandler<? super MouseEvent> getOnSelectButtonClicked() {
|
||||
return onSelectButtonClicked.get();
|
||||
}
|
||||
|
||||
public ObjectProperty<EventHandler<? super MouseEvent>> onSelectButtonClickedProperty() {
|
||||
return onSelectButtonClicked;
|
||||
}
|
||||
|
||||
public void setOnSelectButtonClicked(EventHandler<? super MouseEvent> onSelectButtonClicked) {
|
||||
this.onSelectButtonClicked.set(onSelectButtonClicked);
|
||||
}
|
||||
|
||||
public Image getImage() {
|
||||
return image.get();
|
||||
}
|
||||
|
||||
public ObjectProperty<Image> imageProperty() {
|
||||
return image;
|
||||
}
|
||||
|
||||
public void setImage(Image image) {
|
||||
this.image.set(image);
|
||||
}
|
||||
|
||||
public ImageView getImageView() {
|
||||
return imageView;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -106,12 +106,12 @@ public final class MessageBox {
|
||||
* @param title The title of MsgBox.
|
||||
* @return user operation.
|
||||
*/
|
||||
public static void show(String message, String title) {
|
||||
public static Optional<ButtonType> show(String message, String title) {
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||
alert.setTitle(title);
|
||||
alert.setHeaderText(title);
|
||||
alert.setContentText(message);
|
||||
alert.showAndWait();
|
||||
return alert.showAndWait();
|
||||
}
|
||||
|
||||
public static int confirm(String message, String title) {
|
||||
@@ -137,6 +137,8 @@ public final class MessageBox {
|
||||
case OK_CANCEL_OPTION:
|
||||
alert.getButtonTypes().setAll(ButtonType.OK, ButtonType.CANCEL);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unrecognized message box option " + option);
|
||||
}
|
||||
Optional<ButtonType> buttonType = alert.showAndWait();
|
||||
if (!buttonType.isPresent()) return CLOSED_OPTION;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 4017 huangyuhui <huanghongxun4008@126.com>
|
||||
* Copyright (C) 2018 huangyuhui <huanghongxun4008@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
|
||||
@@ -24,6 +24,7 @@ import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.SVG;
|
||||
|
||||
@@ -62,20 +63,22 @@ public final class MessageDialogPane extends StackPane {
|
||||
|
||||
switch (type) {
|
||||
case MessageBox.INFORMATION_MESSAGE:
|
||||
graphic.setGraphic(SVG.infoCircle("black", 40, 40));
|
||||
graphic.setGraphic(SVG.infoCircle(Theme.blackFillBinding(), 40, 40));
|
||||
break;
|
||||
case MessageBox.ERROR_MESSAGE:
|
||||
graphic.setGraphic(SVG.closeCircle("black", 40, 40));
|
||||
graphic.setGraphic(SVG.closeCircle(Theme.blackFillBinding(), 40, 40));
|
||||
break;
|
||||
case MessageBox.FINE_MESSAGE:
|
||||
graphic.setGraphic(SVG.checkCircle("black", 40, 40));
|
||||
graphic.setGraphic(SVG.checkCircle(Theme.blackFillBinding(), 40, 40));
|
||||
break;
|
||||
case MessageBox.WARNING_MESSAGE:
|
||||
graphic.setGraphic(SVG.alert("black", 40, 40));
|
||||
graphic.setGraphic(SVG.alert(Theme.blackFillBinding(), 40, 40));
|
||||
break;
|
||||
case MessageBox.QUESTION_MESSAGE:
|
||||
graphic.setGraphic(SVG.helpCircle("black", 40, 40));
|
||||
graphic.setGraphic(SVG.helpCircle(Theme.blackFillBinding(), 40, 40));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unrecognized message box message type " + type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
package org.jackhuang.hmcl.ui.construct;
|
||||
|
||||
import com.jfoenix.controls.JFXColorPicker;
|
||||
import com.jfoenix.controls.JFXRadioButton;
|
||||
import javafx.beans.NamedArg;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Toggle;
|
||||
import javafx.scene.control.ToggleGroup;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.paint.Color;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class MultiColorItem extends ComponentList {
|
||||
private final StringProperty customTitle = new SimpleStringProperty(this, "customTitle", Main.i18n("selector.custom"));
|
||||
private final StringProperty chooserTitle = new SimpleStringProperty(this, "chooserTitle", Main.i18n("selector.choose_file"));
|
||||
|
||||
private final ToggleGroup group = new ToggleGroup();
|
||||
private final JFXColorPicker colorPicker = new JFXColorPicker();
|
||||
private final JFXRadioButton radioCustom = new JFXRadioButton();
|
||||
private final BorderPane custom = new BorderPane();
|
||||
private final VBox pane = new VBox();
|
||||
private final boolean hasCustom;
|
||||
|
||||
private Consumer<Toggle> toggleSelectedListener;
|
||||
private Consumer<Color> colorConsumer;
|
||||
|
||||
public MultiColorItem(@NamedArg(value = "hasCustom", defaultValue = "true") boolean hasCustom) {
|
||||
this.hasCustom = hasCustom;
|
||||
|
||||
radioCustom.textProperty().bind(customTitleProperty());
|
||||
radioCustom.setToggleGroup(group);
|
||||
colorPicker.disableProperty().bind(radioCustom.selectedProperty().not());
|
||||
|
||||
colorPicker.setOnAction(e -> Optional.ofNullable(colorConsumer).ifPresent(c -> c.accept(colorPicker.getValue())));
|
||||
|
||||
custom.setLeft(radioCustom);
|
||||
custom.setStyle("-fx-padding: 3;");
|
||||
HBox right = new HBox();
|
||||
right.setSpacing(3);
|
||||
right.getChildren().addAll(colorPicker);
|
||||
custom.setRight(right);
|
||||
FXUtils.setLimitHeight(custom, 40);
|
||||
|
||||
pane.setStyle("-fx-padding: 0 0 10 0;");
|
||||
pane.setSpacing(8);
|
||||
|
||||
if (hasCustom)
|
||||
pane.getChildren().add(custom);
|
||||
addChildren(pane);
|
||||
|
||||
group.selectedToggleProperty().addListener((a, b, newValue) -> {
|
||||
if (toggleSelectedListener != null)
|
||||
toggleSelectedListener.accept(newValue);
|
||||
});
|
||||
}
|
||||
|
||||
public Node createChildren(String title) {
|
||||
return createChildren(title, null);
|
||||
}
|
||||
|
||||
public Node createChildren(String title, Object userData) {
|
||||
return createChildren(title, "", userData);
|
||||
}
|
||||
|
||||
public Node createChildren(String title, String subtitle, Object userData) {
|
||||
BorderPane pane = new BorderPane();
|
||||
pane.setStyle("-fx-padding: 3;");
|
||||
|
||||
JFXRadioButton left = new JFXRadioButton(title);
|
||||
left.setToggleGroup(group);
|
||||
left.setUserData(userData);
|
||||
pane.setLeft(left);
|
||||
|
||||
Label right = new Label(subtitle);
|
||||
right.getStyleClass().add("subtitle-label");
|
||||
right.setStyle("-fx-font-size: 10;");
|
||||
pane.setRight(right);
|
||||
|
||||
return pane;
|
||||
}
|
||||
|
||||
public void loadChildren(Collection<Node> list) {
|
||||
pane.getChildren().setAll(list);
|
||||
|
||||
if (hasCustom)
|
||||
pane.getChildren().add(custom);
|
||||
}
|
||||
|
||||
public ToggleGroup getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
public String getCustomTitle() {
|
||||
return customTitle.get();
|
||||
}
|
||||
|
||||
public StringProperty customTitleProperty() {
|
||||
return customTitle;
|
||||
}
|
||||
|
||||
public void setCustomTitle(String customTitle) {
|
||||
this.customTitle.set(customTitle);
|
||||
}
|
||||
|
||||
public String getChooserTitle() {
|
||||
return chooserTitle.get();
|
||||
}
|
||||
|
||||
public StringProperty chooserTitleProperty() {
|
||||
return chooserTitle;
|
||||
}
|
||||
|
||||
public void setChooserTitle(String chooserTitle) {
|
||||
this.chooserTitle.set(chooserTitle);
|
||||
}
|
||||
|
||||
public void setCustomUserData(Object userData) {
|
||||
radioCustom.setUserData(userData);
|
||||
}
|
||||
|
||||
public boolean isCustomToggle(Toggle toggle) {
|
||||
return radioCustom == toggle;
|
||||
}
|
||||
|
||||
public void setToggleSelectedListener(Consumer<Toggle> consumer) {
|
||||
toggleSelectedListener = consumer;
|
||||
}
|
||||
|
||||
public void setOnColorPickerChanged(Consumer<Color> consumer) {
|
||||
colorConsumer = consumer;
|
||||
}
|
||||
|
||||
public void setColor(Color color) {
|
||||
colorPicker.setValue(color);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -32,12 +32,14 @@ import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Toggle;
|
||||
import javafx.scene.control.ToggleGroup;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.stage.DirectoryChooser;
|
||||
import javafx.stage.FileChooser;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.SVG;
|
||||
@@ -50,7 +52,8 @@ public class MultiFileItem extends ComponentList {
|
||||
private final StringProperty customTitle = new SimpleStringProperty(this, "customTitle", Main.i18n("selector.custom"));
|
||||
private final StringProperty chooserTitle = new SimpleStringProperty(this, "chooserTitle", Main.i18n("selector.choose_file"));
|
||||
private final BooleanProperty directory = new SimpleBooleanProperty(this, "directory", false);
|
||||
private ObservableList<FileChooser.ExtensionFilter> extensionFilters = FXCollections.observableArrayList();
|
||||
private final SimpleStringProperty tooltip = new SimpleStringProperty(this, "tooltip");
|
||||
private final ObservableList<FileChooser.ExtensionFilter> extensionFilters = FXCollections.observableArrayList();
|
||||
|
||||
private final ToggleGroup group = new ToggleGroup();
|
||||
private final JFXTextField txtCustom = new JFXTextField();
|
||||
@@ -67,7 +70,7 @@ public class MultiFileItem extends ComponentList {
|
||||
|
||||
BorderPane.setAlignment(txtCustom, Pos.CENTER_RIGHT);
|
||||
|
||||
btnSelect.setGraphic(SVG.folderOpen("black", 15, 15));
|
||||
btnSelect.setGraphic(SVG.folderOpen(Theme.blackFillBinding(), 15, 15));
|
||||
btnSelect.setOnMouseClicked(e -> {
|
||||
if (isDirectory()) {
|
||||
DirectoryChooser chooser = new DirectoryChooser();
|
||||
@@ -96,7 +99,7 @@ public class MultiFileItem extends ComponentList {
|
||||
right.setSpacing(3);
|
||||
right.getChildren().addAll(txtCustom, btnSelect);
|
||||
custom.setRight(right);
|
||||
FXUtils.limitHeight(custom, 20);
|
||||
FXUtils.setLimitHeight(custom, 20);
|
||||
|
||||
pane.setStyle("-fx-padding: 0 0 10 0;");
|
||||
pane.setSpacing(8);
|
||||
@@ -109,6 +112,10 @@ public class MultiFileItem extends ComponentList {
|
||||
if (toggleSelectedListener != null)
|
||||
toggleSelectedListener.accept(newValue);
|
||||
});
|
||||
|
||||
Tooltip tip = new Tooltip();
|
||||
tip.textProperty().bind(tooltipProperty());
|
||||
Tooltip.install(this, tip);
|
||||
}
|
||||
|
||||
public Node createChildren(String title) {
|
||||
@@ -122,7 +129,7 @@ public class MultiFileItem extends ComponentList {
|
||||
public Node createChildren(String title, String subtitle, Object userData) {
|
||||
BorderPane pane = new BorderPane();
|
||||
pane.setStyle("-fx-padding: 3;");
|
||||
FXUtils.limitHeight(pane, 20);
|
||||
FXUtils.setLimitHeight(pane, 20);
|
||||
|
||||
JFXRadioButton left = new JFXRadioButton(title);
|
||||
left.setToggleGroup(group);
|
||||
@@ -223,4 +230,16 @@ public class MultiFileItem extends ComponentList {
|
||||
public ObservableList<FileChooser.ExtensionFilter> getExtensionFilters() {
|
||||
return extensionFilters;
|
||||
}
|
||||
|
||||
public String getTooltip() {
|
||||
return tooltip.get();
|
||||
}
|
||||
|
||||
public StringProperty tooltipProperty() {
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
public void setTooltip(String tooltip) {
|
||||
this.tooltip.set(tooltip);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -21,13 +21,9 @@ import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.control.MultipleSelectionModel;
|
||||
|
||||
public final class NoneMultipleSelectionModel<T> extends MultipleSelectionModel<T> {
|
||||
private static final NoneMultipleSelectionModel INSTANCE = new NoneMultipleSelectionModel();
|
||||
private NoneMultipleSelectionModel() {}
|
||||
public class NoneMultipleSelectionModel<T> extends MultipleSelectionModel<T> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> NoneMultipleSelectionModel<T> getInstance() {
|
||||
return (NoneMultipleSelectionModel<T>) INSTANCE;
|
||||
public NoneMultipleSelectionModel() {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -18,21 +18,23 @@
|
||||
package org.jackhuang.hmcl.ui.construct;
|
||||
|
||||
import com.jfoenix.validation.base.ValidatorBase;
|
||||
import javafx.beans.NamedArg;
|
||||
import javafx.scene.control.TextInputControl;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
|
||||
public class NumberValidator extends ValidatorBase {
|
||||
private boolean nullable;
|
||||
private final boolean nullable;
|
||||
|
||||
public NumberValidator() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
public NumberValidator(boolean nullable) {
|
||||
public NumberValidator(@NamedArg("nullable") boolean nullable) {
|
||||
this.nullable = nullable;
|
||||
}
|
||||
|
||||
public NumberValidator(String message, boolean nullable) {
|
||||
public NumberValidator(@NamedArg("message") String message, @NamedArg("nullable") boolean nullable) {
|
||||
super(message);
|
||||
this.nullable = nullable;
|
||||
}
|
||||
@@ -48,13 +50,8 @@ public class NumberValidator extends ValidatorBase {
|
||||
TextInputControl textField = ((TextInputControl) srcControl.get());
|
||||
|
||||
if (StringUtils.isBlank(textField.getText()))
|
||||
hasErrors.set(nullable);
|
||||
hasErrors.set(!nullable);
|
||||
else
|
||||
try {
|
||||
Integer.parseInt(textField.getText());
|
||||
hasErrors.set(false);
|
||||
} catch (NumberFormatException e) {
|
||||
hasErrors.set(true);
|
||||
}
|
||||
hasErrors.set(Lang.toIntOrNull(textField.getText()) == null);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -30,7 +30,6 @@ import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.css.*;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.layout.Background;
|
||||
import javafx.scene.layout.BackgroundFill;
|
||||
import javafx.scene.layout.CornerRadii;
|
||||
@@ -217,10 +216,12 @@ public class RipplerContainer extends StackPane {
|
||||
private static class StyleableProperties {
|
||||
|
||||
private static final CssMetaData<RipplerContainer, Paint> RIPPLER_FILL = new CssMetaData<RipplerContainer, Paint>("-jfx-rippler-fill", StyleConverter.getPaintConverter(), Color.rgb(0, 200, 255)) {
|
||||
@Override
|
||||
public boolean isSettable(RipplerContainer control) {
|
||||
return control.ripplerFill == null || !control.ripplerFill.isBound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StyleableProperty<Paint> getStyleableProperty(RipplerContainer control) {
|
||||
return control.ripplerFillProperty();
|
||||
}
|
||||
@@ -232,7 +233,7 @@ public class RipplerContainer extends StackPane {
|
||||
}
|
||||
|
||||
static {
|
||||
List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<>(Parent.getClassCssMetaData());
|
||||
List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<>(Node.getClassCssMetaData());
|
||||
Collections.addAll(styleables, RIPPLER_FILL);
|
||||
STYLEABLES = Collections.unmodifiableList(styleables);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -47,9 +47,6 @@ public class TaskExecutorDialogPane extends StackPane {
|
||||
public TaskExecutorDialogPane(Runnable cancel) {
|
||||
FXUtils.loadFXML(this, "/assets/fxml/task-dialog.fxml");
|
||||
|
||||
FXUtils.limitHeight(this, 200);
|
||||
FXUtils.limitWidth(this, 400);
|
||||
|
||||
setCancel(cancel);
|
||||
|
||||
btnCancel.setOnMouseClicked(e -> {
|
||||
|
||||
+17
-18
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -57,24 +57,23 @@ public interface TaskExecutorDialogWizardDisplayer extends AbstractWizardDisplay
|
||||
}
|
||||
|
||||
JFXUtilities.runInFX(() -> {
|
||||
TaskExecutor executor = task.executor(e -> new TaskListener() {
|
||||
TaskExecutor executor = task.executor(new TaskListener() {
|
||||
@Override
|
||||
public void onSucceed() {
|
||||
if (settings.containsKey("success_message") && settings.get("success_message") instanceof String)
|
||||
JFXUtilities.runInFX(() -> Controllers.dialog((String) settings.get("success_message"), null, MessageBox.FINE_MESSAGE, () -> Controllers.navigate(null)));
|
||||
else if (!settings.containsKey("forbid_success_message"))
|
||||
JFXUtilities.runInFX(() -> Controllers.dialog(Main.i18n("message.success"), null, MessageBox.FINE_MESSAGE, () -> Controllers.navigate(null)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTerminate() {
|
||||
if (e.getLastException() == null)
|
||||
return;
|
||||
String appendix = StringUtils.getStackTrace(e.getLastException());
|
||||
if (settings.containsKey("failure_message") && settings.get("failure_message") instanceof String)
|
||||
JFXUtilities.runInFX(() -> Controllers.dialog(appendix, (String) settings.get("failure_message"), MessageBox.ERROR_MESSAGE, () -> Controllers.navigate(null)));
|
||||
else if (!settings.containsKey("forbid_failure_message"))
|
||||
JFXUtilities.runInFX(() -> Controllers.dialog(appendix, Main.i18n("wizard.failed"), MessageBox.ERROR_MESSAGE, () -> Controllers.navigate(null)));
|
||||
public void onStop(boolean success, TaskExecutor executor) {
|
||||
if (success) {
|
||||
if (settings.containsKey("success_message") && settings.get("success_message") instanceof String)
|
||||
JFXUtilities.runInFX(() -> Controllers.dialog((String) settings.get("success_message"), null, MessageBox.FINE_MESSAGE, () -> Controllers.navigate(null)));
|
||||
else if (!settings.containsKey("forbid_success_message"))
|
||||
JFXUtilities.runInFX(() -> Controllers.dialog(Main.i18n("message.success"), null, MessageBox.FINE_MESSAGE, () -> Controllers.navigate(null)));
|
||||
} else {
|
||||
if (executor.getLastException() == null)
|
||||
return;
|
||||
String appendix = StringUtils.getStackTrace(executor.getLastException());
|
||||
if (settings.containsKey("failure_message") && settings.get("failure_message") instanceof String)
|
||||
JFXUtilities.runInFX(() -> Controllers.dialog(appendix, (String) settings.get("failure_message"), MessageBox.ERROR_MESSAGE, () -> Controllers.navigate(null)));
|
||||
else if (!settings.containsKey("forbid_failure_message"))
|
||||
JFXUtilities.runInFX(() -> Controllers.dialog(appendix, Main.i18n("wizard.failed"), MessageBox.ERROR_MESSAGE, () -> Controllers.navigate(null)));
|
||||
}
|
||||
}
|
||||
});
|
||||
pane.setExecutor(executor);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -31,10 +31,7 @@ import org.jackhuang.hmcl.download.liteloader.LiteLoaderInstallTask;
|
||||
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
|
||||
import org.jackhuang.hmcl.game.HMCLModpackExportTask;
|
||||
import org.jackhuang.hmcl.game.HMCLModpackInstallTask;
|
||||
import org.jackhuang.hmcl.mod.CurseCompletionTask;
|
||||
import org.jackhuang.hmcl.mod.CurseInstallTask;
|
||||
import org.jackhuang.hmcl.mod.MinecraftInstanceTask;
|
||||
import org.jackhuang.hmcl.mod.MultiMCModpackInstallTask;
|
||||
import org.jackhuang.hmcl.mod.*;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.task.TaskExecutor;
|
||||
import org.jackhuang.hmcl.task.TaskListener;
|
||||
@@ -60,7 +57,7 @@ public final class TaskListPane extends StackPane {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReady(Task task) {
|
||||
public void onRunning(Task task) {
|
||||
if (!task.getSignificance().shouldShow())
|
||||
return;
|
||||
|
||||
@@ -76,6 +73,8 @@ public final class TaskListPane extends StackPane {
|
||||
task.setName(Main.i18n("install.installer.install", Main.i18n("install.installer.optifine")));
|
||||
} else if (task instanceof CurseCompletionTask) {
|
||||
task.setName(Main.i18n("modpack.type.curse.completion"));
|
||||
} else if (task instanceof ModpackInstallTask) {
|
||||
task.setName(Main.i18n("modpack.installing"));
|
||||
} else if (task instanceof CurseInstallTask) {
|
||||
task.setName(Main.i18n("modpack.install", Main.i18n("modpack.type.curse")));
|
||||
} else if (task instanceof MultiMCModpackInstallTask) {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.jackhuang.hmcl.ui.construct;
|
||||
|
||||
import com.jfoenix.validation.base.ValidatorBase;
|
||||
import javafx.beans.NamedArg;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.control.TextInputControl;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
public class URLValidator extends ValidatorBase {
|
||||
|
||||
private final ObservableList<String> protocols = FXCollections.observableArrayList();
|
||||
|
||||
public URLValidator() {
|
||||
super();
|
||||
}
|
||||
|
||||
public URLValidator(@NamedArg("message") String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ObservableList<String> getProtocols() {
|
||||
return protocols;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void eval() {
|
||||
if (srcControl.get() instanceof TextInputControl) {
|
||||
try {
|
||||
URL url = new URL(((TextInputControl) srcControl.get()).getText());
|
||||
if (protocols.isEmpty())
|
||||
hasErrors.set(false);
|
||||
else
|
||||
hasErrors.set(!protocols.contains(url.getProtocol()));
|
||||
} catch (MalformedURLException e) {
|
||||
hasErrors.set(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -21,15 +21,10 @@ import javafx.scene.Node;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.download.DownloadProvider;
|
||||
import org.jackhuang.hmcl.download.GameBuilder;
|
||||
import org.jackhuang.hmcl.game.HMCLModpackInstallTask;
|
||||
import org.jackhuang.hmcl.game.HMCLModpackManifest;
|
||||
import org.jackhuang.hmcl.game.ModpackHelper;
|
||||
import org.jackhuang.hmcl.game.MultiMCInstallVersionSettingTask;
|
||||
import org.jackhuang.hmcl.mod.*;
|
||||
import org.jackhuang.hmcl.setting.EnumGameDirectory;
|
||||
import org.jackhuang.hmcl.mod.Modpack;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
import org.jackhuang.hmcl.setting.Settings;
|
||||
import org.jackhuang.hmcl.setting.VersionSetting;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardProvider;
|
||||
@@ -71,9 +66,9 @@ public final class DownloadWizardProvider implements WizardProvider {
|
||||
if (!settings.containsKey(ModpackPage.MODPACK_FILE))
|
||||
return null;
|
||||
|
||||
File selected = Lang.get(settings, ModpackPage.MODPACK_FILE, File.class, null);
|
||||
Modpack modpack = Lang.get(settings, ModpackPage.MODPACK_CURSEFORGE_MANIFEST, Modpack.class, null);
|
||||
String name = Lang.get(settings, ModpackPage.MODPACK_NAME, String.class, null);
|
||||
File selected = Lang.get(settings, ModpackPage.MODPACK_FILE, File.class).orElse(null);
|
||||
Modpack modpack = Lang.get(settings, ModpackPage.MODPACK_CURSEFORGE_MANIFEST, Modpack.class).orElse(null);
|
||||
String name = Lang.get(settings, ModpackPage.MODPACK_NAME, String.class).orElse(null);
|
||||
if (selected == null || modpack == null || name == null) return null;
|
||||
|
||||
return ModpackHelper.getInstallTask(profile, selected, name, modpack);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -99,9 +99,6 @@ public final class ModpackPage extends StackPane implements WizardPage {
|
||||
txtModpackName.setText(Main.i18n("modpack.task.install.error"));
|
||||
}
|
||||
}
|
||||
|
||||
//FXUtils.limitHeight(borderPane, 100.0);
|
||||
FXUtils.limitWidth(borderPane, 500.0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -27,7 +27,6 @@ import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.jackhuang.hmcl.download.DownloadProvider;
|
||||
import org.jackhuang.hmcl.download.RemoteVersion;
|
||||
import org.jackhuang.hmcl.download.VersionList;
|
||||
import org.jackhuang.hmcl.download.game.GameRemoteVersionTag;
|
||||
import org.jackhuang.hmcl.download.game.GameVersionList;
|
||||
@@ -42,7 +41,6 @@ import org.jackhuang.hmcl.ui.wizard.WizardPage;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class VersionsPage extends StackPane implements WizardPage, Refreshable {
|
||||
private final WizardController controller;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -19,10 +19,15 @@ package org.jackhuang.hmcl.ui.download;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.download.RemoteVersion;
|
||||
import org.jackhuang.hmcl.download.game.GameRemoteVersionTag;
|
||||
import org.jackhuang.hmcl.download.liteloader.LiteLoaderRemoteVersionTag;
|
||||
import org.jackhuang.hmcl.download.optifine.OptiFineRemoteVersion;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
|
||||
/**
|
||||
@@ -34,6 +39,12 @@ public final class VersionsPageItem extends StackPane {
|
||||
private Label lblSelfVersion;
|
||||
@FXML
|
||||
private Label lblGameVersion;
|
||||
@FXML
|
||||
private ImageView imageView;
|
||||
@FXML
|
||||
private HBox leftPane;
|
||||
@FXML
|
||||
private StackPane imageViewContainer;
|
||||
|
||||
public VersionsPageItem(RemoteVersion<?> remoteVersion) {
|
||||
this.remoteVersion = remoteVersion;
|
||||
@@ -45,17 +56,29 @@ public final class VersionsPageItem extends StackPane {
|
||||
switch (((GameRemoteVersionTag) remoteVersion.getTag()).getType()) {
|
||||
case RELEASE:
|
||||
lblGameVersion.setText(Main.i18n("version.game.release"));
|
||||
imageView.setImage(new Image("/assets/img/icon.png", 32, 32, false, true));
|
||||
break;
|
||||
case SNAPSHOT:
|
||||
lblGameVersion.setText(Main.i18n("version.game.snapshot"));
|
||||
imageView.setImage(new Image("/assets/img/command.png", 32, 32, false, true));
|
||||
break;
|
||||
default:
|
||||
lblGameVersion.setText(Main.i18n("version.game.old"));
|
||||
imageView.setImage(new Image("/assets/img/grass.png", 32, 32, false, true));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
} else if (remoteVersion.getTag() instanceof LiteLoaderRemoteVersionTag) {
|
||||
imageView.setImage(new Image("/assets/img/chicken.png", 32, 32, false, true));
|
||||
lblGameVersion.setText(remoteVersion.getGameVersion());
|
||||
} else if (remoteVersion instanceof OptiFineRemoteVersion) {
|
||||
// optifine has no icon.
|
||||
lblGameVersion.setText(remoteVersion.getGameVersion());
|
||||
} else { // forge
|
||||
imageView.setImage(new Image("/assets/img/forge.png", 32, 32, false, true));
|
||||
lblGameVersion.setText(remoteVersion.getGameVersion());
|
||||
}
|
||||
|
||||
leftPane.getChildren().remove(imageViewContainer);
|
||||
}
|
||||
|
||||
public RemoteVersion<?> getRemoteVersion() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -44,7 +44,10 @@ public final class ExportWizardProvider implements WizardProvider {
|
||||
|
||||
@Override
|
||||
public Object finish(Map<String, Object> settings) {
|
||||
return new HMCLModpackExportTask(profile.getRepository(), version, (List<String>) settings.get(ModpackFileSelectionPage.MODPACK_FILE_SELECTION),
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> whitelist = (List<String>) settings.get(ModpackFileSelectionPage.MODPACK_FILE_SELECTION);
|
||||
|
||||
return new HMCLModpackExportTask(profile.getRepository(), version, whitelist,
|
||||
new Modpack(
|
||||
(String) settings.get(ModpackInfoPage.MODPACK_NAME),
|
||||
(String) settings.get(ModpackInfoPage.MODPACK_AUTHOR),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -52,7 +52,7 @@ public final class ModpackFileSelectionPage extends StackPane implements WizardP
|
||||
private final ModAdviser adviser;
|
||||
@FXML
|
||||
private JFXTreeView<String> treeView;
|
||||
private CheckBoxTreeItem<String> rootNode;
|
||||
private final CheckBoxTreeItem<String> rootNode;
|
||||
|
||||
public ModpackFileSelectionPage(WizardController controller, Profile profile, String version, ModAdviser adviser) {
|
||||
this.controller = controller;
|
||||
@@ -62,7 +62,7 @@ public final class ModpackFileSelectionPage extends StackPane implements WizardP
|
||||
FXUtils.loadFXML(this, "/assets/fxml/modpack/selection.fxml");
|
||||
rootNode = getTreeItem(profile.getRepository().getRunDirectory(version), "minecraft");
|
||||
treeView.setRoot(rootNode);
|
||||
treeView.setSelectionModel(NoneMultipleSelectionModel.getInstance());
|
||||
treeView.setSelectionModel(new NoneMultipleSelectionModel<>());
|
||||
}
|
||||
|
||||
private CheckBoxTreeItem<String> getTreeItem(File file, String basePath) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -17,21 +17,14 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.wizard;
|
||||
|
||||
import com.jfoenix.controls.JFXProgressBar;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.task.TaskExecutor;
|
||||
import org.jackhuang.hmcl.task.TaskListener;
|
||||
import org.jackhuang.hmcl.ui.construct.TaskListPane;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public interface AbstractWizardDisplayer extends WizardDisplayer {
|
||||
WizardController getWizardController();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
* Copyright (C) 2018 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
|
||||
@@ -28,7 +28,6 @@ import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.animation.TransitionHandler;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
|
||||
Loaded 100 of 367 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user