Merge branch 'javafx' into switch-to-https

This commit is contained in:
huanghongxun
2018-03-05 20:56:26 +08:00
committed by GitHub
120 changed files with 1676 additions and 1107 deletions

View File

@@ -1,3 +1,6 @@
import org.apache.tools.ant.filters.ReplaceTokens
import proguard.gradle.ProGuardTask
import java.security.MessageDigest
import java.util.jar.JarFile
import java.util.jar.Pack200
@@ -15,11 +18,9 @@ if (buildnumber == null)
def versionroot = System.getenv("VERSION_ROOT")
if (versionroot == null)
versionroot = "3.0"
versionroot = "3.1"
String mavenGroupId = 'HMCL'
String mavenVersion = versionroot + '.' + buildnumber
String bundleName = "Hello Minecraft! Launcher"
version = mavenVersion
dependencies {
@@ -30,7 +31,7 @@ dependencies {
task generateSources(type: Sync) {
from 'src/main/java'
into "$buildDir/generated-src"
filter(org.apache.tools.ant.filters.ReplaceTokens, tokens: [
filter(ReplaceTokens, tokens: [
'HELLO_MINECRAFT_LAUNCHER_VERSION_FOR_GRADLE_REPLACING': mavenVersion
])
}
@@ -122,7 +123,7 @@ task makePackGZ(dependsOn: jar) doLast {
fileEx.append sha1Hex
}
task proguard(type: proguard.gradle.ProGuardTask, dependsOn: jar) {
task proguard(type: ProGuardTask, dependsOn: jar) {
ext {
def re = jar.classifier
injar = jar.archivePath

View File

@@ -0,0 +1,152 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl;
import com.jfoenix.concurrency.JFXUtilities;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
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.*;
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 Launcher extends Application {
@Override
public void start(Stage primaryStage) {
Thread.currentThread().setUncaughtExceptionHandler(CRASH_REPORTER);
try {
// When launcher visibility is set to "hide and reopen" without Platform.implicitExit = false,
// Stage.show() cannot work again because JavaFX Toolkit have already shut down.
Platform.setImplicitExit(false);
Controllers.initialize(primaryStage);
primaryStage.setResizable(false);
primaryStage.setScene(Controllers.getScene());
UPDATE_CHECKER.process(false)
.then(Task.of(Schedulers.javafx(), () -> {
if (UPDATE_CHECKER.isOutOfDate())
Controllers.showUpdate();
}))
.start();
primaryStage.show();
} catch (Throwable e) {
CRASH_REPORTER.uncaughtException(Thread.currentThread(), e);
}
}
public static void main(String[] args) {
Thread.setDefaultUncaughtExceptionHandler(CRASH_REPORTER);
try {
Logging.start();
// NetworkUtils.setUserAgentSupplier(() -> "Hello Minecraft! Launcher");
Constants.UI_THREAD_SCHEDULER = Constants.JAVAFX_UI_THREAD_SCHEDULER;
UPGRADER.parseArguments(VersionNumber.asVersion(VERSION), Arrays.asList(args));
Logging.LOG.info("*** " + TITLE + " ***");
launch(args);
} catch (Throwable e) { // Fucking JavaFX will suppress the exception and will break our crash reporter.
CRASH_REPORTER.uncaughtException(Thread.currentThread(), e);
}
}
public static void stopApplication() {
Logging.LOG.info("Stopping application.\n" + StringUtils.getStackTrace(Thread.currentThread().getStackTrace()));
JFXUtilities.runInFX(() -> {
if (Controllers.getStage() == null)
return;
Controllers.getStage().close();
Schedulers.shutdown();
Controllers.shutdown();
Platform.exit();
Lang.executeDelayed(OperatingSystem::forceGC, TimeUnit.SECONDS, 5, true);
});
}
public static void stopWithoutPlatform() {
Logging.LOG.info("Stopping application without JavaFX Toolkit.\n" + StringUtils.getStackTrace(Thread.currentThread().getStackTrace()));
JFXUtilities.runInFX(() -> {
if (Controllers.getStage() == null)
return;
Controllers.getStage().close();
Schedulers.shutdown();
Controllers.shutdown();
Lang.executeDelayed(OperatingSystem::forceGC, TimeUnit.SECONDS, 5, true);
});
}
public static File getWorkingDirectory(String folder) {
String home = System.getProperty("user.home", ".");
switch (OperatingSystem.CURRENT_OS) {
case LINUX:
return new File(home, "." + folder + "/");
case WINDOWS:
String appdata = System.getenv("APPDATA");
return new File(Lang.nonNull(appdata, home), "." + folder + "/");
case OSX:
return new File(home, "Library/Application Support/" + folder);
default:
return new File(home, folder + "/");
}
}
public static String i18n(String key) {
try {
return RESOURCE_BUNDLE.getString(key);
} catch (Exception e) {
Logging.LOG.log(Level.SEVERE, "Cannot find key " + key + " in resource bundle", e);
return key;
}
}
public static String i18n(String key, Object... formatArgs) {
return String.format(i18n(key), formatArgs);
}
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 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";
public static final String PUBLISH = "http://www.mcbbs.net/thread-142335-1-1.html";
}

View File

@@ -17,128 +17,24 @@
*/
package org.jackhuang.hmcl;
import com.jfoenix.concurrency.JFXUtilities;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
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.*;
import org.jackhuang.hmcl.util.Logging;
import javax.swing.*;
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 {
@Override
public void start(Stage primaryStage) {
Thread.currentThread().setUncaughtExceptionHandler(CRASH_REPORTER);
try {
// When launcher visibility is set to "hide and reopen" without Platform.implicitExit = false,
// Stage.show() cannot work again because JavaFX Toolkit have already shut down.
Platform.setImplicitExit(false);
Controllers.initialize(primaryStage);
primaryStage.setResizable(false);
primaryStage.setScene(Controllers.getScene());
primaryStage.show();
} catch (Throwable e) {
CRASH_REPORTER.uncaughtException(Thread.currentThread(), e);
}
}
public final class Main {
public static void main(String[] args) {
Thread.setDefaultUncaughtExceptionHandler(CRASH_REPORTER);
String currentDirectory = new File("").getAbsolutePath();
if (currentDirectory.contains("!")) {
System.err.println("Exclamation mark(!) is not allowed in the path where HMCL is in. Forcibly exit.");
try {
// NetworkUtils.setUserAgentSupplier(() -> "Hello Minecraft! Launcher");
Constants.UI_THREAD_SCHEDULER = Constants.JAVAFX_UI_THREAD_SCHEDULER;
UPGRADER.parseArguments(VersionNumber.asVersion(VERSION), Arrays.asList(args));
Logging.LOG.info("*** " + TITLE + " ***");
UPDATE_CHECKER.process(false)
.then(Task.of(Schedulers.javafx(), () -> {
if (UPDATE_CHECKER.isOutOfDate())
Controllers.showUpdate();
}))
.start();
launch(args);
} catch (Throwable e) { // Fucking JavaFX will suppress the exception and will break our crash reporter.
CRASH_REPORTER.uncaughtException(Thread.currentThread(), e);
}
// No Chinese translation because both Swing and JavaFX cannot render Chinese character properly when exclamation mark exists in the path.
String message = "Exclamation mark(!) is not allowed in the path where HMCL is in.\nThe path is " + currentDirectory;
JOptionPane.showMessageDialog(null, message, "Error", JOptionPane.ERROR_MESSAGE);
System.exit(1);
} else
Launcher.main(args);
}
public static void stopApplication() {
JFXUtilities.runInFX(() -> {
stopWithoutPlatform();
Platform.exit();
});
}
public static void stopWithoutPlatform() {
JFXUtilities.runInFX(() -> {
if (Controllers.getStage() == null)
return;
Controllers.getStage().close();
Logging.LOG.info("Shutting down executor services.");
Schedulers.shutdown();
Controllers.shutdown();
Lang.executeDelayed(OperatingSystem::forceGC, TimeUnit.SECONDS, 5, true);
});
}
public static String i18n(String key) {
try {
return RESOURCE_BUNDLE.getString(key);
} catch (Exception e) {
Logging.LOG.log(Level.SEVERE, "Cannot find key " + key + " in resource bundle", e);
return key;
}
}
public static String i18n(String key, Object... formatArgs) {
return String.format(i18n(key), formatArgs);
}
public static File getWorkingDirectory(String folder) {
String home = System.getProperty("user.home", ".");
switch (OperatingSystem.CURRENT_OS) {
case LINUX:
return new File(home, "." + folder + "/");
case WINDOWS:
String appdata = System.getenv("APPDATA");
return new File(Lang.nonNull(appdata, home), "." + folder + "/");
case OSX:
return new File(home, "Library/Application Support/" + folder);
default:
return new File(home, folder + "/");
}
}
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 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 = "https://huangyuhui.duapp.com/hmcl.php";
public static final String PUBLISH = "http://www.mcbbs.net/thread-142335-1-1.html";
}

View File

@@ -0,0 +1,61 @@
/*
* 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.event;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.util.ToStringBuilder;
import java.util.Collection;
import java.util.Collections;
/**
* This event gets fired when loading accounts.
* <br>
* This event is fired on the {@link org.jackhuang.hmcl.event.EventBus#EVENT_BUS}
*
* @author huangyuhui
*/
public class AccountLoadingEvent extends Event {
private final Collection<Account> accounts;
/**
* Constructor.
*
* @param source {@link org.jackhuang.hmcl.setting.Settings}
*/
public AccountLoadingEvent(Object source, Collection<Account> accounts) {
super(source);
this.accounts = Collections.unmodifiableCollection(accounts);
}
public Collection<Account> getAccounts() {
return accounts;
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("source", source)
.append("accounts", accounts)
.toString();
}
}

View File

@@ -17,6 +17,7 @@
*/
package org.jackhuang.hmcl.event;
import org.jackhuang.hmcl.util.ToStringBuilder;
import org.jackhuang.hmcl.util.VersionNumber;
/**
@@ -42,4 +43,11 @@ public final class OutOfDateEvent extends Event {
return true;
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("source", getSource())
.append("version", getVersion())
.toString();
}
}

View File

@@ -18,6 +18,7 @@
package org.jackhuang.hmcl.event;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.util.ToStringBuilder;
/**
* This event gets fired when the selected profile changed.
@@ -44,4 +45,11 @@ public final class ProfileChangedEvent extends Event {
return profile;
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("source", source)
.append("profile", profile)
.toString();
}
}

View File

@@ -17,6 +17,12 @@
*/
package org.jackhuang.hmcl.event;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.util.ToStringBuilder;
import java.util.Collection;
import java.util.Collections;
/**
* This event gets fired when loading profiles.
* <br>
@@ -26,14 +32,29 @@ package org.jackhuang.hmcl.event;
*/
public class ProfileLoadingEvent extends Event {
private final Collection<Profile> profiles;
/**
* Constructor.
*
* @param source {@link org.jackhuang.hmcl.setting.Settings}
*/
public ProfileLoadingEvent(Object source) {
public ProfileLoadingEvent(Object source, Collection<Profile> profiles) {
super(source);
this.profiles = Collections.unmodifiableCollection(profiles);
}
public Collection<Profile> getProfiles() {
return profiles;
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("source", source)
.append("profiles", profiles)
.toString();
}
}

View File

@@ -19,7 +19,7 @@ package org.jackhuang.hmcl.game;
import javafx.geometry.Rectangle2D;
import javafx.scene.image.Image;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.auth.yggdrasil.GameProfile;
import org.jackhuang.hmcl.auth.yggdrasil.Texture;
@@ -43,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.HMCL_DIRECTORY, "skins");
public static final File SKIN_DIR = new File(Launcher.HMCL_DIRECTORY, "skins");
public static void loadSkins() {
loadSkins(Proxy.NO_PROXY);

View File

@@ -17,7 +17,7 @@
*/
package org.jackhuang.hmcl.game;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.auth.AuthInfo;
import org.jackhuang.hmcl.launch.DefaultLauncher;
import org.jackhuang.hmcl.launch.ProcessListener;
@@ -45,7 +45,7 @@ public final class HMCLGameLauncher extends DefaultLauncher {
protected void appendJvmArgs(List<String> result) {
super.appendJvmArgs(result);
result.add("-Dminecraft.launcher.version=" + Main.VERSION);
result.add("-Dminecraft.launcher.brand=" + Main.NAME);
result.add("-Dminecraft.launcher.version=" + Launcher.VERSION);
result.add("-Dminecraft.launcher.brand=" + Launcher.NAME);
}
}

View File

@@ -173,10 +173,20 @@ public class HMCLGameRepository extends DefaultGameRepository {
return new File(getVersionRoot(id), "icon.png");
}
public void saveVersionSetting(String id) {
public boolean saveVersionSetting(String id) {
if (!versionSettings.containsKey(id))
return;
Lang.invoke(() -> FileUtils.writeText(getVersionSettingFile(id), GSON.toJson(versionSettings.get(id))));
return false;
File file = getVersionSettingFile(id);
if (!FileUtils.makeDirectory(file.getAbsoluteFile().getParentFile()))
return false;
try {
FileUtils.writeText(file, GSON.toJson(versionSettings.get(id)));
return true;
} catch (IOException e) {
Logging.LOG.log(Level.SEVERE, "Unable to save version setting of " + id, e);
return false;
}
}
public boolean forbidsVersion(String id) {

View File

@@ -42,7 +42,6 @@ public final class HMCLModpackInstallTask extends Task {
private final String name;
private final HMCLGameRepository repository;
private final Modpack modpack;
private final File run;
private final List<Task> dependencies = new LinkedList<>();
private final List<Task> dependents = new LinkedList<>();
@@ -52,8 +51,8 @@ public final class HMCLModpackInstallTask extends Task {
this.zipFile = zipFile;
this.name = name;
this.modpack = modpack;
this.run = repository.getRunDirectory(name);
File run = repository.getRunDirectory(name);
File json = repository.getModpackConfiguration(name);
if (repository.hasVersion(name) && !json.exists())
throw new IllegalArgumentException("Version " + name + " already exists");

View File

@@ -26,7 +26,6 @@ import org.jackhuang.hmcl.util.StringUtils;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/**

View File

@@ -19,13 +19,12 @@ package org.jackhuang.hmcl.game;
import com.jfoenix.concurrency.JFXUtilities;
import javafx.application.Platform;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
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.LauncherVisibility;
@@ -51,38 +50,34 @@ public final class LauncherHelper {
public static final LauncherHelper INSTANCE = new LauncherHelper();
private LauncherHelper(){}
private TaskExecutor executor;
public static final Queue<ManagedProcess> PROCESSES = new ConcurrentLinkedQueue<>();
private final TaskExecutorDialogPane launchingStepsPane = new TaskExecutorDialogPane(() -> {});
public void launch(String selectedVersion, File scriptFile) {
Profile profile = Settings.INSTANCE.getSelectedProfile();
GameRepository repository = profile.getRepository();
Account account = Settings.INSTANCE.getSelectedAccount();
public void launch(Profile profile, Account account, String selectedVersion, File scriptFile) {
if (account == null)
throw new IllegalStateException("No account");
throw new IllegalArgumentException("No account");
GameRepository repository = profile.getRepository();
Version version = repository.getResolvedVersion(selectedVersion);
VersionSetting setting = profile.getVersionSetting(selectedVersion);
Platform.runLater(() -> {
try {
checkGameState(profile, setting, version, () -> Schedulers.newThread().schedule(() -> launch0(selectedVersion, scriptFile)));
checkGameState(profile, setting, version, () -> Schedulers.newThread().schedule(() -> launch0(profile, account, selectedVersion, scriptFile)));
} catch (InterruptedException ignore) {
}
});
}
private void launch0(String selectedVersion, File scriptFile) {
Profile profile = Settings.INSTANCE.getSelectedProfile();
private void launch0(Profile profile, Account account, String selectedVersion, File scriptFile) {
GameRepository repository = profile.getRepository();
DefaultDependencyManager dependencyManager = profile.getDependency();
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))
TaskExecutor executor = Task.of(Schedulers.javafx(), () -> Controllers.dialog(launchingStepsPane))
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.DEPENDENCIES)))
.then(variables -> {
if (setting.isNotCheckGame())
@@ -93,7 +88,7 @@ public final class LauncherHelper {
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.MODS)))
.then(new CurseCompletionTask(dependencyManager, selectedVersion))
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.LOGGING_IN)))
.then(Task.of(Main.i18n("account.methods"), variables -> {
.then(Task.of(Launcher.i18n("account.methods"), variables -> {
try {
try {
variables.set("account", account.logIn());
@@ -105,7 +100,6 @@ public final class LauncherHelper {
}
} catch (AuthenticationException e) {
variables.set("account", DialogController.logIn(account));
JFXUtilities.runInFX(() -> Controllers.dialog(launchingStepsPane));
}
}))
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.LAUNCHING)))
@@ -117,12 +111,12 @@ public final class LauncherHelper {
.then(variables -> {
DefaultLauncher launcher = variables.get("launcher");
if (scriptFile == null) {
return new LaunchTask<>(launcher::launch).setName(Main.i18n("version.launch"));
return new LaunchTask<>(launcher::launch).setName(Launcher.i18n("version.launch"));
} else {
return new LaunchTask<>(() -> {
launcher.makeLaunchScript(scriptFile);
return null;
}).setName(Main.i18n("version.launch_script"));
}).setName(Launcher.i18n("version.launch_script"));
}
})
.then(Task.of(variables -> {
@@ -130,15 +124,17 @@ public final class LauncherHelper {
ManagedProcess process = variables.get(LaunchTask.LAUNCH_ID);
PROCESSES.add(process);
if (setting.getLauncherVisibility() == LauncherVisibility.CLOSE)
Main.stopApplication();
Launcher.stopApplication();
else
launchingStepsPane.setCancel(() -> {
process.stop();
Controllers.closeDialog();
});
} else
Platform.runLater(() ->
Controllers.dialog(Main.i18n("version.launch_script.success", scriptFile.getAbsolutePath())));
Platform.runLater(() -> {
Controllers.closeDialog();
Controllers.dialog(Launcher.i18n("version.launch_script.success", scriptFile.getAbsolutePath()));
});
}))
.executor();
@@ -158,16 +154,14 @@ public final class LauncherHelper {
public void onStop(boolean success, TaskExecutor executor) {
if (!success) {
Platform.runLater(() -> {
Controllers.closeDialog();
if (executor.getLastException() != null)
Controllers.dialog(I18nException.getStackTrace(executor.getLastException()),
scriptFile == null ? Main.i18n("launch.failed") : Main.i18n("version.launch_script.failed"),
scriptFile == null ? Launcher.i18n("launch.failed") : Launcher.i18n("version.launch_script.failed"),
MessageBox.ERROR_MESSAGE, Controllers::closeDialog);
else
Controllers.closeDialog();
});
}
LauncherHelper.this.executor = null;
launchingStepsPane.setExecutor(null);
}
});
@@ -180,35 +174,35 @@ public final class LauncherHelper {
VersionNumber gameVersion = VersionNumber.asVersion(GameVersion.minecraftVersion(profile.getRepository().getVersionJar(version)).orElse("Unknown"));
JavaVersion java = setting.getJavaVersion();
if (java == null) {
Controllers.dialog(Main.i18n("launch.wrong_javadir"), Main.i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
Controllers.dialog(Launcher.i18n("launch.wrong_javadir"), Launcher.i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
setting.setJava(null);
java = JavaVersion.fromCurrentEnvironment();
flag = true;
}
if (java.getParsedVersion() < JavaVersion.JAVA_8) {
Controllers.dialog(Main.i18n("launch.advice.newer_java"), Main.i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
if (!flag && java.getParsedVersion() < JavaVersion.JAVA_8) {
Controllers.dialog(Launcher.i18n("launch.advice.newer_java"), Launcher.i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
flag = true;
}
if (java.getParsedVersion() >= JavaVersion.JAVA_9 && gameVersion.compareTo(VersionNumber.asVersion("1.12.5")) < 0 && version.getMainClass().contains("launchwrapper")) {
Controllers.dialog(Main.i18n("launch.advice.java9"), Main.i18n("message.error"), MessageBox.ERROR_MESSAGE, null);
if (!flag && java.getParsedVersion() >= JavaVersion.JAVA_9 && gameVersion.compareTo(VersionNumber.asVersion("1.12.5")) < 0 && version.getMainClass().contains("launchwrapper")) {
Controllers.dialog(Launcher.i18n("launch.advice.java9"), Launcher.i18n("message.error"), MessageBox.ERROR_MESSAGE, null);
suggest = false;
flag = true;
}
if (java.getPlatform() == org.jackhuang.hmcl.util.Platform.BIT_32 &&
if (!flag && java.getPlatform() == org.jackhuang.hmcl.util.Platform.BIT_32 &&
org.jackhuang.hmcl.util.Platform.IS_64_BIT) {
Controllers.dialog(Main.i18n("launch.advice.different_platform"), Main.i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
Controllers.dialog(Launcher.i18n("launch.advice.different_platform"), Launcher.i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
flag = true;
}
if (java.getPlatform() == org.jackhuang.hmcl.util.Platform.BIT_32 &&
if (!flag && java.getPlatform() == org.jackhuang.hmcl.util.Platform.BIT_32 &&
setting.getMaxMemory() > 1.5 * 1024) {
Controllers.dialog(Main.i18n("launch.advice.too_large_memory_for_32bit"), Main.i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
Controllers.dialog(Launcher.i18n("launch.advice.too_large_memory_for_32bit"), Launcher.i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
flag = true;
}
if (OperatingSystem.TOTAL_MEMORY > 0 && OperatingSystem.TOTAL_MEMORY < setting.getMaxMemory()) {
Controllers.dialog(Main.i18n("launch.advice.not_enough_space", OperatingSystem.TOTAL_MEMORY), Main.i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
if (!flag && OperatingSystem.TOTAL_MEMORY > 0 && OperatingSystem.TOTAL_MEMORY < setting.getMaxMemory()) {
Controllers.dialog(Launcher.i18n("launch.advice.not_enough_space", OperatingSystem.TOTAL_MEMORY), Launcher.i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
flag = true;
}
@@ -225,10 +219,8 @@ public final class LauncherHelper {
}
public void emitStatus(LoadingState state) {
launchingStepsPane.setTitle(state.getLocalizedMessage());
launchingStepsPane.setSubtitle((state.ordinal() + 1) + " / " + LoadingState.values().length);
Controllers.dialog(launchingStepsPane);
}
private void checkExit(LauncherVisibility v) {
@@ -245,8 +237,8 @@ public final class LauncherHelper {
Platform.runLater(() -> {
// Shut down the platform when user closed log window.
Platform.setImplicitExit(true);
// If we use Main.stop(), log window will be halt immediately.
Main.stopWithoutPlatform();
// If we use Launcher.stop(), log window will be halt immediately.
Launcher.stopWithoutPlatform();
});
break;
}
@@ -264,11 +256,11 @@ public final class LauncherHelper {
try {
setResult(supplier.get());
} catch (PermissionException e) {
throw new I18nException(Main.i18n("launch.failed.executable_permission"), e);
throw new I18nException(Launcher.i18n("launch.failed.executable_permission"), e);
} catch (ProcessCreationException e) {
throw new I18nException(Main.i18n("launch.failed.creating_process") + e.getLocalizedMessage(), e);
throw new I18nException(Launcher.i18n("launch.failed.creating_process") + e.getLocalizedMessage(), e);
} catch (NotDecompressingNativesException e) {
throw new I18nException(Main.i18n("launch.failed.decompressing_natives") + e.getLocalizedMessage(), e);
throw new I18nException(Launcher.i18n("launch.failed.decompressing_natives") + e.getLocalizedMessage(), e);
}
}
@@ -360,7 +352,8 @@ public final class LauncherHelper {
});
break;
case CLOSE:
throw new Error("Never come to here");
// Never come to here.
break;
case KEEP:
// No operations here
break;
@@ -385,10 +378,10 @@ public final class LauncherHelper {
switch (exitType) {
case JVM_ERROR:
logWindow.setTitle(Main.i18n("launch.failed.cannot_create_jvm"));
logWindow.setTitle(Launcher.i18n("launch.failed.cannot_create_jvm"));
break;
case APPLICATION_ERROR:
logWindow.setTitle(Main.i18n("launch.failed.exited_abnormally"));
logWindow.setTitle(Launcher.i18n("launch.failed.exited_abnormally"));
break;
}

View File

@@ -17,7 +17,7 @@
*/
package org.jackhuang.hmcl.game;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
public enum LoadingState {
DEPENDENCIES("launch.state.dependencies"),
@@ -33,6 +33,6 @@ public enum LoadingState {
}
public String getLocalizedMessage() {
return Main.i18n(key);
return Launcher.i18n(key);
}
}

View File

@@ -17,8 +17,7 @@
*/
package org.jackhuang.hmcl.setting;
import com.google.gson.JsonParseException;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.auth.AccountFactory;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
@@ -31,6 +30,8 @@ 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.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.*;
import java.io.File;
@@ -74,8 +75,8 @@ public final class Accounts {
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");
File jar = new File(Launcher.HMCL_DIRECTORY, "authlib-injector.jar");
File local = new File(Launcher.HMCL_DIRECTORY, "authlib-injector.txt");
int buildNumber = 0;
try {
buildNumber = Integer.parseInt(FileUtils.readText(local));
@@ -88,17 +89,17 @@ public final class Accounts {
return jar.getAbsolutePath();
}
public static String getAuthlibInjectorServerName(String serverIp) {
public static String getAuthlibInjectorServerName(String serverIp) throws Exception {
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;
}
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();
}
}
public static TaskResult<String> getAuthlibInjectorServerNameAsync(AuthlibInjectorAccount account) {
return Task.ofResult("serverName", () -> Accounts.getAuthlibInjectorServerName(account.getServerBaseURL()));
}
}

View File

@@ -18,7 +18,7 @@
package org.jackhuang.hmcl.setting;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.util.JavaVersion;
import java.util.*;
@@ -35,7 +35,7 @@ final class Config {
private String backgroundImage = null;
@SerializedName("commonpath")
private String commonDirectory = Main.MINECRAFT_DIRECTORY.getAbsolutePath();
private String commonDirectory = Launcher.MINECRAFT_DIRECTORY.getAbsolutePath();
@SerializedName("hasProxy")
private boolean hasProxy = false;

View File

@@ -23,7 +23,6 @@ import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.MojangDownloadProvider;
import org.jackhuang.hmcl.util.Lang;
import java.util.Arrays;
import java.util.List;
public final class DownloadProviders {

View File

@@ -20,7 +20,6 @@ package org.jackhuang.hmcl.setting;
import org.jackhuang.hmcl.ui.construct.UTF8Control;
import org.jackhuang.hmcl.util.Lang;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;

View File

@@ -24,6 +24,7 @@ import org.jackhuang.hmcl.game.HMCLGameRepository;
import org.jackhuang.hmcl.mod.ModManager;
import org.jackhuang.hmcl.util.ImmediateObjectProperty;
import org.jackhuang.hmcl.util.ImmediateStringProperty;
import org.jackhuang.hmcl.util.ToStringBuilder;
import java.io.File;
import java.lang.reflect.Type;
@@ -146,6 +147,14 @@ public final class Profile {
vs.setUsesGlobal(true);
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("gameDir", getGameDir())
.append("name", getName())
.toString();
}
public void addPropertyChangedListener(InvalidationListener listener) {
nameProperty.addListener(listener);
globalProperty.addListener(listener);

View File

@@ -17,7 +17,7 @@
*/
package org.jackhuang.hmcl.setting;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
public final class Profiles {
private Profiles() {
@@ -26,9 +26,9 @@ public final class Profiles {
public static String getProfileDisplayName(Profile profile) {
switch (profile.getName()) {
case Settings.DEFAULT_PROFILE:
return Main.i18n("profile.default");
return Launcher.i18n("profile.default");
case Settings.HOME_PROFILE:
return Main.i18n("profile.home");
return Launcher.i18n("profile.home");
default:
return profile.getName();
}

View File

@@ -20,7 +20,6 @@ package org.jackhuang.hmcl.setting;
import org.jackhuang.hmcl.util.Lang;
import java.net.Proxy;
import java.util.Arrays;
import java.util.List;
/**

View File

@@ -23,11 +23,12 @@ import javafx.beans.property.ObjectProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.text.Font;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.auth.AccountFactory;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.event.AccountLoadingEvent;
import org.jackhuang.hmcl.event.EventBus;
import org.jackhuang.hmcl.event.ProfileChangedEvent;
import org.jackhuang.hmcl.event.ProfileLoadingEvent;
@@ -62,7 +63,7 @@ public class Settings {
private final Config SETTINGS = initSettings();
private Map<String, Account> accounts = new HashMap<>();
private final Map<String, Account> accounts = new HashMap<>();
{
loadProxy();
@@ -368,6 +369,7 @@ public class Settings {
public void addAccount(Account account) {
accounts.put(Accounts.getAccountId(account), account);
onAccountLoading();
}
public Account getAccount(String name, String character) {
@@ -381,12 +383,14 @@ public class Settings {
public void deleteAccount(String name, String character) {
accounts.remove(Accounts.getAccountId(name, character));
onAccountLoading();
selectedAccount.get();
}
public void deleteAccount(Account account) {
accounts.remove(Accounts.getAccountId(account));
onAccountLoading();
selectedAccount.get();
}
@@ -469,7 +473,7 @@ public class Settings {
checkProfileMap();
if (!hasProfile(SETTINGS.getSelectedProfile())) {
SETTINGS.setSelectedProfile(getProfileMap().keySet().stream().findFirst().get());
getProfileMap().keySet().stream().findFirst().ifPresent(SETTINGS::setSelectedProfile);
Schedulers.computation().schedule(this::onProfileChanged);
}
return getProfile(SETTINGS.getSelectedProfile());
@@ -506,6 +510,7 @@ public class Settings {
throw new IllegalArgumentException("Profile's name is empty");
getProfileMap().put(ver.getName(), ver);
Schedulers.computation().schedule(this::onProfileLoading);
ver.nameProperty().setChangedListener(this::profileNameChanged);
@@ -525,12 +530,12 @@ public class Settings {
private void checkProfileMap() {
if (getProfileMap().isEmpty()) {
getProfileMap().put(DEFAULT_PROFILE, new Profile(DEFAULT_PROFILE));
getProfileMap().put(HOME_PROFILE, new Profile(HOME_PROFILE, Main.MINECRAFT_DIRECTORY));
getProfileMap().put(HOME_PROFILE, new Profile(HOME_PROFILE, Launcher.MINECRAFT_DIRECTORY));
}
}
private void onProfileChanged() {
EventBus.EVENT_BUS.fireEvent(new ProfileChangedEvent(SETTINGS, getSelectedProfile()));
EventBus.EVENT_BUS.fireEvent(new ProfileChangedEvent(this, getSelectedProfile()));
getSelectedProfile().getRepository().refreshVersionsAsync().start();
}
@@ -543,7 +548,11 @@ public class Settings {
* Invoked by loading GUI phase.
*/
public void onProfileLoading() {
EventBus.EVENT_BUS.fireEvent(new ProfileLoadingEvent(SETTINGS));
EventBus.EVENT_BUS.fireEvent(new ProfileLoadingEvent(this, getProfiles()));
onProfileChanged();
}
public void onAccountLoading() {
EventBus.EVENT_BUS.fireEvent(new AccountLoadingEvent(this, getAccounts()));
}
}

View File

@@ -19,7 +19,7 @@ package org.jackhuang.hmcl.setting;
import com.google.gson.*;
import javafx.beans.InvalidationListener;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.game.LaunchOptions;
import org.jackhuang.hmcl.util.*;
@@ -482,8 +482,8 @@ public final class VersionSetting {
return new LaunchOptions.Builder()
.setGameDir(gameDir)
.setJava(javaVersion)
.setVersionName(Main.TITLE)
.setProfileName(Main.TITLE)
.setVersionName(Launcher.TITLE)
.setProfileName(Launcher.TITLE)
.setMinecraftArgs(getMinecraftArgs())
.setJavaArgs(getJavaArgs())
.setMaxMemory(getMaxMemory())

View File

@@ -1,133 +0,0 @@
/*
* 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.controls.JFXProgressBar;
import com.jfoenix.controls.JFXRadioButton;
import javafx.beans.binding.Bindings;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.ToggleGroup;
import javafx.scene.effect.BlurType;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
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.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 {
private final Account account;
@FXML
private Pane icon;
@FXML private VBox content;
@FXML private StackPane header;
@FXML private StackPane body;
@FXML private JFXButton btnDelete;
@FXML private JFXButton btnRefresh;
@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;
@FXML private ImageView portraitView;
@FXML private HBox buttonPane;
public AccountItem(int i, Account account, ToggleGroup toggleGroup) {
this.account = account;
FXUtils.loadFXML(this, "/assets/fxml/account-item.fxml");
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(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 - 16, header.boundsInParentProperty(), icon.heightProperty()));
chkSelected.getProperties().put("account", account);
setSelected(Settings.INSTANCE.getSelectedAccount() == 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);
AccountHelper.refreshSkinAsync((YggdrasilAccount) account)
.subscribe(Schedulers.javafx(), this::loadSkin);
});
AccountHelper.loadSkinAsync((YggdrasilAccount) account)
.subscribe(Schedulers.javafx(), this::loadSkin);
} else
loadSkin();
if (account instanceof OfflineAccount) { // Offline Account cannot be refreshed,
buttonPane.getChildren().remove(btnRefresh);
}
}
private void loadSkin() {
pgsSkin.setVisible(false);
portraitView.setViewport(AccountHelper.getViewport(4));
if (account instanceof YggdrasilAccount)
portraitView.setImage(AccountHelper.getSkin((YggdrasilAccount) account, 4));
else
portraitView.setImage(AccountHelper.getDefaultSkin(account, 4));
FXUtils.limitSize(portraitView, 32, 32);
}
public Account getAccount() {
return account;
}
public void setSelected(boolean selected) {
lblCurrentAccount.setVisible(selected);
chkSelected.setSelected(selected);
}
public void setOnDeleteButtonMouseClicked(EventHandler<? super MouseEvent> eventHandler) {
btnDelete.setOnMouseClicked(eventHandler);
}
}

View File

@@ -73,7 +73,7 @@ public class AccountLoginPane extends StackPane {
} else if (account instanceof NoSelectedCharacterException) {
dialog.close();
} else if (account instanceof Exception) {
lblCreationWarning.setText(AccountsPage.accountException((Exception) account));
lblCreationWarning.setText(AddAccountPane.accountException((Exception) account));
}
progressBar.setVisible(false);

View File

@@ -0,0 +1,152 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.ui;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXProgressBar;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
import org.jackhuang.hmcl.auth.offline.OfflineAccount;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
import org.jackhuang.hmcl.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.ui.construct.ComponentList;
import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
import java.util.Optional;
public class AccountPage extends StackPane implements DecoratorPage {
private final StringProperty title;
private final ObjectProperty<Runnable> onDelete = new SimpleObjectProperty<>(this, "onDelete");
private final VersionListItem item;
private final Account account;
@FXML
private Label lblType;
@FXML
private Label lblServer;
@FXML
private Label lblCharacter;
@FXML
private Label lblEmail;
@FXML
private BorderPane paneServer;
@FXML
private BorderPane paneEmail;
@FXML
private ComponentList componentList;
@FXML
private JFXButton btnDelete;
@FXML
private JFXButton btnRefresh;
@FXML
private JFXProgressBar progressBar;
public AccountPage(Account account, VersionListItem item) {
this.account = account;
this.item = item;
title = new SimpleStringProperty(this, "title", Launcher.i18n("account") + " - " + account.getCharacter());
FXUtils.loadFXML(this, "/assets/fxml/account.fxml");
FXUtils.setLimitWidth(this, 300);
if (account instanceof AuthlibInjectorAccount) {
Accounts.getAuthlibInjectorServerNameAsync((AuthlibInjectorAccount) account)
.subscribe(Schedulers.javafx(), variables -> lblServer.setText(variables.get("serverName")));
FXUtils.setLimitHeight(this, 182);
} else {
componentList.removeChildren(paneServer);
if (account instanceof OfflineAccount) {
componentList.removeChildren(paneEmail);
FXUtils.setLimitHeight(this, 110);
} else
FXUtils.setLimitHeight(this, 145);
}
btnDelete.setGraphic(SVG.delete(Theme.blackFillBinding(), 15, 15));
btnRefresh.setGraphic(SVG.refresh(Theme.blackFillBinding(), 15, 15));
lblCharacter.setText(account.getCharacter());
lblType.setText(AddAccountPane.accountType(account));
lblEmail.setText(account.getUsername());
btnRefresh.setVisible(account instanceof YggdrasilAccount);
}
@FXML
private void onDelete() {
Settings.INSTANCE.deleteAccount(account);
Optional.ofNullable(onDelete.get()).ifPresent(Runnable::run);
}
@FXML
private void onRefresh() {
if (account instanceof YggdrasilAccount) {
progressBar.setVisible(true);
AccountHelper.refreshSkinAsync((YggdrasilAccount) account)
.finalized(Schedulers.javafx(), (variables, isDependentsSucceeded) -> {
progressBar.setVisible(false);
if (isDependentsSucceeded) {
Image image = AccountHelper.getSkin((YggdrasilAccount) account, 4);
item.setImage(image, AccountHelper.getViewport(4));
}
}).start();
}
}
public String getTitle() {
return title.get();
}
@Override
public StringProperty titleProperty() {
return title;
}
public void setTitle(String title) {
this.title.set(title);
}
public Runnable getOnDelete() {
return onDelete.get();
}
public ObjectProperty<Runnable> onDeleteProperty() {
return onDelete;
}
public void setOnDelete(Runnable onDelete) {
this.onDelete.set(onDelete);
}
}

View File

@@ -1,6 +1,6 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2018 huangyuhui <huanghongxun2008@126.com>
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,22 +19,17 @@ package org.jackhuang.hmcl.ui;
import com.jfoenix.concurrency.JFXUtilities;
import com.jfoenix.controls.*;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
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;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.auth.*;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
import org.jackhuang.hmcl.auth.offline.OfflineAccount;
@@ -48,22 +43,18 @@ import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.construct.AdvancedListBox;
import org.jackhuang.hmcl.ui.construct.IconedItem;
import org.jackhuang.hmcl.ui.construct.Validator;
import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
import org.jackhuang.hmcl.util.Logging;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public final class AccountsPage extends StackPane implements DecoratorPage {
private final StringProperty title = new SimpleStringProperty(this, "title", Main.i18n("account"));
public class AddAccountPane extends StackPane {
@FXML
private ScrollPane scrollPane;
@FXML private JFXMasonryPane masonryPane;
@FXML private JFXDialog dialog;
@FXML private JFXTextField txtUsername;
@FXML private JFXPasswordField txtPassword;
@FXML private Label lblCreationWarning;
@@ -73,16 +64,17 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
@FXML private JFXProgressBar progressBar;
@FXML private Label lblAddInjectorServer;
@FXML private Hyperlink linkAddInjectorServer;
@FXML private JFXDialogLayout layout;
private final Runnable finalization;
{
FXUtils.loadFXML(this, "/assets/fxml/account.fxml");
public AddAccountPane(Runnable finalization) {
this.finalization = finalization;
getChildren().remove(dialog);
dialog.setDialogContainer(this);
FXUtils.loadFXML(this, "/assets/fxml/account-add.fxml");
FXUtils.smoothScrolling(scrollPane);
loadServers();
cboType.getItems().setAll(Main.i18n("account.methods.offline"), Main.i18n("account.methods.yggdrasil"), Main.i18n("account.methods.authlib_injector"));
cboType.getItems().setAll(Launcher.i18n("account.methods.offline"), Launcher.i18n("account.methods.yggdrasil"), Launcher.i18n("account.methods.authlib_injector"));
cboType.getSelectionModel().selectedIndexProperty().addListener((a, b, newValue) -> {
txtPassword.setVisible(newValue.intValue() != 0);
lblPassword.setVisible(newValue.intValue() != 0);
@@ -98,42 +90,20 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
txtPassword.setOnAction(e -> onCreationAccept());
txtUsername.setOnAction(e -> onCreationAccept());
txtUsername.getValidators().add(new Validator(Main.i18n("input.email"), str -> !txtPassword.isVisible() || str.contains("@")));
txtUsername.getValidators().add(new Validator(Launcher.i18n("input.email"), str -> !txtPassword.isVisible() || str.contains("@")));
FXUtils.onChangeAndOperate(Settings.INSTANCE.selectedAccountProperty(), account -> {
for (Node node : masonryPane.getChildren())
if (node instanceof AccountItem)
((AccountItem) node).setSelected(account == ((AccountItem) node).getAccount());
});
loadAccounts();
loadServers();
if (Settings.INSTANCE.getAccounts().isEmpty())
addNewAccount();
}
public void loadAccounts() {
List<Node> children = new LinkedList<>();
int i = 0;
ToggleGroup group = new ToggleGroup();
for (Account account : Settings.INSTANCE.getAccounts()) {
children.add(buildNode(++i, account, group));
}
group.selectedToggleProperty().addListener((a, b, newValue) -> {
if (newValue != null)
Settings.INSTANCE.setSelectedAccount((Account) newValue.getProperties().get("account"));
});
FXUtils.resetChildren(masonryPane, children);
Platform.runLater(() -> {
masonryPane.requestLayout();
scrollPane.requestLayout();
});
}
public void loadServers() {
private void loadServers() {
Task.ofResult("list", () -> Settings.INSTANCE.getAuthlibInjectorServerURLs().parallelStream()
.map(serverURL -> new TwoLineListItem(Accounts.getAuthlibInjectorServerName(serverURL), serverURL))
.flatMap(serverURL -> {
try {
return Stream.of(new TwoLineListItem(Accounts.getAuthlibInjectorServerName(serverURL), serverURL));
} catch (Exception e) {
Logging.LOG.log(Level.WARNING, "Authlib-injector server root " + serverURL + " cannot be recognized.", e);
return Stream.empty();
}
})
.collect(Collectors.toList()))
.subscribe(Task.of(Schedulers.javafx(), variables -> {
cboServers.getItems().setAll(variables.<Collection<TwoLineListItem>>get("list"));
@@ -142,28 +112,6 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
}));
}
private Node buildNode(int i, Account account, ToggleGroup group) {
AccountItem item = new AccountItem(i, account, group);
item.setOnDeleteButtonMouseClicked(e -> {
Settings.INSTANCE.deleteAccount(account);
Platform.runLater(this::loadAccounts);
});
return item;
}
@FXML
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();
@@ -173,23 +121,22 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
progressBar.setVisible(true);
lblCreationWarning.setText("");
Task.ofResult("create_account", () -> {
AccountFactory<?> factory;
switch (type) {
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();
}
AccountFactory<?> factory;
switch (type) {
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();
}
return factory.create(new Selector(), username, password, apiRoot, Settings.INSTANCE.getProxy());
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);
finalization.run();
}, exception -> {
if (exception instanceof NoSelectedCharacterException) {
dialog.close();
finalization.run();
} else {
lblCreationWarning.setText(accountException(exception));
}
@@ -199,46 +146,24 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
@FXML
private void onCreationCancel() {
dialog.close();
finalization.run();
}
public String getTitle() {
return title.get();
@FXML
private void onAddInjecterServer() {
finalization.run();
Controllers.navigate(Controllers.getServersPage());
}
@Override
public StringProperty titleProperty() {
return title;
private void showSelector(Node node) {
getChildren().setAll(node);
}
public void setTitle(String title) {
this.title.set(title);
private void closeSelector() {
getChildren().setAll(layout);
}
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);
}
private static class Selector extends BorderPane implements CharacterSelector {
private class Selector extends BorderPane implements CharacterSelector {
private final AdvancedListBox listBox = new AdvancedListBox();
private final JFXButton cancel = new JFXButton();
@@ -248,11 +173,11 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
{
setStyle("-fx-padding: 8px;");
cancel.setText(Main.i18n("button.cancel"));
cancel.setText(Launcher.i18n("button.cancel"));
StackPane.setAlignment(cancel, Pos.BOTTOM_RIGHT);
cancel.setOnMouseClicked(e -> latch.countDown());
listBox.startCategory(Main.i18n("account.choose"));
listBox.startCategory(Launcher.i18n("account.choose"));
setCenter(listBox);
@@ -293,20 +218,43 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
listBox.add(accountItem);
}
JFXUtilities.runInFX(() -> Controllers.dialog(this));
JFXUtilities.runInFX(() -> showSelector(this));
try {
latch.await();
JFXUtilities.runInFX(Controllers::closeDialog);
if (selectedProfile == null)
throw new NoSelectedCharacterException(account);
JFXUtilities.runInFX(AddAccountPane.this::closeSelector);
return selectedProfile;
} catch (InterruptedException ignore) {
throw new NoSelectedCharacterException(account);
}
}
}
public static String accountException(Exception exception) {
if (exception instanceof InvalidCredentialsException) {
return Launcher.i18n("account.failed.invalid_credentials");
} else if (exception instanceof NoCharacterException) {
return Launcher.i18n("account.failed.no_charactor");
} else if (exception instanceof ServerDisconnectException) {
return Launcher.i18n("account.failed.connect_authentication_server");
} else if (exception instanceof InvalidTokenException) {
return Launcher.i18n("account.failed.invalid_token");
} else if (exception instanceof InvalidPasswordException) {
return Launcher.i18n("account.failed.invalid_password");
} else {
return exception.getClass() + ": " + exception.getLocalizedMessage();
}
}
public static String accountType(Account account) {
if (account instanceof OfflineAccount) return Launcher.i18n("account.methods.offline");
else if (account instanceof AuthlibInjectorAccount) return Launcher.i18n("account.methods.authlib_injector");
else if (account instanceof YggdrasilAccount) return Launcher.i18n("account.methods.yggdrasil");
else throw new Error(Launcher.i18n("account.methods.no_method") + ": " + account);
}
}

View File

@@ -9,7 +9,7 @@ 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.Launcher;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServerInfo;
import org.jackhuang.hmcl.setting.Accounts;
import org.jackhuang.hmcl.setting.Settings;
@@ -18,14 +18,16 @@ 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.Logging;
import org.jackhuang.hmcl.util.NetworkUtils;
import java.util.Collection;
import java.util.Objects;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class AuthlibInjectorServersPage extends StackPane implements DecoratorPage {
private final StringProperty title = new SimpleStringProperty(this, "title", Main.i18n("account.injector.server"));
private final StringProperty title = new SimpleStringProperty(this, "title", Launcher.i18n("account.injector.server"));
@FXML private ScrollPane scrollPane;
@FXML private StackPane addServerContainer;
@@ -69,7 +71,14 @@ public class AuthlibInjectorServersPage extends StackPane implements DecoratorPa
spinner.setVisible(true);
Task.ofResult("list", () -> Settings.INSTANCE.getAuthlibInjectorServerURLs().parallelStream()
.map(serverURL -> new AuthlibInjectorServerItem(new AuthlibInjectorServerInfo(serverURL, Accounts.getAuthlibInjectorServerName(serverURL)), this::removeServer))
.flatMap(serverURL -> {
try {
return Stream.of(new AuthlibInjectorServerItem(new AuthlibInjectorServerInfo(serverURL, Accounts.getAuthlibInjectorServerName(serverURL)), this::removeServer));
} catch (Exception e) {
Logging.LOG.log(Level.WARNING, "Authlib-injector server root " + serverURL + " cannot be recognized.", e);
return Stream.empty();
}
})
.collect(Collectors.toList()))
.subscribe(Task.of(Schedulers.javafx(), variables -> {
listPane.getChildren().setAll(variables.<Collection<? extends Node>>get("list"));
@@ -105,7 +114,7 @@ public class AuthlibInjectorServersPage extends StackPane implements DecoratorPa
progressBar.setVisible(true);
addServerPane.setDisable(true);
Task.ofResult("serverName", () -> Objects.requireNonNull(Accounts.getAuthlibInjectorServerName(serverIp)))
Task.ofResult("serverName", () -> Accounts.getAuthlibInjectorServerName(serverIp))
.finalized(Schedulers.javafx(), (variables, isDependentsSucceeded) -> {
progressBar.setVisible(false);
addServerPane.setDisable(false);

View File

@@ -17,13 +17,14 @@
*/
package org.jackhuang.hmcl.ui;
import com.jfoenix.concurrency.JFXUtilities;
import com.jfoenix.controls.JFXDialog;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.layout.Region;
import javafx.stage.Stage;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskExecutor;
@@ -32,6 +33,7 @@ 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.JavaVersion;
import org.jackhuang.hmcl.util.Lang;
import java.util.function.Consumer;
@@ -93,7 +95,9 @@ public final class Controllers {
public static void initialize(Stage stage) {
Controllers.stage = stage;
decorator = new Decorator(stage, getMainPage(), Main.TITLE, false, true);
stage.setOnCloseRequest(e -> Launcher.stopApplication());
decorator = new Decorator(stage, getMainPage(), Launcher.TITLE, false, true);
decorator.showPage(null);
leftPaneController = new LeftPaneController(decorator.getLeftPane());
@@ -110,7 +114,7 @@ public final class Controllers {
stage.setMaxHeight(521);
stage.getIcons().add(new Image("/assets/img/icon.png"));
stage.setTitle(Main.TITLE);
stage.setTitle(Launcher.TITLE);
}
public static Region getDialogContent() {
@@ -119,7 +123,7 @@ public final class Controllers {
public static JFXDialog dialog(Region content) {
// TODO: temp fix
decorator.showDialog(new Region());
decorator.showDialog(Lang.apply(new Region(), region -> region.getProperties().put("controllers", true)));
return decorator.showDialog(content);
}
@@ -147,12 +151,15 @@ public final class Controllers {
dialog(new InputDialogPane(text, decorator.getDialog(), onResult));
}
public static void taskDialog(TaskExecutor executor, String title, String subtitle) {
taskDialog(executor, title, subtitle, null);
}
public static void taskDialog(TaskExecutor executor, String title, String subtitle, Runnable onCancel) {
TaskExecutorDialogPane pane = new TaskExecutorDialogPane(onCancel);
pane.setTitle(title);
pane.setSubtitle(subtitle);
pane.setExecutor(executor);
executor.start();
dialog(pane);
}
@@ -168,7 +175,7 @@ public final class Controllers {
}
public static void showUpdate() {
getDecorator().showUpdate();
getLeftPaneController().showUpdate();
}
public static void shutdown() {

View File

@@ -27,7 +27,7 @@ import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
/**
* @author huangyuhui
@@ -36,10 +36,10 @@ public class CrashWindow extends Stage {
public CrashWindow(String text) {
Label lblCrash = new Label();
if (Main.UPDATE_CHECKER.isOutOfDate())
lblCrash.setText(Main.i18n("launcher.crash_out_dated"));
if (Launcher.UPDATE_CHECKER.isOutOfDate())
lblCrash.setText(Launcher.i18n("launcher.crash_out_dated"));
else
lblCrash.setText(Main.i18n("launcher.crash"));
lblCrash.setText(Launcher.i18n("launcher.crash"));
lblCrash.setWrapText(true);
TextArea textArea = new TextArea();
@@ -47,8 +47,8 @@ public class CrashWindow extends Stage {
textArea.setEditable(false);
Button btnContact = new Button();
btnContact.setText(Main.i18n("launcher.contact"));
btnContact.setOnMouseClicked(event -> FXUtils.openLink(Main.CONTACT));
btnContact.setText(Launcher.i18n("launcher.contact"));
btnContact.setOnMouseClicked(event -> FXUtils.openLink(Launcher.CONTACT));
HBox box = new HBox();
box.setStyle("-fx-padding: 8px;");
box.getChildren().add(btnContact);
@@ -65,7 +65,7 @@ public class CrashWindow extends Stage {
Scene scene = new Scene(pane, 800, 480);
setScene(scene);
getIcons().add(new Image("/assets/img/icon.png"));
setTitle(Main.i18n("message.error"));
setTitle(Launcher.i18n("message.error"));
setOnCloseRequest(e -> System.exit(1));
}

View File

@@ -17,6 +17,7 @@
*/
package org.jackhuang.hmcl.ui;
import com.jfoenix.concurrency.JFXUtilities;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXDialog;
import com.jfoenix.controls.JFXDrawer;
@@ -45,7 +46,7 @@ import javafx.scene.shape.Rectangle;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.setting.EnumBackgroundImage;
import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.setting.Theme;
@@ -53,6 +54,7 @@ import org.jackhuang.hmcl.ui.animation.AnimationProducer;
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
import org.jackhuang.hmcl.ui.animation.TransitionHandler;
import org.jackhuang.hmcl.ui.construct.AdvancedListBox;
import org.jackhuang.hmcl.ui.construct.StackContainerPane;
import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogWizardDisplayer;
import org.jackhuang.hmcl.ui.wizard.*;
import org.jackhuang.hmcl.util.FileUtils;
@@ -74,7 +76,7 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
private static final SVGGlyph close = Lang.apply(new SVGGlyph(0, "CLOSE", "M810 274l-238 238 238 238-60 60-238-238-238 238-60-60 238-238-238-238 60-60 238 238 238-238z", Color.WHITE),
glyph -> { glyph.setPrefSize(12, 12); glyph.setSize(12, 12); });
private final ObjectProperty<Runnable> onCloseButtonAction = new SimpleObjectProperty<>(Main::stopApplication);
private final ObjectProperty<Runnable> onCloseButtonAction;
private final BooleanProperty customMaximize = new SimpleBooleanProperty(false);
private final Stage primaryStage;
@@ -82,11 +84,13 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
private final boolean max, min;
private final WizardController wizardController = new WizardController(this);
private final Queue<Object> cancelQueue = new ConcurrentLinkedQueue<>();
private final JFXDialog dialog;
private double xOffset, yOffset, newX, newY, initX, initY;
private boolean allowMove, isDragging, dialogShown, maximized;
private boolean allowMove, isDragging, maximized;
private BoundingBox originalBox, maximizedBox;
private final TransitionHandler animationHandler;
private final StackContainerPane dialogPane = new StackContainerPane();
@FXML
private StackPane contentPlaceHolder;
@@ -119,16 +123,12 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
@FXML
private JFXHamburger titleBurger;
@FXML
private JFXDialog dialog;
@FXML
private JFXButton btnMin;
@FXML
private JFXButton btnMax;
@FXML
private JFXButton btnClose;
@FXML
private HBox updatePane;
@FXML
private HBox navLeft;
public Decorator(Stage primaryStage, Node mainPage, String title) {
@@ -143,8 +143,7 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
FXUtils.loadFXML(this, "/assets/fxml/decorator.fxml");
updatePane.setCursor(Cursor.HAND);
updatePane.setOnMouseClicked(event -> Main.UPDATE_CHECKER.checkOutdate());
onCloseButtonAction = new SimpleObjectProperty<>(this, "onCloseButtonAction", Launcher::stopApplication);
primaryStage.initStyle(StageStyle.UNDECORATED);
btnClose.setGraphic(close);
@@ -168,9 +167,21 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
btnMax.fire();
});
dialog = new JFXDialog() {
@Override
public void close() {
dialogPane.pop();
if (dialogPane.getChildren().isEmpty())
Platform.runLater(() -> {
if (dialogPane.getChildren().isEmpty())
super.close();
});
}
};
dialog.setOverlayClose(false);
drawerWrapper.getChildren().add(0, dialog);
dialog.setDialogContainer(drawerWrapper);
dialog.setOnDialogClosed(e -> dialogShown = false);
dialog.setOnDialogOpened(e -> dialogShown = true);
dialog.setContent(dialogPane);
if (!min) buttonsContainer.getChildren().remove(btnMin);
if (!max) buttonsContainer.getChildren().remove(btnMax);
@@ -474,10 +485,6 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
}
}
public void showUpdate() {
updatePane.setVisible(true);
}
private void showCloseNavButton() {
navLeft.getChildren().add(closeNavButton);
}
@@ -538,10 +545,10 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
}
}
public JFXDialog showDialog(Region content) {
dialog.setContent(content);
if (!dialogShown)
public JFXDialog showDialog(Node node) {
if (dialogPane.isEmpty())
dialog.show();
dialogPane.push(node);
return dialog;
}

View File

@@ -37,11 +37,7 @@ public final class DialogController {
AccountLoginPane pane = new AccountLoginPane(account, it -> {
res.set(it);
latch.countDown();
Controllers.closeDialog();
}, () -> {
latch.countDown();
Controllers.closeDialog();
});
}, latch::countDown);
pane.setDialog(Controllers.dialog(pane));
});
latch.await();

View File

@@ -23,6 +23,7 @@ import javafx.animation.Animation;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.beans.property.Property;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
@@ -41,14 +42,17 @@ 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.Launcher;
import org.jackhuang.hmcl.util.*;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.logging.Level;
import static org.jackhuang.hmcl.util.ReflectionHelper.call;
@@ -184,7 +188,7 @@ public final class FXUtils {
}
public static void loadFXML(Node node, String absolutePath) {
FXMLLoader loader = new FXMLLoader(node.getClass().getResource(absolutePath), Main.RESOURCE_BUNDLE);
FXMLLoader loader = new FXMLLoader(node.getClass().getResource(absolutePath), Launcher.RESOURCE_BUNDLE);
loader.setRoot(node);
loader.setController(node);
Lang.invoke((ExceptionalSupplier<Object, IOException>) loader::load);
@@ -368,6 +372,31 @@ public final class FXUtils {
});
}
public static <T> T runInUIThread(Supplier<T> supplier) {
if (javafx.application.Platform.isFxApplicationThread()) {
return supplier.get();
} else {
CountDownLatch doneLatch = new CountDownLatch(1);
AtomicReference<T> reference = new AtomicReference<>();
Platform.runLater(() -> {
try {
reference.set(supplier.get());
} finally {
doneLatch.countDown();
}
});
try {
doneLatch.await();
} catch (InterruptedException var3) {
Thread.currentThread().interrupt();
}
return reference.get();
}
}
public static final Image DEFAULT_ICON = new Image("/assets/img/icon.png");
public static final Interpolator SINE = new Interpolator() {

View File

@@ -20,6 +20,7 @@ package org.jackhuang.hmcl.ui;
import javafx.fxml.FXML;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.download.MaintainTask;
import org.jackhuang.hmcl.download.game.VersionJsonSaveTask;
import org.jackhuang.hmcl.game.GameVersion;
@@ -89,7 +90,7 @@ public class InstallerController {
Optional<String> gameVersion = GameVersion.minecraftVersion(profile.getRepository().getVersionJar(version));
if (!gameVersion.isPresent())
Controllers.dialog("version.cannot_read");
Controllers.dialog(Launcher.i18n("version.cannot_read"));
else
Controllers.getDecorator().startWizard(new InstallerWizardProvider(profile, gameVersion.get(), version, forge, liteLoader, optiFine));
}

View File

@@ -21,7 +21,7 @@ import com.jfoenix.effects.JFXDepthManager;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import java.util.function.Consumer;
@@ -44,7 +44,7 @@ public class InstallerItem extends BorderPane {
setStyle("-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;");
JFXDepthManager.setDepth(this, 1);
lblInstallerArtifact.setText(artifact);
lblInstallerVersion.setText(Main.i18n("archive.version") + ": " + version);
lblInstallerVersion.setText(Launcher.i18n("archive.version") + ": " + version);
}
@FXML

View File

@@ -19,27 +19,26 @@ package org.jackhuang.hmcl.ui;
import com.jfoenix.concurrency.JFXUtilities;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXPopup;
import javafx.application.Platform;
import javafx.scene.Node;
import javafx.scene.control.Tooltip;
import javafx.scene.image.Image;
import javafx.scene.layout.BorderPane;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import org.jackhuang.hmcl.Main;
import javafx.scene.paint.Color;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
import org.jackhuang.hmcl.auth.offline.OfflineAccount;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
import org.jackhuang.hmcl.event.EventBus;
import org.jackhuang.hmcl.event.ProfileChangedEvent;
import org.jackhuang.hmcl.event.ProfileLoadingEvent;
import org.jackhuang.hmcl.event.RefreshedVersionsEvent;
import org.jackhuang.hmcl.event.*;
import org.jackhuang.hmcl.game.AccountHelper;
import org.jackhuang.hmcl.game.HMCLGameRepository;
import org.jackhuang.hmcl.game.ModpackHelper;
import org.jackhuang.hmcl.mod.Modpack;
import org.jackhuang.hmcl.mod.UnsupportedModpackException;
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.setting.*;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.construct.AdvancedListBox;
@@ -55,50 +54,56 @@ import java.util.Objects;
public final class LeftPaneController {
private final AdvancedListBox leftPane;
private final VBox profilePane = new VBox();
private final VersionListItem accountItem = new VersionListItem("", "");
private final VBox accountPane = new VBox();
private final IconedItem launcherSettingsItem;
private final VersionListItem missingAccountItem = new VersionListItem(Launcher.i18n("account.missing"), Launcher.i18n("message.unknown"));
public LeftPaneController(AdvancedListBox leftPane) {
this.leftPane = leftPane;
leftPane.startCategory(Main.i18n("account").toUpperCase())
.add(Lang.apply(new RipplerContainer(accountItem), rippler -> {
rippler.setOnMouseClicked(e -> Controllers.navigate(new AccountsPage()));
accountItem.setOnSettingsButtonClicked(() -> Controllers.navigate(new AccountsPage()));
}))
.startCategory(Main.i18n("launcher").toUpperCase())
.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()))));
JFXButton addProfileButton = new JFXButton();
addProfileButton.setGraphic(SVG.plus(Theme.blackFillBinding(), 10, 10));
addProfileButton.getStyleClass().add("toggle-icon-tiny");
addProfileButton.setOnMouseClicked(e ->
this.launcherSettingsItem = Lang.apply(new IconedItem(SVG.gear(Theme.blackFillBinding(), 20, 20), Launcher.i18n("settings.launcher")), iconedItem -> {
iconedItem.prefWidthProperty().bind(leftPane.widthProperty());
iconedItem.setOnMouseClicked(e -> Controllers.navigate(Controllers.getSettingsPage()));
});
leftPane
.add(new ClassTitle(Launcher.i18n("account").toUpperCase(), Lang.apply(new JFXButton(), button -> {
button.setGraphic(SVG.plus(Theme.blackFillBinding(), 10, 10));
button.getStyleClass().add("toggle-icon-tiny");
button.setOnMouseClicked(e -> addNewAccount());
})))
.add(accountPane)
.startCategory(Launcher.i18n("launcher").toUpperCase())
.add(launcherSettingsItem)
.add(new ClassTitle(Launcher.i18n("profile.title").toUpperCase(), Lang.apply(new JFXButton(), button -> {
button.setGraphic(SVG.plus(Theme.blackFillBinding(), 10, 10));
button.getStyleClass().add("toggle-icon-tiny");
button.setOnMouseClicked(e ->
Controllers.getDecorator().showPage(new ProfilePage(null)));
borderPane.setRight(addProfileButton);
})))
.add(profilePane);
EventBus.EVENT_BUS.channel(AccountLoadingEvent.class).register(this::onAccountsLoading);
EventBus.EVENT_BUS.channel(ProfileLoadingEvent.class).register(this::onProfilesLoading);
EventBus.EVENT_BUS.channel(ProfileChangedEvent.class).register(this::onProfileChanged);
EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).register(this::onRefreshedVersions);
FXUtils.onChangeAndOperate(Settings.INSTANCE.selectedAccountProperty(), it -> {
if (it == null) {
accountItem.setVersionName(Main.i18n("account.missing"));
accountItem.setGameVersion(Main.i18n("message.unknown"));
} else {
accountItem.setVersionName(it.getCharacter());
accountItem.setGameVersion(AccountsPage.accountType(it));
}
FXUtils.onChangeAndOperate(Settings.INSTANCE.selectedAccountProperty(), this::onSelectedAccountChanged);
onAccountsLoading();
}
if (it instanceof YggdrasilAccount) {
Image image = AccountHelper.getSkin((YggdrasilAccount) it, 4);
accountItem.setImage(image, AccountHelper.getViewport(4));
} else
accountItem.setImage(AccountHelper.getDefaultSkin(it, 4), AccountHelper.getViewport(4));
private void addNewAccount() {
Controllers.dialog(new AddAccountPane(Controllers::closeDialog));
}
private void onSelectedAccountChanged(Account newAccount) {
Platform.runLater(() -> {
for (Node node : accountPane.getChildren()) {
if (node instanceof RipplerContainer && node.getProperties().get("account") instanceof Account) {
boolean current = Objects.equals(node.getProperties().get("account"), newAccount);
((RipplerContainer) node).setSelected(current);
}
}
});
}
@@ -110,7 +115,7 @@ public final class LeftPaneController {
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") : "");
((VersionListItem) ((RipplerContainer) node).getContainer()).setGameVersion(current ? Launcher.i18n("profile.selected") : "");
}
}
});
@@ -121,7 +126,7 @@ public final class LeftPaneController {
for (Profile profile : Settings.INSTANCE.getProfiles()) {
VersionListItem item = new VersionListItem(Profiles.getProfileDisplayName(profile));
RipplerContainer ripplerContainer = new RipplerContainer(item);
item.setOnSettingsButtonClicked(() -> Controllers.getDecorator().showPage(new ProfilePage(profile)));
item.setOnSettingsButtonClicked(e -> Controllers.getDecorator().showPage(new ProfilePage(profile)));
ripplerContainer.setOnMouseClicked(e -> Settings.INSTANCE.setSelectedProfile(profile));
ripplerContainer.getProperties().put("profile", profile.getName());
ripplerContainer.maxWidthProperty().bind(leftPane.widthProperty());
@@ -130,6 +135,62 @@ public final class LeftPaneController {
Platform.runLater(() -> profilePane.getChildren().setAll(list));
}
private static String accountType(Account account) {
if (account instanceof OfflineAccount) return Launcher.i18n("account.methods.offline");
else if (account instanceof YggdrasilAccount) return account.getUsername();
else throw new Error(Launcher.i18n("account.methods.no_method") + ": " + account);
}
private void onAccountsLoading() {
LinkedList<RipplerContainer> list = new LinkedList<>();
Account selectedAccount = Settings.INSTANCE.getSelectedAccount();
for (Account account : Settings.INSTANCE.getAccounts()) {
VersionListItem item = new VersionListItem(account.getCharacter(), accountType(account));
RipplerContainer ripplerContainer = new RipplerContainer(item);
item.setOnSettingsButtonClicked(e -> {
AccountPage accountPage = new AccountPage(account, item);
JFXPopup popup = new JFXPopup(accountPage);
accountPage.setOnDelete(popup::hide);
popup.show((Node) e.getSource(), JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.LEFT, e.getX(), e.getY());
});
ripplerContainer.setOnMouseClicked(e -> {
if (e.getButton() == MouseButton.PRIMARY)
Settings.INSTANCE.setSelectedAccount(account);
});
ripplerContainer.getProperties().put("account", account);
ripplerContainer.maxWidthProperty().bind(leftPane.widthProperty());
if (account instanceof YggdrasilAccount) {
Image image = AccountHelper.getSkin((YggdrasilAccount) account, 4);
item.setImage(image, AccountHelper.getViewport(4));
} else
item.setImage(AccountHelper.getDefaultSkin(account, 4), AccountHelper.getViewport(4));
if (account instanceof AuthlibInjectorAccount)
Accounts.getAuthlibInjectorServerNameAsync((AuthlibInjectorAccount) account)
.subscribe(Schedulers.javafx(), variables -> FXUtils.installTooltip(ripplerContainer, 500, 5000, 0, new Tooltip(variables.get("serverName"))));
if (selectedAccount == account)
ripplerContainer.setSelected(true);
list.add(ripplerContainer);
}
if (Settings.INSTANCE.getAccounts().isEmpty()) {
RipplerContainer container = new RipplerContainer(missingAccountItem);
missingAccountItem.setOnSettingsButtonClicked(e -> addNewAccount());
container.setOnMouseClicked(e -> addNewAccount());
list.add(container);
}
Platform.runLater(() -> accountPane.getChildren().setAll(list));
}
public void showUpdate() {
launcherSettingsItem.setText(Launcher.i18n("update.found"));
launcherSettingsItem.setTextFill(Color.RED);
}
private boolean checkedModpack = false;
private void onRefreshedVersions(RefreshedVersionsEvent event) {
@@ -148,8 +209,8 @@ public final class LeftPaneController {
.with(Task.of(Schedulers.javafx(), () -> {
Controllers.closeDialog();
checkAccount();
})).executor(),
Main.i18n("modpack.installing"), "", null);
})).executor(true),
Launcher.i18n("modpack.installing"), "", null);
flag = false;
} catch (UnsupportedModpackException ignore) {
}
@@ -162,8 +223,8 @@ public final class LeftPaneController {
});
}
private void checkAccount() {
public void checkAccount() {
if (Settings.INSTANCE.getAccounts().isEmpty())
Controllers.navigate(new AccountsPage());
addNewAccount();
}
}

View File

@@ -30,7 +30,7 @@ import javafx.scene.layout.StackPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.event.Event;
import org.jackhuang.hmcl.event.EventManager;
import org.jackhuang.hmcl.game.LauncherHelper;
@@ -63,7 +63,7 @@ public final class LogWindow extends Stage {
public LogWindow() {
setScene(new Scene(impl, 800, 480));
getScene().getStylesheets().addAll(Settings.INSTANCE.getTheme().getStylesheets());
setTitle(Main.i18n("logwindow.title"));
setTitle(Launcher.i18n("logwindow.title"));
getIcons().add(new Image("/assets/img/icon.png"));
}

View File

@@ -18,10 +18,7 @@
package org.jackhuang.hmcl.ui;
import com.jfoenix.concurrency.JFXUtilities;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXListView;
import com.jfoenix.controls.JFXMasonryPane;
import com.jfoenix.controls.JFXPopup;
import com.jfoenix.controls.*;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
@@ -30,10 +27,11 @@ import javafx.scene.image.Image;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.StackPane;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.event.EventBus;
import org.jackhuang.hmcl.event.ProfileChangedEvent;
import org.jackhuang.hmcl.event.RefreshedVersionsEvent;
import org.jackhuang.hmcl.event.RefreshingVersionsEvent;
import org.jackhuang.hmcl.game.*;
import org.jackhuang.hmcl.mod.MismatchedModpackTypeException;
import org.jackhuang.hmcl.mod.UnsupportedModpackException;
@@ -46,54 +44,58 @@ 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;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
public final class MainPage extends StackPane implements DecoratorPage {
private final StringProperty title = new SimpleStringProperty(this, "title", Main.i18n("main_page"));
private final StringProperty title = new SimpleStringProperty(this, "title", Launcher.i18n("main_page"));
private Profile profile;
private String rightClickedVersion;
@FXML
private JFXButton btnRefresh;
@FXML
private StackPane contentPane;
@FXML
private JFXButton btnAdd;
@FXML
private JFXSpinner spinner;
@FXML
private JFXMasonryPane masonryPane;
@FXML
private JFXListView versionList;
private final JFXPopup versionPopup;
{
FXUtils.loadFXML(this, "/assets/fxml/main.fxml");
loadingVersions();
EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).register(event -> {
if (event.getSource() == profile.getRepository())
loadVersions((HMCLGameRepository) event.getSource());
});
EventBus.EVENT_BUS.channel(ProfileChangedEvent.class).register(event -> this.profile = event.getProfile());
EventBus.EVENT_BUS.channel(RefreshingVersionsEvent.class).register(event -> {
if (event.getSource() == profile.getRepository())
JFXUtilities.runInFXAndWait(this::loadingVersions);
});
EventBus.EVENT_BUS.channel(ProfileChangedEvent.class).register(event -> {
this.profile = event.getProfile();
});
versionPopup = new JFXPopup(versionList);
getChildren().remove(versionList);
btnAdd.setOnMouseClicked(e -> Controllers.getDecorator().startWizard(new DownloadWizardProvider(), Main.i18n("install")));
FXUtils.installTooltip(btnAdd, Main.i18n("install"));
btnAdd.setOnMouseClicked(e -> Controllers.getDecorator().startWizard(new DownloadWizardProvider(), Launcher.i18n("install")));
FXUtils.installTooltip(btnAdd, Launcher.i18n("install"));
btnRefresh.setOnMouseClicked(e -> Settings.INSTANCE.getSelectedProfile().getRepository().refreshVersionsAsync().start());
FXUtils.installTooltip(btnRefresh, Main.i18n("button.refresh"));
FXUtils.installTooltip(btnRefresh, Launcher.i18n("button.refresh"));
}
private Node buildNode(HMCLGameRepository repository, Version version, String game) {
Profile profile = repository.getProfile();
String id = version.getId();
VersionItem item = new VersionItem();
item.setUpdate(repository.isModpack(id));
@@ -103,36 +105,37 @@ public final class MainPage extends StackPane implements DecoratorPage {
StringBuilder libraries = new StringBuilder();
for (Library library : 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");
libraries.append(Launcher.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");
libraries.append(Launcher.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");
libraries.append(Launcher.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"));
Controllers.getLeftPaneController().checkAccount();
else
LauncherHelper.INSTANCE.launch(id, null);
LauncherHelper.INSTANCE.launch(profile, Settings.INSTANCE.getSelectedAccount(), id, null);
});
item.setOnScriptButtonClicked(e -> {
if (Settings.INSTANCE.getSelectedAccount() == null)
Controllers.dialog(Main.i18n("login.empty_username"));
Controllers.dialog(Launcher.i18n("login.empty_username"));
else {
FileChooser chooser = new FileChooser();
chooser.setInitialDirectory(repository.getRunDirectory(id));
chooser.setTitle(Main.i18n("version.launch_script.save"));
if (repository.getRunDirectory(id).isDirectory())
chooser.setInitialDirectory(repository.getRunDirectory(id));
chooser.setTitle(Launcher.i18n("version.launch_script.save"));
chooser.getExtensionFilters().add(OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS
? new FileChooser.ExtensionFilter(Main.i18n("extension.bat"), "*.bat")
: new FileChooser.ExtensionFilter(Main.i18n("extension.sh"), "*.sh"));
? new FileChooser.ExtensionFilter(Launcher.i18n("extension.bat"), "*.bat")
: new FileChooser.ExtensionFilter(Launcher.i18n("extension.sh"), "*.sh"));
File file = chooser.showSaveDialog(Controllers.getStage());
if (file != null)
LauncherHelper.INSTANCE.launch(id, file);
LauncherHelper.INSTANCE.launch(profile, Settings.INSTANCE.getSelectedAccount(), id, file);
}
});
item.setOnSettingsButtonClicked(e -> {
@@ -141,37 +144,62 @@ public final class MainPage extends StackPane implements DecoratorPage {
});
item.setOnUpdateButtonClicked(event -> {
FileChooser chooser = new FileChooser();
chooser.setTitle(Main.i18n("modpack.choose"));
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(Main.i18n("modpack"), "*.zip"));
chooser.setTitle(Launcher.i18n("modpack.choose"));
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(Launcher.i18n("modpack"), "*.zip"));
File selectedFile = chooser.showOpenDialog(Controllers.getStage());
if (selectedFile != null) {
TaskExecutorDialogPane pane = new TaskExecutorDialogPane(null);
try {
TaskExecutor executor = ModpackHelper.getUpdateTask(profile, selectedFile, id, ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(id)))
.then(Task.of(Schedulers.javafx(), Controllers::closeDialog)).executor();
pane.setExecutor(executor);
pane.setTitle(Main.i18n("modpack.update"));
executor.start();
Controllers.dialog(pane);
.then(Task.of(Schedulers.javafx(), Controllers::closeDialog)).executor(true);
Controllers.taskDialog(executor, Launcher.i18n("modpack.update"), "", null);
} catch (UnsupportedModpackException e) {
Controllers.dialog(Main.i18n("modpack.unsupported"), Main.i18n("message.error"), MessageBox.ERROR_MESSAGE);
Controllers.closeDialog();
Controllers.dialog(Launcher.i18n("modpack.unsupported"), Launcher.i18n("message.error"), MessageBox.ERROR_MESSAGE);
} catch (MismatchedModpackTypeException e) {
Controllers.dialog(Main.i18n("modpack.mismatched_type"), Main.i18n("message.error"), MessageBox.ERROR_MESSAGE);
Controllers.closeDialog();
Controllers.dialog(Launcher.i18n("modpack.mismatched_type"), Launcher.i18n("message.error"), MessageBox.ERROR_MESSAGE);
} catch (IOException e) {
Controllers.dialog(Main.i18n("modpack.invalid"), Main.i18n("message.error"), MessageBox.ERROR_MESSAGE);
Controllers.closeDialog();
Controllers.dialog(Launcher.i18n("modpack.invalid"), Launcher.i18n("message.error"), MessageBox.ERROR_MESSAGE);
}
}
});
item.setOnMouseClicked(event -> {
if (event.getButton() == MouseButton.SECONDARY) {
rightClickedVersion = id;
versionList.getSelectionModel().select(-1);
JFXListView<String> versionList = new JFXListView<>();
JFXPopup versionPopup = new JFXPopup(versionList);
versionList.getStyleClass().add("option-list-view");
FXUtils.setLimitWidth(versionList, 150);
versionList.getItems().setAll(Lang.immutableListOf(
Launcher.i18n("version.manage.rename"),
Launcher.i18n("version.manage.remove"),
Launcher.i18n("modpack.export"),
Launcher.i18n("folder.game")
));
versionList.setOnMouseClicked(e ->{
versionPopup.hide();
switch (versionList.getSelectionModel().getSelectedIndex()) {
case 0:
VersionPage.renameVersion(profile, id);
break;
case 1:
VersionPage.deleteVersion(profile, id);
break;
case 2:
VersionPage.exportVersion(profile, id);
break;
case 3:
FXUtils.openFolder(repository.getRunDirectory(id));
break;
default:
throw new Error();
}});
versionPopup.show(item, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.LEFT, event.getX(), event.getY());
} else if (event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2) {
if (Settings.INSTANCE.getSelectedAccount() == null)
Controllers.dialog(Main.i18n("login.empty_username"));
Controllers.dialog(Launcher.i18n("login.empty_username"));
else
LauncherHelper.INSTANCE.launch(id, null);
LauncherHelper.INSTANCE.launch(profile, Settings.INSTANCE.getSelectedAccount(), id, null);
}
});
File iconFile = repository.getVersionIcon(id);
@@ -180,33 +208,22 @@ public final class MainPage extends StackPane implements DecoratorPage {
return item;
}
private void loadingVersions() {
getChildren().setAll(spinner);
FXUtils.resetChildren(masonryPane, Collections.emptyList());
}
private void loadVersions(HMCLGameRepository repository) {
List<Node> children = new LinkedList<>();
for (Version version : repository.getVersions()) {
children.add(buildNode(repository, version, GameVersion.minecraftVersion(repository.getVersionJar(version.getId())).orElse("Unknown")));
}
JFXUtilities.runInFX(() -> FXUtils.resetChildren(masonryPane, children));
}
@FXML
private void onVersionManagement() {
versionPopup.hide();
switch (versionList.getSelectionModel().getSelectedIndex()) {
case 0:
VersionPage.renameVersion(profile, rightClickedVersion);
break;
case 1:
VersionPage.deleteVersion(profile, rightClickedVersion);
break;
case 2:
VersionPage.exportVersion(profile, rightClickedVersion);
break;
case 3:
FXUtils.openFolder(profile.getRepository().getRunDirectory(rightClickedVersion));
break;
default:
throw new Error();
}
JFXUtilities.runInFX(() -> {
if (profile == repository.getProfile()) {
getChildren().setAll(contentPane);
FXUtils.resetChildren(masonryPane, children);
}
});
}
public String getTitle() {

View File

@@ -17,16 +17,16 @@
*/
package org.jackhuang.hmcl.ui;
import com.jfoenix.concurrency.JFXUtilities;
import com.jfoenix.controls.JFXSpinner;
import com.jfoenix.controls.JFXTabPane;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.ScrollPane;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.mod.ModInfo;
import org.jackhuang.hmcl.mod.ModManager;
import org.jackhuang.hmcl.task.Schedulers;
@@ -93,7 +93,7 @@ public final class ModController {
this.versionId = versionId;
Task.of(variables -> {
synchronized (ModController.this) {
Platform.runLater(() -> {
JFXUtilities.runInFX(() -> {
rootPane.getChildren().remove(contentPane);
spinner.setVisible(true);
});
@@ -121,25 +121,24 @@ public final class ModController {
list.add(item);
}
Platform.runLater(() -> {
rootPane.getChildren().add(contentPane);
spinner.setVisible(false);
});
variables.set("list", list);
}
}).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();
}).finalized(Schedulers.javafx(), (variables, isDependentsSucceeded) -> {
rootPane.getChildren().add(contentPane);
spinner.setVisible(false);
if (isDependentsSucceeded)
FXUtils.onWeakChangeAndOperate(parentTab.getSelectionModel().selectedItemProperty(), newValue -> {
if (newValue != null && newValue.getUserData() == ModController.this)
modPane.getChildren().setAll(variables.<List<ModItem>>get("list"));
});
}).start();
}
@FXML
private void onAdd() {
FileChooser chooser = new FileChooser();
chooser.setTitle(Main.i18n("mods.choose_mod"));
chooser.getExtensionFilters().setAll(new FileChooser.ExtensionFilter(Main.i18n("extension.mod"), "*.jar", "*.zip", "*.litemod"));
chooser.setTitle(Launcher.i18n("mods.choose_mod"));
chooser.getExtensionFilters().setAll(new FileChooser.ExtensionFilter(Launcher.i18n("extension.mod"), "*.jar", "*.zip", "*.litemod"));
List<File> res = chooser.showOpenMultipleDialog(Controllers.getStage());
// It's guaranteed that succeeded and failed are thread safe here.
@@ -161,10 +160,10 @@ public final class ModController {
}).with(Task.of(Schedulers.javafx(), variables -> {
List<String> prompt = new LinkedList<>();
if (!succeeded.isEmpty())
prompt.add(Main.i18n("mods.add.success", String.join(", ", succeeded)));
prompt.add(Launcher.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"));
prompt.add(Launcher.i18n("mods.add.failed", String.join(", ", failed)));
Controllers.dialog(String.join("\n", prompt), Launcher.i18n("mods.add"));
loadMods(modManager, versionId);
})).start();
}

View File

@@ -22,7 +22,7 @@ import com.jfoenix.controls.JFXCheckBox;
import com.jfoenix.effects.JFXDepthManager;
import javafx.geometry.Pos;
import javafx.scene.layout.BorderPane;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.mod.ModInfo;
import org.jackhuang.hmcl.setting.Theme;
@@ -40,7 +40,7 @@ public final class ModItem extends BorderPane {
setCenter(modItem);
JFXButton btnRemove = new JFXButton();
FXUtils.installTooltip(btnRemove, Main.i18n("mods.remove"));
FXUtils.installTooltip(btnRemove, Launcher.i18n("mods.remove"));
btnRemove.setOnMouseClicked(e -> deleteCallback.accept(this));
btnRemove.getStyleClass().add("toggle-icon4");
BorderPane.setAlignment(btnRemove, Pos.CENTER);
@@ -50,7 +50,7 @@ public final class ModItem extends BorderPane {
setStyle("-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;");
JFXDepthManager.setDepth(this, 1);
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());
modItem.setSubtitle(info.getName() + ", " + Launcher.i18n("archive.version") + ": " + info.getVersion() + ", " + Launcher.i18n("archive.game_version") + ": " + info.getGameVersion() + ", " + Launcher.i18n("archive.author") + ": " + info.getAuthors());
chkEnabled.setSelected(info.isActive());
chkEnabled.selectedProperty().addListener((a, b, newValue) ->
info.activeProperty().set(newValue));

View File

@@ -23,7 +23,7 @@ import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.Profiles;
import org.jackhuang.hmcl.setting.Settings;
@@ -54,7 +54,7 @@ public final class ProfilePage extends StackPane implements DecoratorPage {
String profileDisplayName = Optional.ofNullable(profile).map(Profiles::getProfileDisplayName).orElse("");
title = new SimpleStringProperty(this, "title",
profile == null ? Main.i18n("profile.new") : Main.i18n("profile") + " - " + profileDisplayName);
profile == null ? Launcher.i18n("profile.new") : Launcher.i18n("profile") + " - " + profileDisplayName);
location = new SimpleStringProperty(this, "location",
Optional.ofNullable(profile).map(Profile::getGameDir).map(File::getAbsolutePath).orElse(""));

View File

@@ -34,9 +34,12 @@ 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.Launcher;
import org.jackhuang.hmcl.setting.*;
import org.jackhuang.hmcl.ui.construct.*;
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.wizard.DecoratorPage;
import org.jackhuang.hmcl.util.Lang;
@@ -46,7 +49,7 @@ 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"));
private final StringProperty title = new SimpleStringProperty(this, "title", Launcher.i18n("settings.launcher"));
@FXML
private JFXTextField txtProxyHost;
@@ -174,12 +177,12 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
fileCommonLocation.setProperty(Settings.INSTANCE.commonPathProperty());
FXUtils.installTooltip(btnUpdate, Main.i18n("update.tooltip"));
FXUtils.installTooltip(btnUpdate, Launcher.i18n("update.tooltip"));
checkUpdate();
// background
backgroundItem.loadChildren(Collections.singletonList(
backgroundItem.createChildren(Main.i18n("launcher.background.default"), EnumBackgroundImage.DEFAULT)
backgroundItem.createChildren(Launcher.i18n("launcher.background.default"), EnumBackgroundImage.DEFAULT)
));
FXUtils.bindString(backgroundItem.getTxtCustom(), Settings.INSTANCE.backgroundImageProperty());
@@ -196,8 +199,8 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
// theme
JFXColorPicker picker = new JFXColorPicker(Color.web(Settings.INSTANCE.getTheme().getColor()), null);
picker.setCustomColorText(Main.i18n("color.custom"));
picker.setRecentColorsText(Main.i18n("color.recent"));
picker.setCustomColorText(Launcher.i18n("color.custom"));
picker.setRecentColorsText(Launcher.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()));
@@ -211,7 +214,7 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
private void initBackgroundItemSubtitle() {
switch (Settings.INSTANCE.getBackgroundImageType()) {
case DEFAULT:
backgroundItem.setSubtitle(Main.i18n("launcher.background.default"));
backgroundItem.setSubtitle(Launcher.i18n("launcher.background.default"));
break;
case CUSTOM:
backgroundItem.setSubtitle(Settings.INSTANCE.getBackgroundImage());
@@ -233,25 +236,25 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
}
public void checkUpdate() {
btnUpdate.setVisible(Main.UPDATE_CHECKER.isOutOfDate());
btnUpdate.setVisible(Launcher.UPDATE_CHECKER.isOutOfDate());
if (Main.UPDATE_CHECKER.isOutOfDate()) {
lblUpdateSub.setText(Main.i18n("update.newest_version", Main.UPDATE_CHECKER.getNewVersion().toString()));
if (Launcher.UPDATE_CHECKER.isOutOfDate()) {
lblUpdateSub.setText(Launcher.i18n("update.newest_version", Launcher.UPDATE_CHECKER.getNewVersion().toString()));
lblUpdateSub.getStyleClass().setAll("update-label");
lblUpdate.setText(Main.i18n("update.found"));
lblUpdate.setText(Launcher.i18n("update.found"));
lblUpdate.getStyleClass().setAll("update-label");
} else {
lblUpdateSub.setText(Main.i18n("update.latest"));
lblUpdateSub.setText(Launcher.i18n("update.latest"));
lblUpdateSub.getStyleClass().setAll("subtitle-label");
lblUpdate.setText(Main.i18n("update"));
lblUpdate.setText(Launcher.i18n("update"));
lblUpdate.getStyleClass().setAll();
}
}
@FXML
private void onUpdate() {
Main.UPDATE_CHECKER.checkOutdate();
Launcher.UPDATE_CHECKER.checkOutdate();
}
}

View File

@@ -32,7 +32,7 @@ import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.setting.Theme;
import java.util.Optional;
@@ -73,10 +73,10 @@ public final class VersionItem extends StackPane {
btnLaunch.setGraphic(SVG.launch(Theme.blackFillBinding(), 15, 15));
btnScript.setGraphic(SVG.script(Theme.blackFillBinding(), 15, 15));
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"));
FXUtils.installTooltip(btnSettings, Launcher.i18n("version.settings"));
FXUtils.installTooltip(btnUpdate, Launcher.i18n("version.update"));
FXUtils.installTooltip(btnLaunch, Launcher.i18n("version.launch"));
FXUtils.installTooltip(btnScript, Launcher.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);

View File

@@ -17,11 +17,14 @@
*/
package org.jackhuang.hmcl.ui;
import com.jfoenix.controls.JFXButton;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.geometry.Rectangle2D;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
public final class VersionListItem extends StackPane {
@@ -33,7 +36,7 @@ public final class VersionListItem extends StackPane {
private Label lblGameVersion;
@FXML
private ImageView imageView;
private Runnable handler;
@FXML private JFXButton btnSettings;
public VersionListItem(String versionName) {
this(versionName, "");
@@ -48,13 +51,8 @@ public final class VersionListItem extends StackPane {
FXUtils.limitSize(imageView, 32, 32);
}
@FXML
private void onSettings() {
handler.run();
}
public void setOnSettingsButtonClicked(Runnable handler) {
this.handler = handler;
public void setOnSettingsButtonClicked(EventHandler<? super MouseEvent> handler) {
btnSettings.setOnMouseClicked(handler);
}
public void setVersionName(String versionName) {

View File

@@ -26,7 +26,7 @@ import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.scene.control.Tab;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.download.game.GameAssetIndexDownloadTask;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.ui.export.ExportWizardProvider;
@@ -79,17 +79,17 @@ public final class VersionPage extends StackPane implements DecoratorPage {
browsePopup = new JFXPopup(browseList);
managementPopup = new JFXPopup(managementList);
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"));
FXUtils.installTooltip(btnDelete, Launcher.i18n("version.manage.remove"));
FXUtils.installTooltip(btnBrowseMenu, Launcher.i18n("settings.game.exploration"));
FXUtils.installTooltip(btnManagementMenu, Launcher.i18n("settings.game.management"));
FXUtils.installTooltip(btnExport, Launcher.i18n("modpack.export"));
}
public void load(String id, Profile profile) {
this.version = id;
this.profile = profile;
title.set(Main.i18n("settings.game") + " - " + id);
title.set(Launcher.i18n("settings.game") + " - " + id);
versionSettingsController.loadVersionSetting(profile, id);
modController.setParentTab(tabPane);
@@ -185,7 +185,7 @@ public final class VersionPage extends StackPane implements DecoratorPage {
}
public static void deleteVersion(Profile profile, String version) {
Controllers.confirmDialog(Main.i18n("version.manage.remove.confirm", version), Main.i18n("message.confirm"), () -> {
Controllers.confirmDialog(Launcher.i18n("version.manage.remove.confirm", version), Launcher.i18n("message.confirm"), () -> {
if (profile.getRepository().removeVersionFromDisk(version)) {
profile.getRepository().refreshVersionsAsync().start();
Controllers.navigate(null);
@@ -194,7 +194,7 @@ public final class VersionPage extends StackPane implements DecoratorPage {
}
public static void renameVersion(Profile profile, String version) {
Controllers.inputDialog(Main.i18n("version.manage.rename.message"), res -> {
Controllers.inputDialog(Launcher.i18n("version.manage.rename.message"), res -> {
if (profile.getRepository().renameVersion(version, res)) {
profile.getRepository().refreshVersionsAsync().start();
Controllers.navigate(null);
@@ -203,6 +203,6 @@ public final class VersionPage extends StackPane implements DecoratorPage {
}
public static void exportVersion(Profile profile, String version) {
Controllers.getDecorator().startWizard(new ExportWizardProvider(profile, version), Main.i18n("modpack.wizard"));
Controllers.getDecorator().startWizard(new ExportWizardProvider(profile, version), Launcher.i18n("modpack.wizard"));
}
}

View File

@@ -17,7 +17,10 @@
*/
package org.jackhuang.hmcl.ui;
import com.jfoenix.controls.*;
import com.jfoenix.controls.JFXCheckBox;
import com.jfoenix.controls.JFXComboBox;
import com.jfoenix.controls.JFXTextField;
import com.jfoenix.controls.JFXToggleButton;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.Node;
@@ -27,7 +30,7 @@ import javafx.scene.control.Toggle;
import javafx.scene.image.Image;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.setting.EnumGameDirectory;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.VersionSetting;
@@ -77,7 +80,7 @@ public final class VersionSettingsController {
@FXML
private void initialize() {
lblPhysicalMemory.setText(Main.i18n("settings.physical_memory") + ": " + OperatingSystem.TOTAL_MEMORY + "MB");
lblPhysicalMemory.setText(Launcher.i18n("settings.physical_memory") + ": " + OperatingSystem.TOTAL_MEMORY + "MB");
FXUtils.smoothScrolling(scroll);
@@ -91,13 +94,13 @@ public final class VersionSettingsController {
javaItem.getExtensionFilters().add(new FileChooser.ExtensionFilter("Java", "java.exe", "javaw.exe"));
gameDirItem.loadChildren(Arrays.asList(
gameDirItem.createChildren(Main.i18n("settings.advanced.game_dir.default"), EnumGameDirectory.ROOT_FOLDER),
gameDirItem.createChildren(Main.i18n("settings.advanced.game_dir.independent"), EnumGameDirectory.VERSION_FOLDER)
gameDirItem.createChildren(Launcher.i18n("settings.advanced.game_dir.default"), EnumGameDirectory.ROOT_FOLDER),
gameDirItem.createChildren(Launcher.i18n("settings.advanced.game_dir.independent"), EnumGameDirectory.VERSION_FOLDER)
));
globalItem.loadChildren(Arrays.asList(
globalItem.createChildren(Main.i18n("settings.type.global"), true),
globalItem.createChildren(Main.i18n("settings.type.special"), false)
globalItem.createChildren(Launcher.i18n("settings.type.global"), true),
globalItem.createChildren(Launcher.i18n("settings.type.special"), false)
));
}
@@ -189,7 +192,7 @@ public final class VersionSettingsController {
});
versionSetting.usesGlobalProperty().setChangedListenerAndOperate(it ->
globalItem.setSubtitle(Main.i18n(versionSetting.isUsesGlobal() ? "settings.type.global" : "settings.type.special")));
globalItem.setSubtitle(Launcher.i18n(versionSetting.isUsesGlobal() ? "settings.type.global" : "settings.type.special")));
gameDirItem.getGroup().getToggles().stream()
.filter(it -> it.getUserData() == versionSetting.getGameDirType())
@@ -229,7 +232,7 @@ public final class VersionSettingsController {
@FXML
private void onExploreIcon() {
FileChooser chooser = new FileChooser();
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(Main.i18n("extension.png"), "*.png"));
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(Launcher.i18n("extension.png"), "*.png"));
File selectedFile = chooser.showOpenDialog(Controllers.getStage());
if (selectedFile != null) {
File iconFile = profile.getRepository().getVersionIcon(versionId);

View File

@@ -18,11 +18,13 @@
package org.jackhuang.hmcl.ui.construct;
import javafx.scene.Node;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import org.jackhuang.hmcl.util.Lang;
/**
* @author huangyuhui
@@ -48,6 +50,13 @@ public class ClassTitle extends StackPane {
getStyleClass().add("class-title");
}
public ClassTitle(String text, Node rightNode) {
this(Lang.apply(new BorderPane(), borderPane -> {
borderPane.setLeft(Lang.apply(new VBox(), vBox -> vBox.getChildren().setAll(new Text(text))));
borderPane.setRight(rightNode);
}));
}
public Node getContent() {
return content;
}

View File

@@ -60,9 +60,14 @@ public class ComponentList extends StackPane {
child.getStyleClass().add("options-list-item-ahead");
else
child.getStyleClass().add("options-list-item");
child.getProperties().put("node", node);
vbox.getChildren().add(child);
}
public void removeChildren(Node node) {
vbox.getChildren().removeIf(node1 -> node1.getProperties().get("node") == node);
}
public String getTitle() {
return title.get();
}

View File

@@ -26,7 +26,7 @@ import javafx.scene.control.Tooltip;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.DirectoryChooser;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
@@ -54,7 +54,7 @@ public class FileItem extends BorderPane {
right.setGraphic(SVG.pencil(Theme.blackFillBinding(), 15, 15));
right.getStyleClass().add("toggle-icon4");
right.setOnMouseClicked(e -> onExplore());
FXUtils.installTooltip(right, Main.i18n("button.edit"));
FXUtils.installTooltip(right, Launcher.i18n("button.edit"));
setRight(right);
Tooltip tip = new Tooltip();

View File

@@ -21,21 +21,19 @@ import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Paint;
public class IconedItem extends RipplerContainer {
private final Node icon;
private final String text;
public IconedItem(Node icon, String text) {
super(createHBox(icon, text));
this.icon = icon;
this.text = text;
}
private static HBox createHBox(Node icon, String text) {
HBox hBox = new HBox();
icon.setMouseTransparent(true);
Label textLabel = new Label(text);
textLabel.setId("label");
textLabel.setAlignment(Pos.CENTER);
textLabel.setMouseTransparent(true);
hBox.getChildren().addAll(icon, textLabel);
@@ -43,4 +41,12 @@ public class IconedItem extends RipplerContainer {
hBox.setAlignment(Pos.CENTER_LEFT);
return hBox;
}
public void setText(String text) {
((Label) lookup("#label")).setText(text);
}
public void setTextFill(Paint paint) {
((Label) lookup("#label")).setTextFill(paint);
}
}

View File

@@ -15,7 +15,7 @@ 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.Launcher;
import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG;
@@ -41,7 +41,7 @@ public final class ImagePickerItem extends BorderPane {
selectButton.onMouseClickedProperty().bind(onSelectButtonClicked);
selectButton.getStyleClass().add("toggle-icon4");
FXUtils.installTooltip(selectButton, Main.i18n("button.edit"));
FXUtils.installTooltip(selectButton, Launcher.i18n("button.edit"));
HBox hBox = new HBox();
hBox.getChildren().setAll(imageView, selectButton);

View File

@@ -17,10 +17,12 @@
*/
package org.jackhuang.hmcl.ui.construct;
import com.jfoenix.concurrency.JFXUtilities;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.TextInputDialog;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.ui.FXUtils;
import javax.swing.*;
import java.util.Optional;
@@ -29,7 +31,7 @@ public final class MessageBox {
private MessageBox() {
}
private static final String TITLE = Main.i18n("message.info");
private static final String TITLE = Launcher.i18n("message.info");
/**
* User Operation: Yes
@@ -123,30 +125,32 @@ public final class MessageBox {
}
public static int confirm(String message, String title, int option) {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle(title);
alert.setHeaderText(title);
alert.setContentText(message);
switch (option) {
case YES_NO_OPTION:
alert.getButtonTypes().setAll(ButtonType.YES, ButtonType.NO);
break;
case YES_NO_CANCEL_OPTION:
alert.getButtonTypes().setAll(ButtonType.YES, ButtonType.NO, ButtonType.CANCEL);
break;
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;
else if (buttonType.get() == ButtonType.OK) return OK_OPTION;
else if (buttonType.get() == ButtonType.YES) return YES_OPTION;
else if (buttonType.get() == ButtonType.NO) return NO_OPTION;
else if (buttonType.get() == ButtonType.CANCEL) return CANCEL_OPTION;
else throw new IllegalStateException("Unrecognized button type:" + buttonType.get());
return FXUtils.runInUIThread(() -> {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle(title);
alert.setHeaderText(title);
alert.setContentText(message);
switch (option) {
case YES_NO_OPTION:
alert.getButtonTypes().setAll(ButtonType.YES, ButtonType.NO);
break;
case YES_NO_CANCEL_OPTION:
alert.getButtonTypes().setAll(ButtonType.YES, ButtonType.NO, ButtonType.CANCEL);
break;
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;
else if (buttonType.get() == ButtonType.OK) return OK_OPTION;
else if (buttonType.get() == ButtonType.YES) return YES_OPTION;
else if (buttonType.get() == ButtonType.NO) return NO_OPTION;
else if (buttonType.get() == ButtonType.CANCEL) return CANCEL_OPTION;
else throw new IllegalStateException("Unexpected button type:" + buttonType.get());
});
}
public static Optional<String> input(String message) {

View File

@@ -23,7 +23,7 @@ import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG;
@@ -92,8 +92,8 @@ public final class MessageDialogPane extends StackPane {
Optional.ofNullable(onCancel).ifPresent(Runnable::run);
});
acceptButton.setText(Main.i18n("button.yes"));
cancelButton.setText(Main.i18n("button.no"));
acceptButton.setText(Launcher.i18n("button.yes"));
cancelButton.setText(Launcher.i18n("button.no"));
actions.getChildren().add(cancelButton);
}

View File

@@ -5,17 +5,15 @@ 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.Launcher;
import org.jackhuang.hmcl.ui.FXUtils;
import java.util.Collection;
@@ -23,8 +21,8 @@ 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 StringProperty customTitle = new SimpleStringProperty(this, "customTitle", Launcher.i18n("selector.custom"));
private final StringProperty chooserTitle = new SimpleStringProperty(this, "chooserTitle", Launcher.i18n("selector.choose_file"));
private final ToggleGroup group = new ToggleGroup();
private final JFXColorPicker colorPicker = new JFXColorPicker();

View File

@@ -38,7 +38,7 @@ 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.Launcher;
import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
@@ -49,8 +49,8 @@ import java.util.Collection;
import java.util.function.Consumer;
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 StringProperty customTitle = new SimpleStringProperty(this, "customTitle", Launcher.i18n("selector.custom"));
private final StringProperty chooserTitle = new SimpleStringProperty(this, "chooserTitle", Launcher.i18n("selector.choose_file"));
private final BooleanProperty directory = new SimpleBooleanProperty(this, "directory", false);
private final SimpleStringProperty tooltip = new SimpleStringProperty(this, "tooltip");
private final ObservableList<FileChooser.ExtensionFilter> extensionFilters = FXCollections.observableArrayList();
@@ -153,7 +153,7 @@ public class MultiFileItem extends ComponentList {
public void onExploreJavaDir() {
DirectoryChooser chooser = new DirectoryChooser();
chooser.setTitle(Main.i18n(getChooserTitle()));
chooser.setTitle(Launcher.i18n(getChooserTitle()));
File selectedDir = chooser.showDialog(Controllers.getStage());
if (selectedDir != null)
txtCustom.setText(selectedDir.getAbsolutePath());

View File

@@ -0,0 +1,45 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.ui.construct;
import javafx.scene.Node;
import javafx.scene.layout.StackPane;
import java.util.Stack;
public class StackContainerPane extends StackPane {
private final Stack<Node> stack = new Stack<>();
public void push(Node node) {
if (node.getProperties().containsKey("controllers"))
stack.push(node);
getChildren().setAll(node);
}
public void pop() {
stack.pop();
if (stack.isEmpty())
getChildren().setAll();
else
getChildren().setAll(stack.peek());
}
public boolean isEmpty() {
return stack.isEmpty();
}
}

View File

@@ -57,7 +57,9 @@ public class TaskExecutorDialogPane extends StackPane {
public void setExecutor(TaskExecutor executor) {
this.executor = executor;
taskListPane.setExecutor(executor);
if (executor != null)
taskListPane.setExecutor(executor);
}
public StringProperty titleProperty() {

View File

@@ -19,7 +19,7 @@ package org.jackhuang.hmcl.ui.construct;
import com.jfoenix.concurrency.JFXUtilities;
import javafx.beans.property.StringProperty;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskExecutor;
import org.jackhuang.hmcl.task.TaskListener;
@@ -38,7 +38,7 @@ public interface TaskExecutorDialogWizardDisplayer extends AbstractWizardDisplay
Controllers.navigate(null);
});
pane.setTitle(Main.i18n("message.doing"));
pane.setTitle(Launcher.i18n("message.doing"));
pane.setProgress(Double.MAX_VALUE);
if (settings.containsKey("title")) {
Object title = settings.get("title");
@@ -64,7 +64,7 @@ public interface TaskExecutorDialogWizardDisplayer extends AbstractWizardDisplay
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)));
JFXUtilities.runInFX(() -> Controllers.dialog(Launcher.i18n("message.success"), null, MessageBox.FINE_MESSAGE, () -> Controllers.navigate(null)));
} else {
if (executor.getLastException() == null)
return;
@@ -72,7 +72,7 @@ public interface TaskExecutorDialogWizardDisplayer extends AbstractWizardDisplay
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)));
JFXUtilities.runInFX(() -> Controllers.dialog(appendix, Launcher.i18n("wizard.failed"), MessageBox.ERROR_MESSAGE, () -> Controllers.navigate(null)));
}
}
});

View File

@@ -23,7 +23,7 @@ import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.download.forge.ForgeInstallTask;
import org.jackhuang.hmcl.download.game.GameAssetDownloadTask;
import org.jackhuang.hmcl.download.game.GameAssetRefreshTask;
@@ -62,29 +62,29 @@ public final class TaskListPane extends StackPane {
return;
if (task instanceof GameAssetRefreshTask) {
task.setName(Main.i18n("assets.download"));
task.setName(Launcher.i18n("assets.download"));
} else if (task instanceof GameAssetDownloadTask) {
task.setName(Main.i18n("assets.download_all"));
task.setName(Launcher.i18n("assets.download_all"));
} else if (task instanceof ForgeInstallTask) {
task.setName(Main.i18n("install.installer.install", Main.i18n("install.installer.forge")));
task.setName(Launcher.i18n("install.installer.install", Launcher.i18n("install.installer.forge")));
} else if (task instanceof LiteLoaderInstallTask) {
task.setName(Main.i18n("install.installer.install", Main.i18n("install.installer.liteloader")));
task.setName(Launcher.i18n("install.installer.install", Launcher.i18n("install.installer.liteloader")));
} else if (task instanceof OptiFineInstallTask) {
task.setName(Main.i18n("install.installer.install", Main.i18n("install.installer.optifine")));
task.setName(Launcher.i18n("install.installer.install", Launcher.i18n("install.installer.optifine")));
} else if (task instanceof CurseCompletionTask) {
task.setName(Main.i18n("modpack.type.curse.completion"));
task.setName(Launcher.i18n("modpack.type.curse.completion"));
} else if (task instanceof ModpackInstallTask) {
task.setName(Main.i18n("modpack.installing"));
task.setName(Launcher.i18n("modpack.installing"));
} else if (task instanceof CurseInstallTask) {
task.setName(Main.i18n("modpack.install", Main.i18n("modpack.type.curse")));
task.setName(Launcher.i18n("modpack.install", Launcher.i18n("modpack.type.curse")));
} else if (task instanceof MultiMCModpackInstallTask) {
task.setName(Main.i18n("modpack.install", Main.i18n("modpack.type.multimc")));
task.setName(Launcher.i18n("modpack.install", Launcher.i18n("modpack.type.multimc")));
} else if (task instanceof HMCLModpackInstallTask) {
task.setName(Main.i18n("modpack.install", Main.i18n("modpack.type.hmcl")));
task.setName(Launcher.i18n("modpack.install", Launcher.i18n("modpack.type.hmcl")));
} else if (task instanceof HMCLModpackExportTask) {
task.setName(Main.i18n("modpack.export"));
task.setName(Launcher.i18n("modpack.export"));
} else if (task instanceof MinecraftInstanceTask) {
task.setName(Main.i18n("modpack.scan"));
task.setName(Launcher.i18n("modpack.scan"));
}
ProgressListNode node = new ProgressListNode(task);

View File

@@ -22,7 +22,7 @@ import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.game.GameRepository;
@@ -34,7 +34,7 @@ import org.jackhuang.hmcl.util.Lang;
import java.util.Map;
import java.util.Optional;
import static org.jackhuang.hmcl.Main.i18n;
import static org.jackhuang.hmcl.Launcher.i18n;
class AdditionalInstallersPage extends StackPane implements WizardPage {
private final InstallerWizardProvider provider;
@@ -100,7 +100,7 @@ class AdditionalInstallersPage extends StackPane implements WizardPage {
@Override
public String getTitle() {
return Main.i18n("settings.tabs.installers");
return Launcher.i18n("settings.tabs.installers");
}
private String getVersion(String id) {
@@ -109,24 +109,24 @@ class AdditionalInstallersPage extends StackPane implements WizardPage {
@Override
public void onNavigate(Map<String, Object> settings) {
lblGameVersion.setText(Main.i18n("install.new_game.current_game_version") + ": " + provider.getGameVersion());
lblGameVersion.setText(Launcher.i18n("install.new_game.current_game_version") + ": " + provider.getGameVersion());
btnForge.setDisable(provider.getForge() != null);
if (provider.getForge() != null || controller.getSettings().containsKey("forge"))
lblForge.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.forge")) + ": " + Lang.nonNull(provider.getForge(), getVersion("forge")));
lblForge.setText(Launcher.i18n("install.installer.version", Launcher.i18n("install.installer.forge")) + ": " + Lang.nonNull(provider.getForge(), getVersion("forge")));
else
lblForge.setText(Main.i18n("install.installer.not_installed", Main.i18n("install.installer.forge")));
lblForge.setText(Launcher.i18n("install.installer.not_installed", Launcher.i18n("install.installer.forge")));
btnLiteLoader.setDisable(provider.getLiteLoader() != null);
if (provider.getLiteLoader() != null || controller.getSettings().containsKey("liteloader"))
lblLiteLoader.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.liteloader")) + ": " + Lang.nonNull(provider.getLiteLoader(), getVersion("liteloader")));
lblLiteLoader.setText(Launcher.i18n("install.installer.version", Launcher.i18n("install.installer.liteloader")) + ": " + Lang.nonNull(provider.getLiteLoader(), getVersion("liteloader")));
else
lblLiteLoader.setText(Main.i18n("install.installer.not_installed", Main.i18n("install.installer.liteloader")));
lblLiteLoader.setText(Launcher.i18n("install.installer.not_installed", Launcher.i18n("install.installer.liteloader")));
btnOptiFine.setDisable(provider.getOptiFine() != null);
if (provider.getOptiFine() != null || controller.getSettings().containsKey("optifine"))
lblOptiFine.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.optifine")) + ": " + Lang.nonNull(provider.getOptiFine(), getVersion("optifine")));
lblOptiFine.setText(Launcher.i18n("install.installer.version", Launcher.i18n("install.installer.optifine")) + ": " + Lang.nonNull(provider.getOptiFine(), getVersion("optifine")));
else
lblOptiFine.setText(Main.i18n("install.installer.not_installed", Main.i18n("install.installer.optifine")));
lblOptiFine.setText(Launcher.i18n("install.installer.not_installed", Launcher.i18n("install.installer.optifine")));
}

View File

@@ -18,7 +18,7 @@
package org.jackhuang.hmcl.ui.download;
import javafx.scene.Node;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.GameBuilder;
import org.jackhuang.hmcl.download.RemoteVersion;
@@ -34,7 +34,7 @@ import org.jackhuang.hmcl.util.Lang;
import java.io.File;
import java.util.Map;
import static org.jackhuang.hmcl.Main.i18n;
import static org.jackhuang.hmcl.Launcher.i18n;
public final class DownloadWizardProvider implements WizardProvider {
private Profile profile;
@@ -77,8 +77,8 @@ public final class DownloadWizardProvider implements WizardProvider {
@Override
public Object finish(Map<String, Object> settings) {
settings.put("success_message", Main.i18n("install.success"));
settings.put("failure_message", Main.i18n("install.failed"));
settings.put("success_message", Launcher.i18n("install.success"));
settings.put("failure_message", Launcher.i18n("install.failed"));
switch (Lang.parseInt(settings.get(InstallTypePage.INSTALL_TYPE), -1)) {
case 0: return finishVersionDownloadingAsync(settings);

View File

@@ -20,7 +20,7 @@ package org.jackhuang.hmcl.ui.download;
import com.jfoenix.controls.JFXListView;
import javafx.fxml.FXML;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.ui.wizard.WizardPage;
@@ -28,12 +28,10 @@ import org.jackhuang.hmcl.ui.wizard.WizardPage;
import java.util.Map;
public final class InstallTypePage extends StackPane implements WizardPage {
private final WizardController controller;
@FXML private JFXListView<Object> list;
public InstallTypePage(WizardController controller) {
this.controller = controller;
FXUtils.loadFXML(this, "/assets/fxml/download/dltype.fxml");
list.setOnMouseClicked(e -> {
@@ -51,7 +49,7 @@ public final class InstallTypePage extends StackPane implements WizardPage {
@Override
public String getTitle() {
return Main.i18n("install.select");
return Launcher.i18n("install.select");
}
public static final String INSTALL_TYPE = "INSTALL_TYPE";

View File

@@ -18,7 +18,7 @@
package org.jackhuang.hmcl.ui.download;
import javafx.scene.Node;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.game.Version;
@@ -80,8 +80,8 @@ public final class InstallerWizardProvider implements WizardProvider {
@Override
public Object finish(Map<String, Object> settings) {
settings.put("success_message", Main.i18n("install.success"));
settings.put("failure_message", Main.i18n("install.failed"));
settings.put("success_message", Launcher.i18n("install.success"));
settings.put("failure_message", Launcher.i18n("install.failed"));
Task ret = Task.empty();

View File

@@ -23,7 +23,7 @@ import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.game.GameRepository;
@@ -34,12 +34,9 @@ import org.jackhuang.hmcl.ui.wizard.WizardPage;
import org.jackhuang.hmcl.util.StringUtils;
import java.util.Map;
import java.util.Optional;
public class InstallersPage extends StackPane implements WizardPage {
private final WizardController controller;
private final GameRepository repository;
private final DownloadProvider downloadProvider;
@FXML
private VBox list;
@@ -73,37 +70,35 @@ public class InstallersPage extends StackPane implements WizardPage {
public InstallersPage(WizardController controller, GameRepository repository, DownloadProvider downloadProvider) {
this.controller = controller;
this.repository = repository;
this.downloadProvider = downloadProvider;
FXUtils.loadFXML(this, "/assets/fxml/download/installers.fxml");
String gameVersion = ((RemoteVersion<?>) controller.getSettings().get("game")).getGameVersion();
Validator hasVersion = new Validator(s -> !repository.hasVersion(s) && StringUtils.isNotBlank(s));
hasVersion.setMessage(Main.i18n("install.new_game.already_exists"));
hasVersion.setMessage(Launcher.i18n("install.new_game.already_exists"));
txtName.getValidators().add(hasVersion);
txtName.textProperty().addListener(e -> btnInstall.setDisable(!txtName.validate()));
txtName.setText(gameVersion);
btnForge.setOnMouseClicked(e -> {
controller.getSettings().put(INSTALLER_TYPE, 0);
controller.onNext(new VersionsPage(controller, Main.i18n("install.installer.choose", Main.i18n("install.installer.forge")), gameVersion, downloadProvider, "forge", () -> controller.onPrev(false)));
controller.onNext(new VersionsPage(controller, Launcher.i18n("install.installer.choose", Launcher.i18n("install.installer.forge")), gameVersion, downloadProvider, "forge", () -> controller.onPrev(false)));
});
btnLiteLoader.setOnMouseClicked(e -> {
controller.getSettings().put(INSTALLER_TYPE, 1);
controller.onNext(new VersionsPage(controller, Main.i18n("install.installer.choose", Main.i18n("install.installer.liteloader")), gameVersion, downloadProvider, "liteloader", () -> controller.onPrev(false)));
controller.onNext(new VersionsPage(controller, Launcher.i18n("install.installer.choose", Launcher.i18n("install.installer.liteloader")), gameVersion, downloadProvider, "liteloader", () -> controller.onPrev(false)));
});
btnOptiFine.setOnMouseClicked(e -> {
controller.getSettings().put(INSTALLER_TYPE, 2);
controller.onNext(new VersionsPage(controller, Main.i18n("install.installer.choose", Main.i18n("install.installer.optifine")), gameVersion, downloadProvider, "optifine", () -> controller.onPrev(false)));
controller.onNext(new VersionsPage(controller, Launcher.i18n("install.installer.choose", Launcher.i18n("install.installer.optifine")), gameVersion, downloadProvider, "optifine", () -> controller.onPrev(false)));
});
}
@Override
public String getTitle() {
return Main.i18n("install.new_game");
return Launcher.i18n("install.new_game");
}
private String getVersion(String id) {
@@ -112,21 +107,21 @@ public class InstallersPage extends StackPane implements WizardPage {
@Override
public void onNavigate(Map<String, Object> settings) {
lblGameVersion.setText(Main.i18n("install.new_game.current_game_version") + ": " + getVersion("game"));
lblGameVersion.setText(Launcher.i18n("install.new_game.current_game_version") + ": " + getVersion("game"));
if (controller.getSettings().containsKey("forge"))
lblForge.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.forge")) + ": " + getVersion("forge"));
lblForge.setText(Launcher.i18n("install.installer.version", Launcher.i18n("install.installer.forge")) + ": " + getVersion("forge"));
else
lblForge.setText(Main.i18n("install.installer.not_installed", Main.i18n("install.installer.forge")));
lblForge.setText(Launcher.i18n("install.installer.not_installed", Launcher.i18n("install.installer.forge")));
if (controller.getSettings().containsKey("liteloader"))
lblLiteLoader.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.liteloader")) + ": " + getVersion("liteloader"));
lblLiteLoader.setText(Launcher.i18n("install.installer.version", Launcher.i18n("install.installer.liteloader")) + ": " + getVersion("liteloader"));
else
lblLiteLoader.setText(Main.i18n("install.installer.not_installed", Main.i18n("install.installer.liteloader")));
lblLiteLoader.setText(Launcher.i18n("install.installer.not_installed", Launcher.i18n("install.installer.liteloader")));
if (controller.getSettings().containsKey("optifine"))
lblOptiFine.setText(Main.i18n("install.installer.version", Main.i18n("install.installer.optifine")) + ": " + getVersion("optifine"));
lblOptiFine.setText(Launcher.i18n("install.installer.version", Launcher.i18n("install.installer.optifine")) + ": " + getVersion("optifine"));
else
lblOptiFine.setText(Main.i18n("install.installer.not_installed", Main.i18n("install.installer.optifine")));
lblOptiFine.setText(Launcher.i18n("install.installer.not_installed", Launcher.i18n("install.installer.optifine")));
}
@Override

View File

@@ -25,7 +25,7 @@ import javafx.scene.control.Label;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.game.ModpackHelper;
import org.jackhuang.hmcl.mod.Modpack;
import org.jackhuang.hmcl.mod.UnsupportedModpackException;
@@ -75,16 +75,16 @@ public final class ModpackPage extends StackPane implements WizardPage {
Profile profile = (Profile) controller.getSettings().get("PROFILE");
FileChooser chooser = new FileChooser();
chooser.setTitle(Main.i18n("modpack.choose"));
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(Main.i18n("modpack"), "*.zip"));
chooser.setTitle(Launcher.i18n("modpack.choose"));
chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(Launcher.i18n("modpack"), "*.zip"));
File selectedFile = chooser.showOpenDialog(Controllers.getStage());
if (selectedFile == null) Platform.runLater(() -> Controllers.navigate(null));
else {
controller.getSettings().put(MODPACK_FILE, selectedFile);
lblModpackLocation.setText(selectedFile.getAbsolutePath());
txtModpackName.getValidators().addAll(
new Validator(Main.i18n("install.new_game.already_exists"), str -> !profile.getRepository().hasVersion(str) && StringUtils.isNotBlank(str)),
new Validator(Main.i18n("version.forbidden_name"), str -> !profile.getRepository().forbidsVersion(str))
new Validator(Launcher.i18n("install.new_game.already_exists"), str -> !profile.getRepository().hasVersion(str) && StringUtils.isNotBlank(str)),
new Validator(Launcher.i18n("version.forbidden_name"), str -> !profile.getRepository().forbidsVersion(str))
);
txtModpackName.textProperty().addListener(e -> btnInstall.setDisable(!txtModpackName.validate()));
@@ -96,7 +96,7 @@ public final class ModpackPage extends StackPane implements WizardPage {
lblAuthor.setText(manifest.getAuthor());
txtModpackName.setText(manifest.getName() + (StringUtils.isBlank(manifest.getVersion()) ? "" : "-" + manifest.getVersion()));
} catch (UnsupportedModpackException e) {
txtModpackName.setText(Main.i18n("modpack.task.install.error"));
txtModpackName.setText(Launcher.i18n("modpack.task.install.error"));
}
}
}
@@ -118,14 +118,14 @@ public final class ModpackPage extends StackPane implements WizardPage {
if (manifest != null) {
WebStage stage = new WebStage();
stage.getWebView().getEngine().loadContent(manifest.getDescription());
stage.setTitle(Main.i18n("modpack.wizard.step.3"));
stage.setTitle(Launcher.i18n("modpack.wizard.step.3"));
stage.showAndWait();
}
}
@Override
public String getTitle() {
return Main.i18n("modpack.task.install");
return Launcher.i18n("modpack.task.install");
}
public static final String MODPACK_FILE = "MODPACK_FILE";

View File

@@ -44,11 +44,9 @@ import java.util.Objects;
import java.util.stream.Collectors;
public final class VersionsPage extends StackPane implements WizardPage, Refreshable {
private final WizardController controller;
private final String gameVersion;
private final DownloadProvider downloadProvider;
private final String libraryId;
private final Runnable callback;
private final String title;
@FXML
@@ -73,12 +71,10 @@ public final class VersionsPage extends StackPane implements WizardPage, Refresh
private TaskExecutor executor;
public VersionsPage(WizardController controller, String title, String gameVersion, DownloadProvider downloadProvider, String libraryId, Runnable callback) {
this.controller = controller;
this.title = title;
this.gameVersion = gameVersion;
this.downloadProvider = downloadProvider;
this.libraryId = libraryId;
this.callback = callback;
this.versionList = downloadProvider.getVersionListById(libraryId);
FXUtils.loadFXML(this, "/assets/fxml/download/versions.fxml");
@@ -93,10 +89,10 @@ public final class VersionsPage extends StackPane implements WizardPage, Refresh
chkSnapshot.selectedProperty().addListener(listener);
chkOld.selectedProperty().addListener(listener);
list.getSelectionModel().selectedItemProperty().addListener((a, b, newValue) -> {
if (newValue == null)
list.setOnMouseClicked(e -> {
if (list.getSelectionModel().getSelectedIndex() < 0)
return;
controller.getSettings().put(libraryId, newValue.getRemoteVersion());
controller.getSettings().put(libraryId, list.getSelectionModel().getSelectedItem().getRemoteVersion());
callback.run();
});
refresh();

View File

@@ -23,7 +23,7 @@ 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.Launcher;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.game.GameRemoteVersionTag;
import org.jackhuang.hmcl.download.liteloader.LiteLoaderRemoteVersionTag;
@@ -57,15 +57,15 @@ public final class VersionsPageItem extends StackPane {
if (remoteVersion.getTag() instanceof GameRemoteVersionTag) {
switch (((GameRemoteVersionTag) remoteVersion.getTag()).getType()) {
case RELEASE:
lblGameVersion.setText(Main.i18n("version.game.release"));
lblGameVersion.setText(Launcher.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"));
lblGameVersion.setText(Launcher.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"));
lblGameVersion.setText(Launcher.i18n("version.game.old"));
imageView.setImage(new Image("/assets/img/grass.png", 32, 32, false, true));
break;
}

View File

@@ -25,7 +25,7 @@ import javafx.scene.control.Label;
import javafx.scene.control.TreeItem;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.game.ModAdviser;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.ui.FXUtils;
@@ -66,6 +66,9 @@ public final class ModpackFileSelectionPage extends StackPane implements WizardP
}
private CheckBoxTreeItem<String> getTreeItem(File file, String basePath) {
if (!file.exists())
return null;
ModAdviser.ModSuggestion state = ModAdviser.ModSuggestion.SUGGESTED;
if (basePath.length() > "minecraft/".length()) {
state = adviser.advise(StringUtils.substringAfter(basePath, "minecraft/") + (file.isDirectory() ? "/" : ""), file.isDirectory());
@@ -147,23 +150,23 @@ public final class ModpackFileSelectionPage extends StackPane implements WizardP
@Override
public String getTitle() {
return Main.i18n("modpack.wizard.step.2.title");
return Launcher.i18n("modpack.wizard.step.2.title");
}
public static final String MODPACK_FILE_SELECTION = "modpack.accepted";
private static final Map<String, String> TRANSLATION = Lang.mapOf(
new Pair<>("minecraft/servers.dat", Main.i18n("modpack.files.servers_dat")),
new Pair<>("minecraft/saves", Main.i18n("modpack.files.saves")),
new Pair<>("minecraft/mods", Main.i18n("modpack.files.mods")),
new Pair<>("minecraft/config", Main.i18n("modpack.files.config")),
new Pair<>("minecraft/liteconfig", Main.i18n("modpack.files.liteconfig")),
new Pair<>("minecraft/resourcepacks", Main.i18n("modpack.files.resourcepacks")),
new Pair<>("minecraft/resources", Main.i18n("modpack.files.resourcepacks")),
new Pair<>("minecraft/options.txt", Main.i18n("modpack.files.options_txt")),
new Pair<>("minecraft/optionsshaders.txt", Main.i18n("modpack.files.optionsshaders_txt")),
new Pair<>("minecraft/mods/VoxelMods", Main.i18n("modpack.files.mods.voxelmods")),
new Pair<>("minecraft/dumps", Main.i18n("modpack.files.dumps")),
new Pair<>("minecraft/blueprints", Main.i18n("modpack.files.blueprints")),
new Pair<>("minecraft/scripts", Main.i18n("modpack.files.scripts"))
new Pair<>("minecraft/servers.dat", Launcher.i18n("modpack.files.servers_dat")),
new Pair<>("minecraft/saves", Launcher.i18n("modpack.files.saves")),
new Pair<>("minecraft/mods", Launcher.i18n("modpack.files.mods")),
new Pair<>("minecraft/config", Launcher.i18n("modpack.files.config")),
new Pair<>("minecraft/liteconfig", Launcher.i18n("modpack.files.liteconfig")),
new Pair<>("minecraft/resourcepacks", Launcher.i18n("modpack.files.resourcepacks")),
new Pair<>("minecraft/resources", Launcher.i18n("modpack.files.resourcepacks")),
new Pair<>("minecraft/options.txt", Launcher.i18n("modpack.files.options_txt")),
new Pair<>("minecraft/optionsshaders.txt", Launcher.i18n("modpack.files.optionsshaders_txt")),
new Pair<>("minecraft/mods/VoxelMods", Launcher.i18n("modpack.files.mods.voxelmods")),
new Pair<>("minecraft/dumps", Launcher.i18n("modpack.files.dumps")),
new Pair<>("minecraft/blueprints", Launcher.i18n("modpack.files.blueprints")),
new Pair<>("minecraft/scripts", Launcher.i18n("modpack.files.scripts"))
);
}

View File

@@ -26,7 +26,7 @@ import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.StackPane;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.ui.Controllers;
@@ -72,8 +72,8 @@ public final class ModpackInfoPage extends StackPane implements WizardPage {
@FXML
private void onNext() {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle(Main.i18n("modpack.wizard.step.initialization.save"));
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(Main.i18n("modpack"), "*.zip"));
fileChooser.setTitle(Launcher.i18n("modpack.wizard.step.initialization.save"));
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(Launcher.i18n("modpack"), "*.zip"));
File file = fileChooser.showSaveDialog(Controllers.getStage());
if (file == null) {
Controllers.navigate(null);
@@ -100,7 +100,7 @@ public final class ModpackInfoPage extends StackPane implements WizardPage {
@Override
public String getTitle() {
return Main.i18n("modpack.wizard.step.1.title");
return Launcher.i18n("modpack.wizard.step.1.title");
}
public static final String MODPACK_NAME = "modpack.name";

View File

@@ -18,12 +18,13 @@
package org.jackhuang.hmcl.upgrade;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.Main;
import com.jfoenix.concurrency.JFXUtilities;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskExecutor;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.construct.MessageBox;
import org.jackhuang.hmcl.util.*;
@@ -55,12 +56,19 @@ public class AppDataUpgrader extends IUpgrader {
if (mainClass != null) {
ArrayList<String> al = new ArrayList<>(args);
al.add("--noupdate");
AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
new URLClassLoader(new URL[]{jar.toURI().toURL()},
ClassLoader.getSystemClassLoader().getParent()).loadClass(mainClass)
.getMethod("main", String[].class).invoke(null, new Object[]{al.toArray(new String[0])});
return null;
});
ClassLoader pre = Thread.currentThread().getContextClassLoader();
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
Logging.stop();
ClassLoader now = new URLClassLoader(new URL[]{jar.toURI().toURL()}, ClassLoader.getSystemClassLoader().getParent());
Thread.currentThread().setContextClassLoader(now);
now.loadClass(mainClass).getMethod("main", String[].class).invoke(null, new Object[]{al.toArray(new String[0])});
return null;
});
} finally {
Logging.start();
Thread.currentThread().setContextClassLoader(pre);
}
return true;
}
}
@@ -97,18 +105,18 @@ public class AppDataUpgrader extends IUpgrader {
if (!(ver instanceof IntVersionNumber))
return;
IntVersionNumber version = (IntVersionNumber) ver;
checker.requestDownloadLink().then(Task.of(Schedulers.javafx(), variables -> {
checker.requestDownloadLink().then(Task.of(variables -> {
Map<String, String> map = variables.get(UpdateChecker.REQUEST_DOWNLOAD_LINK_ID);
if (MessageBox.confirm(Main.i18n("update.newest_version") + version.toString() + "\n"
+ Main.i18n("update.should_open_link"),
if (MessageBox.confirm(Launcher.i18n("update.newest_version", version.toString()) + "\n"
+ Launcher.i18n("update.should_open_link"),
MessageBox.YES_NO_OPTION) == MessageBox.YES_OPTION)
if (map != null && map.containsKey("jar") && !StringUtils.isBlank(map.get("jar")))
try {
String hash = null;
if (map.containsKey("jarsha1"))
hash = map.get("jarsha1");
Controllers.dialog(Main.i18n("message.downloading"));
Controllers.dialog(Launcher.i18n("message.downloading"));
if (new AppDataUpgraderJarTask(NetworkUtils.toURL(map.get("jar")), version.toString(), hash).test()) {
new ProcessBuilder(JavaVersion.fromCurrentEnvironment().getBinary().getAbsolutePath(), "-jar", AppDataUpgraderJarTask.getSelf(version.toString()).getAbsolutePath())
.directory(new File("").getAbsoluteFile()).start();
@@ -123,31 +131,31 @@ public class AppDataUpgrader extends IUpgrader {
String hash = null;
if (map.containsKey("packsha1"))
hash = map.get("packsha1");
Controllers.dialog(Main.i18n("message.downloading"));
if (new AppDataUpgraderPackGzTask(NetworkUtils.toURL(map.get("pack")), version.toString(), hash).test()) {
Task task = new AppDataUpgraderPackGzTask(NetworkUtils.toURL(map.get("pack")), version.toString(), hash);
TaskExecutor executor = task.executor();
JFXUtilities.runInFX(() -> Controllers.taskDialog(executor, Launcher.i18n("message.downloading"), "", null));
if (executor.test()) {
new ProcessBuilder(JavaVersion.fromCurrentEnvironment().getBinary().getAbsolutePath(), "-jar", AppDataUpgraderPackGzTask.getSelf(version.toString()).getAbsolutePath())
.directory(new File("").getAbsoluteFile()).start();
System.exit(0);
}
Controllers.closeDialog();
JFXUtilities.runInFX(Controllers::closeDialog);
} catch (IOException ex) {
Logging.LOG.log(Level.SEVERE, "Failed to create upgrader", ex);
}
else {
String url = Main.PUBLISH;
String url = Launcher.PUBLISH;
if (map != null)
if (map.containsKey(OperatingSystem.CURRENT_OS.getCheckedName()))
url = map.get(OperatingSystem.CURRENT_OS.getCheckedName());
else if (map.containsKey(OperatingSystem.UNKNOWN.getCheckedName()))
url = map.get(OperatingSystem.UNKNOWN.getCheckedName());
if (url == null)
url = Main.PUBLISH;
try {
java.awt.Desktop.getDesktop().browse(new URI(url));
} catch (URISyntaxException | IOException e) {
Logging.LOG.log(Level.SEVERE, "Failed to browse uri: " + url, e);
OperatingSystem.setClipboard(url);
MessageBox.show(Main.i18n("update.no_browser"));
MessageBox.show(Launcher.i18n("update.no_browser"));
}
}
})).start();
@@ -155,7 +163,7 @@ public class AppDataUpgrader extends IUpgrader {
public static class AppDataUpgraderPackGzTask extends Task {
public static final File BASE_FOLDER = Main.HMCL_DIRECTORY;
public static final File BASE_FOLDER = Launcher.HMCL_DIRECTORY;
public static final File HMCL_VER_FILE = new File(BASE_FOLDER, "hmclver.json");
public static File getSelf(String ver) {
@@ -177,7 +185,7 @@ public class AppDataUpgrader extends IUpgrader {
@Override
public Collection<Task> getDependents() {
return Arrays.asList(new FileDownloadTask(downloadLink, tempFile, Proxy.NO_PROXY, hash));
return Collections.singleton(new FileDownloadTask(downloadLink, tempFile, Proxy.NO_PROXY, hash));
}
@Override
@@ -205,7 +213,7 @@ public class AppDataUpgrader extends IUpgrader {
public static class AppDataUpgraderJarTask extends Task {
public static final File BASE_FOLDER = Main.getWorkingDirectory("hmcl");
public static final File BASE_FOLDER = Launcher.getWorkingDirectory("hmcl");
public static final File HMCL_VER_FILE = new File(BASE_FOLDER, "hmclver.json");
public static File getSelf(String ver) {

View File

@@ -17,7 +17,7 @@
*/
package org.jackhuang.hmcl.upgrade;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.util.Charsets;
@@ -51,7 +51,7 @@ public class NewFileUpgrader extends IUpgrader {
URL url = requestDownloadLink();
if (url == null) return;
File newf = new File(url.getFile());
Controllers.dialog(Main.i18n("message.downloading"));
Controllers.dialog(Launcher.i18n("message.downloading"));
if (new FileDownloadTask(url, newf).test()) {
try {
new ProcessBuilder(newf.getCanonicalPath(), "--removeOldLauncher", getRealPath())

View File

@@ -18,7 +18,7 @@
package org.jackhuang.hmcl.upgrade;
import com.google.gson.JsonSyntaxException;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.event.Event;
import org.jackhuang.hmcl.event.EventBus;
import org.jackhuang.hmcl.event.OutOfDateEvent;
@@ -67,18 +67,18 @@ public final class UpdateChecker {
return new TaskResult<VersionNumber>() {
@Override
public void execute() throws Exception {
if (Main.VERSION.contains("@"))
if (Launcher.VERSION.contains("@"))
return;
if (value == null) {
versionString = NetworkUtils.doGet(NetworkUtils.toURL("https://huangyuhui.duapp.com/hmcl/update.php?version=" + Main.VERSION));
versionString = NetworkUtils.doGet(NetworkUtils.toURL("https://huangyuhui.duapp.com/hmcl/update.php?version=" + Launcher.VERSION));
value = VersionNumber.asVersion(versionString);
}
if (value == null) {
Logging.LOG.warning("Unable to check update...");
if (showMessage)
MessageBox.show(Main.i18n("update.failed"));
MessageBox.show(Launcher.i18n("update.failed"));
} else if (base.compareTo(value) < 0)
outOfDate = true;
if (outOfDate)
@@ -115,7 +115,7 @@ public final class UpdateChecker {
public void execute() {
if (download_link == null)
try {
download_link = Constants.GSON.<Map<String, String>>fromJson(NetworkUtils.doGet(NetworkUtils.toURL("https://huangyuhui.duapp.com/update_link.php?type=hmcl")), Map.class);
download_link = Constants.GSON.<Map<String, String>>fromJson(NetworkUtils.doGet(NetworkUtils.toURL("https://huangyuhui.duapp.com/hmcl/update_link.php")), Map.class);
} catch (JsonSyntaxException | IOException e) {
Logging.LOG.log(Level.SEVERE, "Failed to get update link.", e);
}
@@ -134,7 +134,7 @@ public final class UpdateChecker {
public void checkOutdate() {
if (outOfDate)
if (EventBus.EVENT_BUS.fireEvent(new OutOfDateEvent(this, getNewVersion())) != Event.Result.DENY) {
Main.UPGRADER.download(this, getNewVersion());
Launcher.UPGRADER.download(this, getNewVersion());
}
}
}

View File

@@ -18,7 +18,7 @@
package org.jackhuang.hmcl.util;
import javafx.application.Platform;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.ui.CrashWindow;
import org.jackhuang.hmcl.ui.construct.MessageBox;
@@ -36,15 +36,17 @@ public class CrashReporter implements Thread.UncaughtExceptionHandler {
private static final HashMap<String, String> SOURCE = new HashMap<String, String>() {
{
put("javafx.fxml.LoadException", Main.i18n("crash.NoClassDefFound"));
put("UnsatisfiedLinkError", Main.i18n("crash.user_fault"));
put("java.lang.NoClassDefFoundError", Main.i18n("crash.NoClassDefFound"));
put("java.lang.VerifyError", Main.i18n("crash.NoClassDefFound"));
put("java.lang.NoSuchMethodError", Main.i18n("crash.NoClassDefFound"));
put("java.lang.IncompatibleClassChangeError", Main.i18n("crash.NoClassDefFound"));
put("java.lang.ClassFormatError", Main.i18n("crash.NoClassDefFound"));
put("javafx.fxml.LoadException", Launcher.i18n("crash.NoClassDefFound"));
put("Location is not set", Launcher.i18n("crash.NoClassDefFound"));
put("UnsatisfiedLinkError", Launcher.i18n("crash.user_fault"));
put("java.lang.NoClassDefFoundError", Launcher.i18n("crash.NoClassDefFound"));
put("java.lang.VerifyError", Launcher.i18n("crash.NoClassDefFound"));
put("java.lang.NoSuchMethodError", Launcher.i18n("crash.NoClassDefFound"));
put("java.lang.IncompatibleClassChangeError", Launcher.i18n("crash.NoClassDefFound"));
put("java.lang.ClassFormatError", Launcher.i18n("crash.NoClassDefFound"));
put("java.lang.OutOfMemoryError", "FUCKING MEMORY LIMIT!");
put("Trampoline", Main.i18n("launcher.update_java"));
put("Trampoline", Launcher.i18n("launcher.update_java"));
put("com.sun.javafx.css.StyleManager.findMatchingStyles", Launcher.i18n("launcher.update_java"));
put("NoSuchAlgorithmException", "Has your operating system been installed completely or is a ghost system?");
}
};
@@ -78,24 +80,22 @@ public class CrashReporter implements Thread.UncaughtExceptionHandler {
THROWABLE_SET.add(stackTrace);
try {
StringBuilder builder = new StringBuilder();
builder.append("---- Hello Minecraft! Crash Report ----\n");
builder.append(" Version: " + Main.VERSION + "\n");
builder.append(" Time: ").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())).append("\n");
builder.append(" Thread: ").append(t.toString()).append("\n");
builder.append("\n Content: \n ");
builder.append(stackTrace).append("\n\n");
builder.append("-- System Details --\n");
builder.append(" Operating System: ").append(System.getProperty("os.name")).append(' ').append(OperatingSystem.SYSTEM_VERSION).append("\n");
builder.append(" Java Version: ").append(System.getProperty("java.version")).append(", ").append(System.getProperty("java.vendor")).append("\n");
builder.append(" Java VM Version: ").append(System.getProperty("java.vm.name")).append(" (").append(System.getProperty("java.vm.info")).append("), ").append(System.getProperty("java.vm.vendor")).append("\n");
String text = builder.toString();
String text = "---- Hello Minecraft! Crash Report ----\n" +
" Version: " + Launcher.VERSION + "\n" +
" Time: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "\n" +
" Thread: " + t.toString() + "\n" +
"\n Content: \n " +
stackTrace + "\n\n" +
"-- System Details --\n" +
" Operating System: " + System.getProperty("os.name") + ' ' + OperatingSystem.SYSTEM_VERSION + "\n" +
" Java Version: " + System.getProperty("java.version") + ", " + System.getProperty("java.vendor") + "\n" +
" Java VM Version: " + System.getProperty("java.vm.name") + " (" + System.getProperty("java.vm.info") + "), " + System.getProperty("java.vm.vendor") + "\n";
Logging.LOG.log(Level.SEVERE, text);
if (checkThrowable(e) && !text.contains("OpenJDK")) {
Platform.runLater(() -> new CrashWindow(text).show());
if (!Main.UPDATE_CHECKER.isOutOfDate())
if (!Launcher.UPDATE_CHECKER.isOutOfDate())
reportToServer(text);
}
} catch (Throwable ex) {
@@ -110,7 +110,8 @@ public class CrashReporter implements Thread.UncaughtExceptionHandler {
Thread t = new Thread(() -> {
HashMap<String, String> map = new HashMap<>();
map.put("crash_report", text);
map.put("version", Main.VERSION);
map.put("version", Launcher.VERSION);
map.put("log", Logging.getLogs());
try {
String response = NetworkUtils.doPost(NetworkUtils.toURL("https://huangyuhui.duapp.com/hmcl/crash.php"), map);
if (StringUtils.isNotBlank(response))

View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import com.jfoenix.controls.*?>
<?import com.jfoenix.validation.RequiredFieldValidator?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import org.jackhuang.hmcl.ui.FXUtils?>
<fx:root xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
type="StackPane">
<JFXDialogLayout fx:id="layout">
<heading>
<Label text="%account.create"/>
</heading>
<body>
<GridPane vgap="15" hgap="15" style="-fx-padding: 15 0 0 0;">
<columnConstraints>
<ColumnConstraints maxWidth="70" minWidth="70"/>
<ColumnConstraints/>
<ColumnConstraints minWidth="140"/>
</columnConstraints>
<Label text="%account.methods" GridPane.halignment="RIGHT" GridPane.columnIndex="0"
GridPane.rowIndex="0"/>
<JFXComboBox fx:id="cboType" GridPane.columnIndex="1" GridPane.rowIndex="0" GridPane.columnSpan="2"/>
<Label fx:id="lblAddInjectorServer" text="%account.injector.server" GridPane.halignment="RIGHT"
GridPane.columnIndex="0" GridPane.rowIndex="1"/>
<JFXComboBox fx:id="cboServers" maxHeight="25" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
<Hyperlink fx:id="linkAddInjectorServer" text="%account.injector.add"
onMouseClicked="#onAddInjecterServer" GridPane.columnIndex="2" GridPane.rowIndex="1"/>
<Label text="%account.username" GridPane.rowIndex="2" GridPane.columnIndex="0"/>
<JFXTextField fx:id="txtUsername" GridPane.columnIndex="1" GridPane.rowIndex="2" GridPane.columnSpan="2"
FXUtils.validateWhileTextChanged="true">
<validators>
<RequiredFieldValidator message="%input.not_empty">
</RequiredFieldValidator>
</validators>
</JFXTextField>
<Label fx:id="lblPassword" text="%account.password" GridPane.rowIndex="3" GridPane.columnIndex="0"/>
<JFXPasswordField fx:id="txtPassword" GridPane.columnIndex="1" GridPane.rowIndex="3"
GridPane.columnSpan="2" FXUtils.validateWhileTextChanged="true">
<validators>
<RequiredFieldValidator message="%input.not_empty">
</RequiredFieldValidator>
</validators>
</JFXPasswordField>
</GridPane>
</body>
<actions>
<Label fx:id="lblCreationWarning"/>
<JFXButton onMouseClicked="#onCreationAccept" text="%button.ok" styleClass="dialog-accept"/>
<JFXButton onMouseClicked="#onCreationCancel" text="%button.cancel" styleClass="dialog-cancel"/>
</actions>
</JFXDialogLayout>
<JFXProgressBar fx:id="progressBar" visible="false" StackPane.alignment="TOP_CENTER"/>
</fx:root>

View File

@@ -1,51 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import com.jfoenix.controls.JFXButton?>
<?import com.jfoenix.controls.JFXProgressBar?>
<?import com.jfoenix.controls.JFXRadioButton?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.*?>
<?import org.jackhuang.hmcl.ui.FXUtils?>
<fx:root xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
type="StackPane" FXUtils.limitWidth="160" FXUtils.limitHeight="156">
<VBox fx:id="content">
<StackPane fx:id="header" VBox.vgrow="ALWAYS" style="-fx-background-radius: 2 2 0 0; -fx-background-color: rgb(255,255,255,0.87); -fx-padding: 8;">
<HBox StackPane.alignment="TOP_CENTER" alignment="CENTER_RIGHT">
<JFXProgressBar fx:id="pgsSkin" visible="false" />
</HBox>
<VBox style="-fx-padding: 8 8 0 8;">
<Label fx:id="lblUser" style="-fx-font-size: 15;" wrapText="true" textAlignment="JUSTIFY" />
<Label fx:id="lblEmail" style="-fx-font-size: 11; -fx-text-fill: gray;" />
<Label fx:id="lblServer" style="-fx-font-size: 11; -fx-text-fill: gray;" />
<Label fx:id="lblType" style="-fx-font-size: 11; -fx-text-fill: gray;" />
</VBox>
</StackPane>
<StackPane fx:id="body" style="-fx-background-radius: 0 0 2 2; -fx-background-color: rgb(255,255,255,0.87); -fx-padding: 8;" minHeight="40">
<BorderPane>
<left>
<HBox fx:id="buttonPane" spacing="8">
<JFXButton fx:id="btnRefresh" styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minWidth="30" minHeight="30" prefWidth="30" prefHeight="30" />
<JFXButton fx:id="btnDelete" styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minWidth="30" minHeight="30" prefWidth="30" prefHeight="30" />
</HBox>
</left>
<right>
<HBox alignment="CENTER_RIGHT">
<Label fx:id="lblCurrentAccount" text="%account.current" />
<JFXRadioButton fx:id="chkSelected" BorderPane.alignment="CENTER_RIGHT" />
</HBox>
</right>
</BorderPane>
</StackPane>
</VBox>
<StackPane fx:id="icon" StackPane.alignment="TOP_RIGHT" pickOnBounds="false">
<ImageView fx:id="portraitView" StackPane.alignment="CENTER_RIGHT" smooth="false">
<StackPane.margin>
<Insets right="12" />
</StackPane.margin>
</ImageView>
</StackPane>
</fx:root>

View File

@@ -1,75 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import com.jfoenix.controls.*?>
<?import com.jfoenix.validation.RequiredFieldValidator?>
<?import com.jfoenix.controls.JFXButton?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import org.jackhuang.hmcl.ui.FXUtils?>
<fx:root xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
<?import org.jackhuang.hmcl.ui.construct.ComponentList?>
<?import com.jfoenix.controls.JFXProgressBar?>
<fx:root xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
type="StackPane">
<ScrollPane fitToHeight="true" fitToWidth="true" fx:id="scrollPane" hbarPolicy="NEVER">
<JFXMasonryPane fx:id="masonryPane" HSpacing="3" VSpacing="3" cellWidth="182" cellHeight="160">
</JFXMasonryPane>
</ScrollPane>
<AnchorPane pickOnBounds="false">
<JFXButton onMouseClicked="#addNewAccount" AnchorPane.bottomAnchor="16" AnchorPane.rightAnchor="16" buttonType="RAISED" prefWidth="40" prefHeight="40" styleClass="jfx-button-raised-round">
<graphic>
<fx:include source="/assets/svg/plus.fxml" />
</graphic>
</JFXButton>
</AnchorPane>
<VBox fx:id="rootPane">
<JFXDialog fx:id="dialog">
<StackPane>
<JFXDialogLayout>
<heading>
<Label text="%account.create" />
</heading>
<body>
<GridPane vgap="15" hgap="15" style="-fx-padding: 15 0 0 0;">
<columnConstraints>
<ColumnConstraints maxWidth="70" minWidth="70"/>
<ColumnConstraints />
<ColumnConstraints minWidth="140" />
</columnConstraints>
<ComponentList fx:id="componentList" depth="1">
<Label text="%account.methods" GridPane.halignment="RIGHT" GridPane.columnIndex="0" GridPane.rowIndex="0" />
<BorderPane> <!-- Name -->
<left>
<VBox>
<Label text="%account.methods" BorderPane.alignment="CENTER_LEFT"/>
</VBox>
</left>
<right>
<VBox>
<Label fx:id="lblType" BorderPane.alignment="CENTER_LEFT"/>
</VBox>
</right>
</BorderPane>
<JFXComboBox fx:id="cboType" GridPane.columnIndex="1" GridPane.rowIndex="0" GridPane.columnSpan="2" />
<BorderPane> <!-- Name -->
<left>
<VBox>
<Label text="%account.character" BorderPane.alignment="CENTER_LEFT"/>
</VBox>
</left>
<right>
<VBox>
<Label fx:id="lblCharacter" BorderPane.alignment="CENTER_LEFT"/>
</VBox>
</right>
</BorderPane>
<Label fx:id="lblAddInjectorServer" text="%account.injector.server" GridPane.halignment="RIGHT" GridPane.columnIndex="0" GridPane.rowIndex="1" />
<BorderPane fx:id="paneEmail"> <!-- Name -->
<left>
<VBox>
<Label text="%account.email" BorderPane.alignment="CENTER_LEFT"/>
</VBox>
</left>
<right>
<VBox>
<Label fx:id="lblEmail" BorderPane.alignment="CENTER_LEFT"/>
</VBox>
</right>
</BorderPane>
<JFXComboBox fx:id="cboServers" maxHeight="25" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<BorderPane fx:id="paneServer"> <!-- Name -->
<left>
<VBox>
<Label text="%account.injector.server" BorderPane.alignment="CENTER_LEFT"/>
</VBox>
</left>
<right>
<VBox>
<Label fx:id="lblServer" BorderPane.alignment="CENTER_LEFT"/>
</VBox>
</right>
</BorderPane>
</ComponentList>
<Hyperlink fx:id="linkAddInjectorServer" text="%account.injector.add" onMouseClicked="#onAddInjecterServer" GridPane.columnIndex="2" GridPane.rowIndex="1" />
<Label text="%account.username" GridPane.rowIndex="2" GridPane.columnIndex="0" />
<JFXTextField fx:id="txtUsername" GridPane.columnIndex="1" GridPane.rowIndex="2" GridPane.columnSpan="2" FXUtils.validateWhileTextChanged="true">
<validators>
<RequiredFieldValidator message="%input.not_empty">
</RequiredFieldValidator>
</validators>
</JFXTextField>
<Label fx:id="lblPassword" text="%account.password" GridPane.rowIndex="3" GridPane.columnIndex="0" />
<JFXPasswordField fx:id="txtPassword" GridPane.columnIndex="1" GridPane.rowIndex="3" GridPane.columnSpan="2" FXUtils.validateWhileTextChanged="true">
<validators>
<RequiredFieldValidator message="%input.not_empty">
</RequiredFieldValidator>
</validators>
</JFXPasswordField>
</GridPane>
</body>
<actions>
<Label fx:id="lblCreationWarning" />
<JFXButton onMouseClicked="#onCreationAccept" text="%button.ok" styleClass="dialog-accept" />
<JFXButton onMouseClicked="#onCreationCancel" text="%button.cancel" styleClass="dialog-cancel" />
</actions>
</JFXDialogLayout>
<JFXProgressBar fx:id="progressBar" visible="false" StackPane.alignment="TOP_CENTER" />
</StackPane>
</JFXDialog>
</VBox>
<BorderPane pickOnBounds="false" style="-fx-padding: 4;">
<left>
<JFXButton BorderPane.alignment="BOTTOM_LEFT" fx:id="btnDelete" onMouseClicked="#onDelete"
styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minWidth="30" minHeight="30"
prefWidth="30" prefHeight="30"/>
</left>
<right>
<JFXButton BorderPane.alignment="BOTTOM_RIGHT" fx:id="btnRefresh" onMouseClicked="#onRefresh"
styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minWidth="30" minHeight="30"
prefWidth="30" prefHeight="30"/>
</right>
</BorderPane>
<JFXProgressBar fx:id="progressBar" StackPane.alignment="TOP_CENTER" visible="false" />
</fx:root>

View File

@@ -32,9 +32,11 @@
<Label text="%account.injector.add" />
</heading>
<body>
<JFXTextField fx:id="txtServerIp" promptText="%account.injector.server_ip" labelFloat="true">
<JFXTextField fx:id="txtServerIp" promptText="%account.injector.server_ip">
<validators>
<URLValidator message="%input.url">
</URLValidator>
<URLValidator message="%input.url.http">
<protocols>
<String fx:value="http" />
<String fx:value="https" />

View File

@@ -20,26 +20,14 @@
maxWidth="800">
<center>
<StackPane fx:id="drawerWrapper" styleClass="jfx-decorator-drawer" FXUtils.overflowHidden="true">
<JFXDialog fx:id="dialog" overlayClose="false" />
<BorderPane>
<left>
<StackPane minWidth="200" maxWidth="200" styleClass="jfx-decorator-content-container">
<BorderPane fx:id="leftRootPane">
<center>
<BorderPane styleClass="gray-background">
<center>
<AdvancedListBox fx:id="leftPane"/>
</center>
<bottom>
<BorderPane fx:id="menuBottomBar">
<left>
<HBox fx:id="updatePane" visible="false" style="-fx-background-color: red;" alignment="CENTER_LEFT">
<Label text="%update.found" style="-fx-text-fill: white; -fx-font-size: 16px;" />
</HBox>
</left>
</BorderPane>
</bottom>
</BorderPane>
<StackPane styleClass="gray-background">
<AdvancedListBox fx:id="leftPane"/>
</StackPane>
</center>
<right>
<Rectangle height="${leftRootPane.height}" width="1" fill="gray"/>

View File

@@ -1,38 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import com.jfoenix.controls.JFXButton?>
<?import com.jfoenix.controls.JFXListView?>
<?import com.jfoenix.controls.JFXMasonryPane?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import com.jfoenix.controls.JFXSpinner?>
<fx:root
maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
type="StackPane" pickOnBounds="false"
xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1">
<ScrollPane fitToHeight="true" fitToWidth="true" fx:id="scrollPane" hbarPolicy="NEVER">
<JFXMasonryPane fx:id="masonryPane" HSpacing="3" VSpacing="3" cellWidth="182" cellHeight="160">
</JFXMasonryPane>
</ScrollPane>
<VBox style="-fx-padding: 15;" spacing="15" pickOnBounds="false" alignment="BOTTOM_RIGHT">
<JFXButton prefWidth="40" prefHeight="40" buttonType="RAISED" fx:id="btnRefresh" styleClass="jfx-button-raised-round">
<graphic>
<fx:include source="/assets/svg/refresh.fxml" />
</graphic>
</JFXButton>
<JFXButton prefWidth="40" prefHeight="40" buttonType="RAISED" fx:id="btnAdd" styleClass="jfx-button-raised-round">
<graphic>
<fx:include source="/assets/svg/plus.fxml" />
</graphic>
</JFXButton>
</VBox>
<JFXListView fx:id="versionList" styleClass="option-list-view" onMouseClicked="#onVersionManagement"
maxWidth="150.0" minWidth="150.0">
<Label text="%version.manage.rename"/>
<Label text="%version.manage.remove"/>
<Label text="%modpack.export"/>
<Label text="%folder.game"/>
</JFXListView>
<JFXSpinner fx:id="spinner" styleClass="first-spinner" />
<StackPane fx:id="contentPane">
<ScrollPane fitToHeight="true" fitToWidth="true" fx:id="scrollPane" hbarPolicy="NEVER">
<JFXMasonryPane fx:id="masonryPane" HSpacing="3" VSpacing="3" cellWidth="182" cellHeight="160">
</JFXMasonryPane>
</ScrollPane>
<VBox style="-fx-padding: 15;" spacing="15" pickOnBounds="false" alignment="BOTTOM_RIGHT">
<JFXButton prefWidth="40" prefHeight="40" buttonType="RAISED" fx:id="btnRefresh" styleClass="jfx-button-raised-round">
<graphic>
<fx:include source="/assets/svg/refresh.fxml" />
</graphic>
</JFXButton>
<JFXButton prefWidth="40" prefHeight="40" buttonType="RAISED" fx:id="btnAdd" styleClass="jfx-button-raised-round">
<graphic>
<fx:include source="/assets/svg/plus.fxml" />
</graphic>
</JFXButton>
</VBox>
</StackPane>
</fx:root>

View File

@@ -31,7 +31,7 @@
</left>
<right>
<HBox alignment="CENTER" pickOnBounds="false">
<JFXButton fx:id="btnSettings" maxWidth="40" minWidth="40" prefWidth="40" styleClass="toggle-icon4" onMouseClicked="#onSettings">
<JFXButton fx:id="btnSettings" maxWidth="40" minWidth="40" prefWidth="40" styleClass="toggle-icon4">
<graphic>
<fx:include source="/assets/svg/gear.fxml" />
</graphic>

View File

@@ -5,7 +5,6 @@
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.*?>
<?import org.jackhuang.hmcl.ui.construct.*?>
<?import org.jackhuang.hmcl.ui.*?>

View File

@@ -29,6 +29,7 @@ about.open_source=Open Source
about.open_source.statement=GPL v3 (https://github.com/huanghongxun/HMCL/)
account=Accounts
account.character=character
account.choose=Choose a character
account.current=Current
account.create=Create a new account
@@ -136,6 +137,7 @@ input.email=The username must be an e-mail.
input.number=Must be a number.
input.not_empty=Input Requrired!
input.url=Must be a valid URL.
input.url.http=Only Http or Https accepted.
install=Install New Game
install.failed=Failed to install

View File

@@ -29,6 +29,7 @@ about.open_source=开源
about.open_source.statement=GPL v3 (https://github.com/huanghongxun/HMCL/)
account=账户
account.character=角色
account.choose=选择一个角色
account.current=当前账户
account.create=新建账户
@@ -136,6 +137,7 @@ input.email=用户名必须是邮箱
input.number=必须是数字
input.not_empty=必填项
input.url=必须是合法的链接
input.url.http=只支持Http或Https协议
install=添加游戏
install.failed=安装失败

View File

@@ -17,6 +17,8 @@
*/
package org.jackhuang.hmcl.auth;
import org.jackhuang.hmcl.util.ToStringBuilder;
import java.util.Map;
import java.util.UUID;
@@ -64,4 +66,13 @@ public abstract class Account {
public abstract Map<Object, Object> toStorage();
public abstract void clearCache();
@Override
public String toString() {
return new ToStringBuilder(this)
.append("username", getUsername())
.append("character", getCharacter())
.append("uuid", getUUID())
.toString();
}
}

View File

@@ -17,7 +17,6 @@
*/
package org.jackhuang.hmcl.auth.yggdrasil;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.jackhuang.hmcl.auth.*;
import org.jackhuang.hmcl.util.StringUtils;

View File

@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.download.game.*;
import org.jackhuang.hmcl.game.SimpleVersionProvider;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.ParallelTask;
import org.jackhuang.hmcl.task.Task;

View File

@@ -17,7 +17,6 @@
*/
package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.game.GameRepository;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.TaskResult;

View File

@@ -28,7 +28,7 @@ import org.jackhuang.hmcl.download.optifine.OptiFineVersionList;
*/
public class MojangDownloadProvider implements DownloadProvider {
private boolean isChina;
private final boolean isChina;
public MojangDownloadProvider(boolean isChina) {
this.isChina = isChina;

View File

@@ -47,7 +47,7 @@ public final class ForgeInstallTask extends TaskResult<Version> {
private final DefaultDependencyManager dependencyManager;
private final Version version;
private final File installer = new File("forge-installer.jar").getAbsoluteFile();
private ForgeRemoteVersion remote;
private final ForgeRemoteVersion remote;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();

View File

@@ -82,7 +82,7 @@ public final class GameAssetDownloadTask extends Task {
AssetObject assetObject = entry.getValue();
String url = dependencyManager.getDownloadProvider().getAssetBaseURL() + assetObject.getLocation();
if (!FileUtils.makeDirectory(file.getAbsoluteFile().getParentFile())) {
Logging.LOG.log(Level.SEVERE, "Unable to create new file {0}, because parent directory cannot be created", file);
Logging.LOG.log(Level.SEVERE, "Unable to create new file " + file + ", because parent directory cannot be created");
continue;
}
if (file.isDirectory())

View File

@@ -20,9 +20,7 @@ package org.jackhuang.hmcl.download.game;
import org.jackhuang.hmcl.download.AbstractDependencyManager;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.NetworkUtils;
import java.io.File;
import java.util.LinkedList;

View File

@@ -4,8 +4,6 @@ import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
import org.jackhuang.hmcl.download.AbstractDependencyManager;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Scheduler;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.*;

View File

@@ -41,7 +41,7 @@ public final class LiteLoaderInstallTask extends TaskResult<Version> {
private final DefaultDependencyManager dependencyManager;
private final Version version;
private LiteLoaderRemoteVersion remote;
private final LiteLoaderRemoteVersion remote;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();

View File

@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.download.liteloader;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.task.GetTask;
import org.jackhuang.hmcl.task.Task;

View File

@@ -38,7 +38,7 @@ public final class OptiFineInstallTask extends TaskResult<Version> {
private final DefaultDependencyManager dependencyManager;
private final Version version;
private OptiFineRemoteVersion remote;
private final OptiFineRemoteVersion remote;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();

View File

@@ -17,6 +17,8 @@
*/
package org.jackhuang.hmcl.event;
import org.jackhuang.hmcl.util.ToStringBuilder;
import java.util.Objects;
/**
@@ -28,13 +30,13 @@ public class Event {
/**
* The object on which the Event initially occurred.
*/
protected transient Object source;
protected final transient Object source;
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @throws NullPointerException if source is null.
* @throws NullPointerException if source is null.
*/
public Event(Object source) {
Objects.requireNonNull(source);
@@ -54,10 +56,10 @@ public class Event {
/**
* Returns a String representation of this Event.
*
* @return A a String representation of this Event.
* @return A a String representation of this Event.
*/
public String toString() {
return getClass().getName() + "[source=" + source + "]";
return new ToStringBuilder(this).append("source", source).toString();
}
private boolean canceled;
@@ -72,7 +74,6 @@ public class Event {
}
/**
*
* @param canceled new value
* @throws UnsupportedOperationException if trying to cancel a non-cancelable event.
*/

View File

@@ -17,6 +17,8 @@
*/
package org.jackhuang.hmcl.event;
import org.jackhuang.hmcl.util.Logging;
import java.util.HashMap;
/**
@@ -36,6 +38,8 @@ public final class EventBus {
@SuppressWarnings("unchecked")
public Event.Result fireEvent(Event obj) {
Logging.LOG.info(obj + " gets fired");
return channel((Class<Event>) obj.getClass()).fireEvent(obj);
}

View File

@@ -17,6 +17,8 @@
*/
package org.jackhuang.hmcl.event;
import org.jackhuang.hmcl.util.ToStringBuilder;
import java.io.File;
/**
@@ -49,4 +51,13 @@ public final class GameJsonParseFailedEvent extends Event {
public String getVersion() {
return version;
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("source", source)
.append("jsonFile", jsonFile)
.append("version", version)
.toString();
}
}

View File

@@ -18,6 +18,7 @@
package org.jackhuang.hmcl.event;
import org.jackhuang.hmcl.util.ManagedProcess;
import org.jackhuang.hmcl.util.ToStringBuilder;
/**
* This event gets fired when we launch the JVM and it got crashed.
@@ -44,4 +45,12 @@ public class JVMLaunchFailedEvent extends Event {
public ManagedProcess getProcess() {
return process;
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("source", source)
.append("process", process)
.toString();
}
}

View File

@@ -18,6 +18,7 @@
package org.jackhuang.hmcl.event;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.util.ToStringBuilder;
/**
* This event gets fired when a minecraft version has been loaded.
@@ -48,4 +49,12 @@ public final class LoadedOneVersionEvent extends Event {
public boolean hasResult() {
return true;
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("source", source)
.append("version", version)
.toString();
}
}

Some files were not shown because too many files have changed in this diff Show More