Prepare for MCBBS modpack
This commit is contained in:
@@ -15,30 +15,33 @@
|
||||
* 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
|
||||
package org.jackhuang.hmcl.event;
|
||||
|
||||
import org.jackhuang.hmcl.event.Event
|
||||
import org.jackhuang.hmcl.setting.Profile
|
||||
import java.util.*
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
|
||||
/**
|
||||
* This event gets fired when the selected profile changed.
|
||||
* <br></br>
|
||||
* This event is fired on the [org.jackhuang.hmcl.event.EVENT_BUS]
|
||||
* @param source [org.jackhuang.hmcl.setting.Settings]
|
||||
* *
|
||||
* @param Profile the new profile.
|
||||
* *
|
||||
* <br>
|
||||
* This event is fired on the {@link org.jackhuang.hmcl.event.EventBus#EVENT_BUS}
|
||||
* @author huangyuhui
|
||||
*/
|
||||
class ProfileChangedEvent(source: Any, val value: Profile) : Event(source)
|
||||
public final class ProfileChangedEvent extends Event {
|
||||
private final Profile profile;
|
||||
|
||||
/**
|
||||
* This event gets fired when loading profiles.
|
||||
* <br></br>
|
||||
* This event is fired on the [org.jackhuang.hmcl.event.EVENT_BUS]
|
||||
* @param source [org.jackhuang.hmcl.setting.Settings]
|
||||
* *
|
||||
* @author huangyuhui
|
||||
*/
|
||||
class ProfileLoadingEvent(source: Any) : Event(source)
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param source {@link org.jackhuang.hmcl.setting.Settings}
|
||||
* @param profile the new profile.
|
||||
*/
|
||||
public ProfileChangedEvent(Object source, Profile profile) {
|
||||
super(source);
|
||||
|
||||
this.profile = profile;
|
||||
}
|
||||
|
||||
public Profile getProfile() {
|
||||
return profile;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,18 +15,25 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui
|
||||
package org.jackhuang.hmcl.event;
|
||||
|
||||
import javafx.scene.Scene
|
||||
import javafx.scene.image.Image
|
||||
import javafx.scene.web.WebView
|
||||
import javafx.stage.Stage
|
||||
/**
|
||||
* This event gets fired when loading profiles.
|
||||
* <br>
|
||||
* This event is fired on the {@link org.jackhuang.hmcl.event.EventBus#EVENT_BUS}
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public class ProfileLoadingEvent extends Event {
|
||||
|
||||
class WebStage: Stage() {
|
||||
val webView = WebView()
|
||||
init {
|
||||
scene = Scene(webView, 800.0, 480.0)
|
||||
scene.stylesheets.addAll(*stylesheets)
|
||||
icons += Image("/assets/img/icon.png")
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param source {@link org.jackhuang.hmcl.setting.Settings}
|
||||
*/
|
||||
public ProfileLoadingEvent(Object source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ public final class LauncherHelper {
|
||||
public void onFinished(Task task) {
|
||||
finished.incrementAndGet();
|
||||
Platform.runLater(() -> {
|
||||
launchingStepsPane.getPgsTasks().setProgress(1.0 * finished.get() / executor.getRunningTasks());
|
||||
launchingStepsPane.setProgress(1.0 * finished.get() / executor.getRunningTasks());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -124,8 +124,8 @@ public final class LauncherHelper {
|
||||
if (state == LoadingState.DONE)
|
||||
Controllers.INSTANCE.closeDialog();
|
||||
|
||||
launchingStepsPane.getLblCurrentState().setText(state.toString());
|
||||
launchingStepsPane.getLblSteps().setText((state.ordinal() + 1) + " / " + LoadingState.values().length);
|
||||
launchingStepsPane.setCurrentState(state.toString());
|
||||
launchingStepsPane.setSteps((state.ordinal() + 1) + " / " + LoadingState.values().length);
|
||||
}
|
||||
|
||||
private void checkExit(LauncherVisibility v) {
|
||||
|
||||
244
HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java
Normal file
244
HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java
Normal file
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* 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.setting;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.util.JavaVersion;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public final class Config {
|
||||
|
||||
@SerializedName("last")
|
||||
private String selectedProfile = "";
|
||||
|
||||
@SerializedName("bgpath")
|
||||
private String backgroundImage = null;
|
||||
|
||||
@SerializedName("commonpath")
|
||||
private String commonDirectory = Main.getMinecraftDirectory().getAbsolutePath();
|
||||
|
||||
@SerializedName("proxyType")
|
||||
private int proxyType = 0;
|
||||
|
||||
@SerializedName("proxyHost")
|
||||
private String proxyHost = null;
|
||||
|
||||
@SerializedName("proxyPort")
|
||||
private String proxyPort = null;
|
||||
|
||||
@SerializedName("proxyUserName")
|
||||
private String proxyUser = null;
|
||||
|
||||
@SerializedName("proxyPassword")
|
||||
private String proxyPass = null;
|
||||
|
||||
@SerializedName("theme")
|
||||
private String theme = null;
|
||||
|
||||
@SerializedName("java")
|
||||
private List<JavaVersion> java = null;
|
||||
|
||||
@SerializedName("localization")
|
||||
private String localization;
|
||||
|
||||
@SerializedName("downloadtype")
|
||||
private int downloadType = 0;
|
||||
|
||||
@SerializedName("configurations")
|
||||
private Map<String, Profile> configurations = new TreeMap<>();
|
||||
|
||||
@SerializedName("accounts")
|
||||
private Map<String, Map<Object, Object>> accounts = new TreeMap<>();
|
||||
|
||||
@SerializedName("selectedAccount")
|
||||
private String selectedAccount = "";
|
||||
|
||||
@SerializedName("fontFamily")
|
||||
private String fontFamily = "Consolas";
|
||||
|
||||
@SerializedName("fontSize")
|
||||
private double fontSize = 12;
|
||||
|
||||
@SerializedName("logLines")
|
||||
private int logLines = 100;
|
||||
|
||||
public String getSelectedProfile() {
|
||||
return selectedProfile;
|
||||
}
|
||||
|
||||
public void setSelectedProfile(String selectedProfile) {
|
||||
this.selectedProfile = selectedProfile;
|
||||
Settings.INSTANCE.save();
|
||||
}
|
||||
|
||||
public String getBackgroundImage() {
|
||||
return backgroundImage;
|
||||
}
|
||||
|
||||
public void setBackgroundImage(String backgroundImage) {
|
||||
this.backgroundImage = backgroundImage;
|
||||
Settings.INSTANCE.save();
|
||||
}
|
||||
|
||||
public String getCommonDirectory() {
|
||||
return commonDirectory;
|
||||
}
|
||||
|
||||
public void setCommonDirectory(String commonDirectory) {
|
||||
this.commonDirectory = commonDirectory;
|
||||
Settings.INSTANCE.save();
|
||||
}
|
||||
|
||||
public int getProxyType() {
|
||||
return proxyType;
|
||||
}
|
||||
|
||||
public void setProxyType(int proxyType) {
|
||||
this.proxyType = proxyType;
|
||||
Settings.INSTANCE.save();
|
||||
}
|
||||
|
||||
public String getProxyHost() {
|
||||
return proxyHost;
|
||||
}
|
||||
|
||||
public void setProxyHost(String proxyHost) {
|
||||
this.proxyHost = proxyHost;
|
||||
Settings.INSTANCE.save();
|
||||
}
|
||||
|
||||
public String getProxyPort() {
|
||||
return proxyPort;
|
||||
}
|
||||
|
||||
public void setProxyPort(String proxyPort) {
|
||||
this.proxyPort = proxyPort;
|
||||
Settings.INSTANCE.save();
|
||||
}
|
||||
|
||||
public String getProxyUser() {
|
||||
return proxyUser;
|
||||
}
|
||||
|
||||
public void setProxyUser(String proxyUser) {
|
||||
this.proxyUser = proxyUser;
|
||||
Settings.INSTANCE.save();
|
||||
}
|
||||
|
||||
public String getProxyPass() {
|
||||
return proxyPass;
|
||||
}
|
||||
|
||||
public void setProxyPass(String proxyPass) {
|
||||
this.proxyPass = proxyPass;
|
||||
Settings.INSTANCE.save();
|
||||
}
|
||||
|
||||
public String getTheme() {
|
||||
return theme;
|
||||
}
|
||||
|
||||
public void setTheme(String theme) {
|
||||
this.theme = theme;
|
||||
Settings.INSTANCE.save();
|
||||
}
|
||||
|
||||
public List<JavaVersion> getJava() {
|
||||
return java;
|
||||
}
|
||||
|
||||
public void setJava(List<JavaVersion> java) {
|
||||
this.java = java;
|
||||
Settings.INSTANCE.save();
|
||||
}
|
||||
|
||||
public String getLocalization() {
|
||||
return localization;
|
||||
}
|
||||
|
||||
public void setLocalization(String localization) {
|
||||
this.localization = localization;
|
||||
Settings.INSTANCE.save();
|
||||
}
|
||||
|
||||
public int getDownloadType() {
|
||||
return downloadType;
|
||||
}
|
||||
|
||||
public void setDownloadType(int downloadType) {
|
||||
this.downloadType = downloadType;
|
||||
Settings.INSTANCE.save();
|
||||
}
|
||||
|
||||
public Map<String, Profile> getConfigurations() {
|
||||
return configurations;
|
||||
}
|
||||
|
||||
public void setConfigurations(Map<String, Profile> configurations) {
|
||||
this.configurations = configurations;
|
||||
Settings.INSTANCE.save();
|
||||
}
|
||||
|
||||
public Map<String, Map<Object, Object>> getAccounts() {
|
||||
return accounts;
|
||||
}
|
||||
|
||||
public void setAccounts(Map<String, Map<Object, Object>> accounts) {
|
||||
this.accounts = accounts;
|
||||
Settings.INSTANCE.save();
|
||||
}
|
||||
|
||||
public String getSelectedAccount() {
|
||||
return selectedAccount;
|
||||
}
|
||||
|
||||
public void setSelectedAccount(String selectedAccount) {
|
||||
this.selectedAccount = selectedAccount;
|
||||
Settings.INSTANCE.save();
|
||||
}
|
||||
|
||||
public String getFontFamily() {
|
||||
return fontFamily;
|
||||
}
|
||||
|
||||
public void setFontFamily(String fontFamily) {
|
||||
this.fontFamily = fontFamily;
|
||||
Settings.INSTANCE.save();
|
||||
}
|
||||
|
||||
public double getFontSize() {
|
||||
return fontSize;
|
||||
}
|
||||
|
||||
public void setFontSize(double fontSize) {
|
||||
this.fontSize = fontSize;
|
||||
Settings.INSTANCE.save();
|
||||
}
|
||||
|
||||
public int getLogLines() {
|
||||
return logLines;
|
||||
}
|
||||
|
||||
public void setLogLines(int logLines) {
|
||||
this.logLines = logLines;
|
||||
}
|
||||
}
|
||||
443
HMCL/src/main/java/org/jackhuang/hmcl/setting/Settings.java
Normal file
443
HMCL/src/main/java/org/jackhuang/hmcl/setting/Settings.java
Normal file
@@ -0,0 +1,443 @@
|
||||
/*
|
||||
* 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.setting;
|
||||
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.scene.text.Font;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.auth.Account;
|
||||
import org.jackhuang.hmcl.auth.AccountFactory;
|
||||
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider;
|
||||
import org.jackhuang.hmcl.download.DownloadProvider;
|
||||
import org.jackhuang.hmcl.download.MojangDownloadProvider;
|
||||
import org.jackhuang.hmcl.event.EventBus;
|
||||
import org.jackhuang.hmcl.event.ProfileChangedEvent;
|
||||
import org.jackhuang.hmcl.event.ProfileLoadingEvent;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.util.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.Authenticator;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.PasswordAuthentication;
|
||||
import java.net.Proxy;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Settings {
|
||||
public static final Gson GSON = new GsonBuilder()
|
||||
.registerTypeAdapter(VersionSetting.class, VersionSetting.Serializer.INSTANCE)
|
||||
.registerTypeAdapter(Profile.class, Profile.Serializer.INSTANCE)
|
||||
.registerTypeAdapter(File.class, FileTypeAdapter.INSTANCE)
|
||||
.setPrettyPrinting().create();
|
||||
|
||||
public static final String DEFAULT_PROFILE = "Default";
|
||||
public static final String HOME_PROFILE = "Home";
|
||||
|
||||
public static final File SETTINGS_FILE = new File("hmcl.json").getAbsoluteFile();
|
||||
|
||||
public static final Settings INSTANCE = new Settings();
|
||||
|
||||
private Settings() {}
|
||||
|
||||
private final Config SETTINGS = initSettings();
|
||||
|
||||
private Map<String, Account> accounts = new HashMap<>();
|
||||
|
||||
{
|
||||
for (Map.Entry<String, Map<Object, Object>> entry : SETTINGS.getAccounts().entrySet()) {
|
||||
String name = entry.getKey();
|
||||
Map<Object, Object> settings = entry.getValue();
|
||||
AccountFactory factory = Accounts.ACCOUNT_FACTORY.get(Lang.get(settings, "type", String.class, ""));
|
||||
if (factory == null) {
|
||||
SETTINGS.getAccounts().remove(name);
|
||||
continue;
|
||||
}
|
||||
|
||||
Account account;
|
||||
try {
|
||||
account = factory.fromStorage(settings);
|
||||
} catch (Exception e) {
|
||||
SETTINGS.getAccounts().remove(name);
|
||||
// storage is malformed, delete.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Objects.equals(account.getUsername(), name)) {
|
||||
SETTINGS.getAccounts().remove(name);
|
||||
continue;
|
||||
}
|
||||
|
||||
accounts.put(name, account);
|
||||
}
|
||||
|
||||
save();
|
||||
|
||||
if (!getProfileMap().containsKey(DEFAULT_PROFILE))
|
||||
getProfileMap().put(DEFAULT_PROFILE, new Profile());
|
||||
|
||||
for (Map.Entry<String, Profile> entry2 : getProfileMap().entrySet()) {
|
||||
entry2.getValue().setName(entry2.getKey());
|
||||
entry2.getValue().addPropertyChangedListener(e -> {
|
||||
save();
|
||||
});
|
||||
}
|
||||
|
||||
Lang.ignoringException(() -> {
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(this::save));
|
||||
});
|
||||
|
||||
loadProxy();
|
||||
}
|
||||
|
||||
private Config initSettings() {
|
||||
Config c = new Config();
|
||||
if (SETTINGS_FILE.exists())
|
||||
try {
|
||||
String str = FileUtils.readText(SETTINGS_FILE);
|
||||
if (StringUtils.isBlank(str))
|
||||
Logging.LOG.finer("Settings file is empty, use the default settings.");
|
||||
else {
|
||||
Config d = GSON.fromJson(str, Config.class);
|
||||
if (d != null)
|
||||
c = d;
|
||||
}
|
||||
Logging.LOG.finest("Initialized settings.");
|
||||
} catch (Exception e) {
|
||||
Logging.LOG.log(Level.WARNING, "Something happened wrongly when load settings.", e);
|
||||
}
|
||||
else {
|
||||
Logging.LOG.config("No settings file here, may be first loading.");
|
||||
if (!c.getConfigurations().containsKey(HOME_PROFILE))
|
||||
c.getConfigurations().put(HOME_PROFILE, new Profile(HOME_PROFILE, Main.getMinecraftDirectory()));
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
public void save() {
|
||||
try {
|
||||
SETTINGS.getAccounts().clear();
|
||||
for (Map.Entry<String, Account> entry : accounts.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
Account account = entry.getValue();
|
||||
Map<Object, Object> storage = account.toStorage();
|
||||
storage.put("type", Accounts.getAccountType(account));
|
||||
SETTINGS.getAccounts().put(name, storage);
|
||||
}
|
||||
|
||||
FileUtils.writeText(SETTINGS_FILE, GSON.toJson(SETTINGS));
|
||||
} catch (IOException ex) {
|
||||
Logging.LOG.log(Level.SEVERE, "Failed to save config", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private final StringProperty commonPath = new ImmediateStringProperty(this, "commonPath", SETTINGS.getCommonDirectory()) {
|
||||
@Override
|
||||
public void invalidated() {
|
||||
super.invalidated();
|
||||
|
||||
SETTINGS.setCommonDirectory(get());
|
||||
}
|
||||
};
|
||||
|
||||
public String getCommonPath() {
|
||||
return commonPath.get();
|
||||
}
|
||||
|
||||
public StringProperty commonPathProperty() {
|
||||
return commonPath;
|
||||
}
|
||||
|
||||
public void setCommonPath(String commonPath) {
|
||||
this.commonPath.set(commonPath);
|
||||
}
|
||||
|
||||
private Locales.SupportedLocale locale = Locales.getLocaleByName(SETTINGS.getLocalization());
|
||||
|
||||
public Locales.SupportedLocale getLocale() {
|
||||
return locale;
|
||||
}
|
||||
|
||||
public void setLocale(Locales.SupportedLocale locale) {
|
||||
this.locale = locale;
|
||||
SETTINGS.setLocalization(Locales.getNameByLocale(locale));
|
||||
}
|
||||
|
||||
private Proxy proxy = Proxy.NO_PROXY;
|
||||
|
||||
public Proxy getProxy() {
|
||||
return proxy;
|
||||
}
|
||||
|
||||
private Proxy.Type proxyType = Proxies.getProxyType(SETTINGS.getProxyType());
|
||||
|
||||
public Proxy.Type getProxyType() {
|
||||
return proxyType;
|
||||
}
|
||||
|
||||
public void setProxyType(Proxy.Type proxyType) {
|
||||
this.proxyType = proxyType;
|
||||
SETTINGS.setProxyType(Proxies.PROXIES.indexOf(proxyType));
|
||||
loadProxy();
|
||||
}
|
||||
|
||||
public String getProxyHost() {
|
||||
return SETTINGS.getProxyHost();
|
||||
}
|
||||
|
||||
public void setProxyHost(String proxyHost) {
|
||||
SETTINGS.setProxyHost(proxyHost);
|
||||
}
|
||||
|
||||
public String getProxyPort() {
|
||||
return SETTINGS.getProxyPort();
|
||||
}
|
||||
|
||||
public void setProxyPort(String proxyPort) {
|
||||
SETTINGS.setProxyPort(proxyPort);
|
||||
}
|
||||
|
||||
public String getProxyUser() {
|
||||
return SETTINGS.getProxyUser();
|
||||
}
|
||||
|
||||
public void setProxyUser(String proxyUser) {
|
||||
SETTINGS.setProxyUser(proxyUser);
|
||||
}
|
||||
|
||||
public String getProxyPass() {
|
||||
return SETTINGS.getProxyPass();
|
||||
}
|
||||
|
||||
public void setProxyPass(String proxyPass) {
|
||||
SETTINGS.setProxyPass(proxyPass);
|
||||
}
|
||||
|
||||
private void loadProxy() {
|
||||
String host = getProxyHost();
|
||||
Integer port = Lang.toIntOrNull(getProxyPort());
|
||||
if (StringUtils.isBlank(host) || port == null)
|
||||
proxy = Proxy.NO_PROXY;
|
||||
else {
|
||||
System.setProperty("http.proxyHost", getProxyHost());
|
||||
System.setProperty("http.proxyPort", getProxyPort());
|
||||
if (getProxyType() == Proxy.Type.DIRECT)
|
||||
proxy = Proxy.NO_PROXY;
|
||||
else
|
||||
proxy = new Proxy(proxyType, new InetSocketAddress(host, port));
|
||||
|
||||
String user = getProxyUser();
|
||||
String pass = getProxyPass();
|
||||
if (StringUtils.isNotBlank(user) && StringUtils.isNotBlank(pass)) {
|
||||
System.setProperty("http.proxyUser", user);
|
||||
System.setProperty("http.proxyPassword", pass);
|
||||
|
||||
Authenticator.setDefault(new Authenticator() {
|
||||
@Override
|
||||
public PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(user, pass.toCharArray());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Font getFont() {
|
||||
return Font.font(SETTINGS.getFontFamily(), SETTINGS.getFontSize());
|
||||
}
|
||||
|
||||
public void setFont(Font font) {
|
||||
SETTINGS.setFontFamily(font.getFamily());
|
||||
SETTINGS.setFontSize(font.getSize());
|
||||
}
|
||||
|
||||
public int getLogLines() {
|
||||
return Math.max(SETTINGS.getLogLines(), 100);
|
||||
}
|
||||
|
||||
public void setLogLines(int logLines) {
|
||||
SETTINGS.setLogLines(logLines);
|
||||
}
|
||||
|
||||
public DownloadProvider getDownloadProvider() {
|
||||
switch (SETTINGS.getDownloadType()) {
|
||||
case 0:
|
||||
return MojangDownloadProvider.INSTANCE;
|
||||
case 1:
|
||||
return BMCLAPIDownloadProvider.INSTANCE;
|
||||
default:
|
||||
return MojangDownloadProvider.INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
public void setDownloadProvider(DownloadProvider downloadProvider) {
|
||||
if (downloadProvider == MojangDownloadProvider.INSTANCE)
|
||||
SETTINGS.setDownloadType(0);
|
||||
else if (downloadProvider == BMCLAPIDownloadProvider.INSTANCE)
|
||||
SETTINGS.setDownloadType(1);
|
||||
else
|
||||
throw new IllegalArgumentException("Unknown download provider: " + downloadProvider);
|
||||
}
|
||||
|
||||
/****************************************
|
||||
* ACCOUNTS *
|
||||
****************************************/
|
||||
|
||||
private final ImmediateObjectProperty<Account> selectedAccount = new ImmediateObjectProperty<Account>(this, "selectedAccount", getAccount(SETTINGS.getSelectedAccount())) {
|
||||
@Override
|
||||
public Account get() {
|
||||
Account a = super.get();
|
||||
if (a == null || !accounts.containsKey(a.getUsername())) {
|
||||
Account acc = accounts.values().stream().findAny().orElse(null);
|
||||
set(acc);
|
||||
return acc;
|
||||
} else return a;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(Account newValue) {
|
||||
if (newValue == null || accounts.containsKey(newValue.getUsername())) {
|
||||
super.set(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidated() {
|
||||
super.invalidated();
|
||||
|
||||
SETTINGS.setSelectedAccount(getValue() == null ? "" : getValue().getUsername());
|
||||
}
|
||||
};
|
||||
|
||||
public Account getSelectedAccount() {
|
||||
return selectedAccount.get();
|
||||
}
|
||||
|
||||
public ObjectProperty<Account> selectedAccountProperty() {
|
||||
return selectedAccount;
|
||||
}
|
||||
|
||||
public void setSelectedAccount(Account selectedAccount) {
|
||||
this.selectedAccount.set(selectedAccount);
|
||||
}
|
||||
|
||||
public void addAccount(Account account) {
|
||||
accounts.put(account.getUsername(), account);
|
||||
}
|
||||
|
||||
public Account getAccount(String name) {
|
||||
return accounts.get(name);
|
||||
}
|
||||
|
||||
public Map<String, Account> getAccounts() {
|
||||
return Collections.unmodifiableMap(accounts);
|
||||
}
|
||||
|
||||
public void deleteAccount(String name) {
|
||||
accounts.remove(name);
|
||||
|
||||
selectedAccount.get();
|
||||
}
|
||||
|
||||
/****************************************
|
||||
* PROFILES *
|
||||
****************************************/
|
||||
|
||||
private Profile selectedProfile;
|
||||
|
||||
public Profile getSelectedProfile() {
|
||||
if (!hasProfile(SETTINGS.getSelectedProfile())) {
|
||||
SETTINGS.setSelectedProfile(DEFAULT_PROFILE);
|
||||
Schedulers.computation().schedule(this::onProfileChanged);
|
||||
}
|
||||
return getProfile(SETTINGS.getSelectedProfile());
|
||||
}
|
||||
|
||||
public void setSelectedProfile(Profile selectedProfile) {
|
||||
if (hasProfile(selectedProfile.getName()) && !Objects.equals(selectedProfile.getName(), SETTINGS.getSelectedProfile())) {
|
||||
SETTINGS.setSelectedProfile(selectedProfile.getName());
|
||||
Schedulers.computation().schedule(this::onProfileChanged);
|
||||
}
|
||||
}
|
||||
|
||||
public Profile getProfile(String name) {
|
||||
Profile p = getProfileMap().get(Lang.nonNull(name, DEFAULT_PROFILE));
|
||||
if (p == null)
|
||||
if (getProfileMap().containsKey(DEFAULT_PROFILE))
|
||||
p = getProfileMap().get(DEFAULT_PROFILE);
|
||||
else {
|
||||
p = new Profile();
|
||||
getProfileMap().put(DEFAULT_PROFILE, p);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
public boolean hasProfile(String name) {
|
||||
return getProfileMap().containsKey(Lang.nonNull(name, DEFAULT_PROFILE));
|
||||
}
|
||||
|
||||
public Map<String, Profile> getProfileMap() {
|
||||
return SETTINGS.getConfigurations();
|
||||
}
|
||||
|
||||
public Collection<Profile> getProfiles() {
|
||||
return getProfileMap().values().stream().filter(t -> StringUtils.isNotBlank(t.getName())).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public boolean putProfile(Profile ver) {
|
||||
if (ver == null || StringUtils.isBlank(ver.getName()) || getProfileMap().containsKey(ver.getName()))
|
||||
return false;
|
||||
getProfileMap().put(ver.getName(), ver);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean deleteProfile(Profile ver) {
|
||||
return deleteProfile(ver.getName());
|
||||
}
|
||||
|
||||
public boolean deleteProfile(String ver) {
|
||||
if (Objects.equals(DEFAULT_PROFILE, ver)) {
|
||||
return false;
|
||||
}
|
||||
boolean flag = getProfileMap().remove(ver) != null;
|
||||
if (flag)
|
||||
Schedulers.computation().schedule(this::onProfileLoading);
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
private void onProfileChanged() {
|
||||
getSelectedProfile().getRepository().refreshVersions();
|
||||
EventBus.EVENT_BUS.fireEvent(new ProfileChangedEvent(SETTINGS, getSelectedProfile()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Start profiles loading process.
|
||||
* Invoked by loading GUI phase.
|
||||
*/
|
||||
public void onProfileLoading() {
|
||||
EventBus.EVENT_BUS.fireEvent(new ProfileLoadingEvent(SETTINGS));
|
||||
onProfileChanged();
|
||||
}
|
||||
}
|
||||
49
HMCL/src/main/java/org/jackhuang/hmcl/ui/ClassTitle.java
Normal file
49
HMCL/src/main/java/org/jackhuang/hmcl/ui/ClassTitle.java
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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 javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
import javafx.scene.text.Text;
|
||||
|
||||
/**
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public class ClassTitle extends StackPane {
|
||||
private final String text;
|
||||
|
||||
public ClassTitle(String text) {
|
||||
this.text = text;
|
||||
|
||||
VBox vbox = new VBox();
|
||||
vbox.getChildren().addAll(new Text(text));
|
||||
Rectangle rectangle = new Rectangle();
|
||||
rectangle.widthProperty().bind(vbox.widthProperty());
|
||||
rectangle.setHeight(1.0);
|
||||
rectangle.setFill(Color.GRAY);
|
||||
vbox.getChildren().add(rectangle);
|
||||
getChildren().setAll(vbox);
|
||||
getStyleClass().add("class-title");
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.concurrency.JFXUtilities;
|
||||
import kotlin.Unit;
|
||||
import org.jackhuang.hmcl.auth.Account;
|
||||
import org.jackhuang.hmcl.auth.AuthInfo;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
|
||||
import org.jackhuang.hmcl.task.SilentException;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public final class DialogController {
|
||||
public static final DialogController INSTANCE = new DialogController();
|
||||
|
||||
private DialogController() {}
|
||||
|
||||
public static AuthInfo logIn(Account account) throws Exception {
|
||||
if (account instanceof YggdrasilAccount) {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
AtomicReference<AuthInfo> res = new AtomicReference<>(null);
|
||||
JFXUtilities.runInFX(() -> {
|
||||
YggdrasilAccountLoginPane pane = new YggdrasilAccountLoginPane((YggdrasilAccount) account, it -> {
|
||||
res.set(it);
|
||||
latch.countDown();
|
||||
Controllers.INSTANCE.closeDialog();
|
||||
return Unit.INSTANCE;
|
||||
}, () -> {
|
||||
latch.countDown();
|
||||
Controllers.INSTANCE.closeDialog();
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
pane.dialog = Controllers.INSTANCE.dialog(pane);
|
||||
});
|
||||
latch.await();
|
||||
return Optional.ofNullable(res.get()).orElseThrow(SilentException::new);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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 javafx.fxml.FXML;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.jackhuang.hmcl.download.game.VersionJsonSaveTask;
|
||||
import org.jackhuang.hmcl.game.GameVersion;
|
||||
import org.jackhuang.hmcl.game.Library;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.download.InstallerWizardProvider;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class InstallerController {
|
||||
private Profile profile;
|
||||
private String versionId;
|
||||
private Version version;
|
||||
@FXML
|
||||
private ScrollPane scrollPane;
|
||||
@FXML private VBox contentPane;
|
||||
private String forge;
|
||||
private String liteLoader;
|
||||
private String optiFine;
|
||||
|
||||
public void initialize() {
|
||||
FXUtilsKt.smoothScrolling(scrollPane);
|
||||
}
|
||||
|
||||
public void loadVersion(Profile profile, String versionId) {
|
||||
this.profile = profile;
|
||||
this.versionId = versionId;
|
||||
this.version = profile.getRepository().getVersion(versionId).resolve(profile.getRepository());
|
||||
|
||||
contentPane.getChildren().clear();
|
||||
forge = liteLoader = optiFine = null;
|
||||
|
||||
for (Library library : version.getLibraries()) {
|
||||
Consumer<InstallerItem> removeAction = x -> {
|
||||
LinkedList<Library> newList = new LinkedList<>(version.getLibraries());
|
||||
newList.remove(library);
|
||||
new VersionJsonSaveTask(profile.getRepository(), version.setLibraries(newList))
|
||||
.with(Task.of(e -> profile.getRepository().refreshVersions()))
|
||||
.with(Task.of(e -> loadVersion(this.profile, this.versionId)))
|
||||
.start();
|
||||
};
|
||||
|
||||
if (library.getGroupId().equalsIgnoreCase("net.minecraftforge") && library.getArtifactId().equalsIgnoreCase("forge")) {
|
||||
contentPane.getChildren().add(new InstallerItem("Forge", library.getVersion(), removeAction));
|
||||
forge = library.getVersion();
|
||||
}
|
||||
if (library.getGroupId().equalsIgnoreCase("com.mumfrey") && library.getArtifactId().equalsIgnoreCase("liteloader")) {
|
||||
contentPane.getChildren().add(new InstallerItem("LiteLoader", library.getVersion(), removeAction));
|
||||
liteLoader = library.getVersion();
|
||||
}
|
||||
if (library.getGroupId().equalsIgnoreCase("net.optifine") && library.getArtifactId().equalsIgnoreCase("optifine")) {
|
||||
contentPane.getChildren().add(new InstallerItem("OptiFine", library.getVersion(), removeAction));
|
||||
optiFine = library.getVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onAdd() {
|
||||
String gameVersion = GameVersion.minecraftVersion(profile.getRepository().getVersionJar(version));
|
||||
|
||||
// TODO: if minecraftVersion returns null.
|
||||
if (gameVersion == null) return;
|
||||
|
||||
Controllers.INSTANCE.getDecorator().startWizard(new InstallerWizardProvider(profile, gameVersion, version, forge, liteLoader, optiFine));
|
||||
}
|
||||
}
|
||||
52
HMCL/src/main/java/org/jackhuang/hmcl/ui/InstallerItem.java
Normal file
52
HMCL/src/main/java/org/jackhuang/hmcl/ui/InstallerItem.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.effects.JFXDepthManager;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public class InstallerItem extends BorderPane {
|
||||
private final Consumer<InstallerItem> deleteCallback;
|
||||
|
||||
@FXML
|
||||
private Label lblInstallerArtifact;
|
||||
|
||||
@FXML
|
||||
private Label lblInstallerVersion;
|
||||
|
||||
public InstallerItem(String artifact, String version, Consumer<InstallerItem> deleteCallback) {
|
||||
this.deleteCallback = deleteCallback;
|
||||
FXUtilsKt.loadFXML(this, "/assets/fxml/version/installer-item.fxml");
|
||||
|
||||
setStyle("-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;");
|
||||
JFXDepthManager.setDepth(this, 1);
|
||||
lblInstallerArtifact.setText(artifact);
|
||||
lblInstallerVersion.setText(version);
|
||||
}
|
||||
|
||||
public void onDelete() {
|
||||
deleteCallback.accept(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.JFXProgressBar;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.StackPane;
|
||||
|
||||
public class LaunchingStepsPane extends StackPane {
|
||||
@FXML
|
||||
private JFXProgressBar pgsTasks;
|
||||
@FXML
|
||||
private Label lblCurrentState;
|
||||
@FXML
|
||||
private Label lblSteps;
|
||||
|
||||
public LaunchingStepsPane() {
|
||||
FXUtilsKt.loadFXML(this, "/assets/fxml/launching-steps.fxml");
|
||||
|
||||
FXUtilsKt.limitHeight(this, 200);
|
||||
FXUtilsKt.limitWidth(this, 400);
|
||||
}
|
||||
|
||||
public void setCurrentState(String currentState) {
|
||||
lblCurrentState.setText(currentState);
|
||||
}
|
||||
|
||||
public void setSteps(String steps) {
|
||||
lblSteps.setText(steps);
|
||||
}
|
||||
|
||||
public void setProgress(double progress) {
|
||||
pgsTasks.setProgress(progress);
|
||||
}
|
||||
}
|
||||
@@ -50,11 +50,11 @@ import org.w3c.dom.Node;
|
||||
*/
|
||||
public final class LogWindow extends Stage {
|
||||
|
||||
private final ReadOnlyIntegerWrapper fatalProperty = new ReadOnlyIntegerWrapper(0);
|
||||
private final ReadOnlyIntegerWrapper errorProperty = new ReadOnlyIntegerWrapper(0);
|
||||
private final ReadOnlyIntegerWrapper warnProperty = new ReadOnlyIntegerWrapper(0);
|
||||
private final ReadOnlyIntegerWrapper infoProperty = new ReadOnlyIntegerWrapper(0);
|
||||
private final ReadOnlyIntegerWrapper debugProperty = new ReadOnlyIntegerWrapper(0);
|
||||
private final ReadOnlyIntegerWrapper fatal = new ReadOnlyIntegerWrapper(0);
|
||||
private final ReadOnlyIntegerWrapper error = new ReadOnlyIntegerWrapper(0);
|
||||
private final ReadOnlyIntegerWrapper warn = new ReadOnlyIntegerWrapper(0);
|
||||
private final ReadOnlyIntegerWrapper info = new ReadOnlyIntegerWrapper(0);
|
||||
private final ReadOnlyIntegerWrapper debug = new ReadOnlyIntegerWrapper(0);
|
||||
private final LogWindowImpl impl = new LogWindowImpl();
|
||||
private final CountDownLatch latch = new CountDownLatch(1);
|
||||
public final EventManager<Event> onDone = new EventManager<>();
|
||||
@@ -75,43 +75,43 @@ public final class LogWindow extends Stage {
|
||||
}
|
||||
|
||||
public ReadOnlyIntegerProperty fatalProperty() {
|
||||
return fatalProperty.getReadOnlyProperty();
|
||||
return fatal.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
public int getFatal() {
|
||||
return fatalProperty.get();
|
||||
return fatal.get();
|
||||
}
|
||||
|
||||
public ReadOnlyIntegerProperty errorProperty() {
|
||||
return errorProperty.getReadOnlyProperty();
|
||||
return error.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
public int getError() {
|
||||
return errorProperty.get();
|
||||
return error.get();
|
||||
}
|
||||
|
||||
public ReadOnlyIntegerProperty warnProperty() {
|
||||
return warnProperty.getReadOnlyProperty();
|
||||
return warn.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
public int getWarn() {
|
||||
return warnProperty.get();
|
||||
return warn.get();
|
||||
}
|
||||
|
||||
public ReadOnlyIntegerProperty infoProperty() {
|
||||
return infoProperty.getReadOnlyProperty();
|
||||
return info.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
public int getInfo() {
|
||||
return infoProperty.get();
|
||||
return info.get();
|
||||
}
|
||||
|
||||
public ReadOnlyIntegerProperty debugProperty() {
|
||||
return debugProperty.getReadOnlyProperty();
|
||||
return debug.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
public int getDebug() {
|
||||
return debugProperty.get();
|
||||
return debug.get();
|
||||
}
|
||||
|
||||
public void logLine(String line, Log4jLevel level) {
|
||||
@@ -125,45 +125,45 @@ public final class LogWindow extends Stage {
|
||||
|
||||
switch (level) {
|
||||
case FATAL:
|
||||
fatalProperty.set(fatalProperty.get() + 1);
|
||||
fatal.set(fatal.get() + 1);
|
||||
break;
|
||||
case ERROR:
|
||||
errorProperty.set(errorProperty.get() + 1);
|
||||
error.set(error.get() + 1);
|
||||
break;
|
||||
case WARN:
|
||||
warnProperty.set(warnProperty.get() + 1);
|
||||
warn.set(warn.get() + 1);
|
||||
break;
|
||||
case INFO:
|
||||
infoProperty.set(infoProperty.get() + 1);
|
||||
info.set(info.get() + 1);
|
||||
break;
|
||||
case DEBUG:
|
||||
debugProperty.set(debugProperty.get() + 1);
|
||||
debug.set(debug.get() + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public class LogWindowImpl extends StackPane {
|
||||
private class LogWindowImpl extends StackPane {
|
||||
|
||||
@FXML
|
||||
public WebView webView;
|
||||
private WebView webView;
|
||||
@FXML
|
||||
public ToggleButton btnFatals;
|
||||
private ToggleButton btnFatals;
|
||||
@FXML
|
||||
public ToggleButton btnErrors;
|
||||
private ToggleButton btnErrors;
|
||||
@FXML
|
||||
public ToggleButton btnWarns;
|
||||
private ToggleButton btnWarns;
|
||||
@FXML
|
||||
public ToggleButton btnInfos;
|
||||
private ToggleButton btnInfos;
|
||||
@FXML
|
||||
public ToggleButton btnDebugs;
|
||||
private ToggleButton btnDebugs;
|
||||
@FXML
|
||||
public ComboBox<String> cboLines;
|
||||
private ComboBox<String> cboLines;
|
||||
|
||||
WebEngine engine;
|
||||
Node body;
|
||||
Document document;
|
||||
|
||||
public LogWindowImpl() {
|
||||
LogWindowImpl() {
|
||||
FXUtilsKt.loadFXML(this, "/assets/fxml/log.fxml");
|
||||
|
||||
engine = webView.getEngine();
|
||||
@@ -194,11 +194,11 @@ public final class LogWindow extends Stage {
|
||||
if (!flag)
|
||||
cboLines.getSelectionModel().select(0);
|
||||
|
||||
btnFatals.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(fatalProperty.get()) + " fatals", fatalProperty));
|
||||
btnErrors.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(errorProperty.get()) + " errors", errorProperty));
|
||||
btnWarns.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(warnProperty.get()) + " warns", warnProperty));
|
||||
btnInfos.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(infoProperty.get()) + " infos", infoProperty));
|
||||
btnDebugs.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(debugProperty.get()) + " debugs", debugProperty));
|
||||
btnFatals.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(fatal.get()) + " fatals", fatal));
|
||||
btnErrors.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(error.get()) + " errors", error));
|
||||
btnWarns.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(warn.get()) + " warns", warn));
|
||||
btnInfos.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(info.get()) + " infos", info));
|
||||
btnDebugs.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(debug.get()) + " debugs", debug));
|
||||
|
||||
btnFatals.selectedProperty().addListener(o -> specificChanged());
|
||||
btnErrors.selectedProperty().addListener(o -> specificChanged());
|
||||
|
||||
@@ -15,24 +15,29 @@
|
||||
* 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.download
|
||||
package org.jackhuang.hmcl.ui;
|
||||
|
||||
import javafx.fxml.FXML
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.layout.StackPane
|
||||
import org.jackhuang.hmcl.download.RemoteVersion
|
||||
import org.jackhuang.hmcl.ui.loadFXML
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import com.jfoenix.controls.JFXDialog;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.StackPane;
|
||||
|
||||
class VersionsPageItem(val remoteVersion: RemoteVersion<*>) : StackPane() {
|
||||
public final class MessageDialogPane extends StackPane {
|
||||
private final String text;
|
||||
private final JFXDialog dialog;
|
||||
|
||||
@FXML lateinit var lblSelfVersion: Label
|
||||
@FXML lateinit var lblGameVersion: Label
|
||||
@FXML
|
||||
private JFXButton acceptButton;
|
||||
@FXML
|
||||
private Label content;
|
||||
|
||||
private var handler: () -> Unit = {}
|
||||
public MessageDialogPane(String text, JFXDialog dialog) {
|
||||
this.text = text;
|
||||
this.dialog = dialog;
|
||||
|
||||
init {
|
||||
loadFXML("/assets/fxml/download/versions-list-item.fxml")
|
||||
lblSelfVersion.text = remoteVersion.selfVersion
|
||||
lblGameVersion.text = remoteVersion.gameVersion
|
||||
FXUtilsKt.loadFXML(this, "/assets/fxml/message-dialog.fxml");
|
||||
content.setText(text);
|
||||
acceptButton.setOnMouseClicked(e -> dialog.close());
|
||||
}
|
||||
}
|
||||
63
HMCL/src/main/java/org/jackhuang/hmcl/ui/ModItem.java
Normal file
63
HMCL/src/main/java/org/jackhuang/hmcl/ui/ModItem.java
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.JFXCheckBox;
|
||||
import com.jfoenix.effects.JFXDepthManager;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.jackhuang.hmcl.mod.ModInfo;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public final class ModItem extends BorderPane {
|
||||
private final Label lblModFileName = new Label();
|
||||
private final Label lblModAuthor = new Label();
|
||||
private final JFXCheckBox chkEnabled = new JFXCheckBox();
|
||||
|
||||
public ModItem(ModInfo info, Consumer<ModItem> deleteCallback) {
|
||||
lblModFileName.setStyle("-fx-font-size: 15;");
|
||||
lblModAuthor.setStyle("-fx-font-size: 10;");
|
||||
BorderPane.setAlignment(chkEnabled, Pos.CENTER);
|
||||
setLeft(chkEnabled);
|
||||
|
||||
VBox center = new VBox();
|
||||
BorderPane.setAlignment(center, Pos.CENTER);
|
||||
center.getChildren().addAll(lblModFileName, lblModAuthor);
|
||||
setCenter(center);
|
||||
|
||||
JFXButton right = new JFXButton();
|
||||
right.setOnMouseClicked(e -> deleteCallback.accept(this));
|
||||
right.getStyleClass().add("toggle-icon4");
|
||||
BorderPane.setAlignment(right, Pos.CENTER);
|
||||
right.setGraphic(SVG.close("black", 15, 15));
|
||||
setRight(right);
|
||||
|
||||
setStyle("-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;");
|
||||
JFXDepthManager.setDepth(this, 1);
|
||||
lblModFileName.setText(info.getFileName());
|
||||
lblModAuthor.setText(info.getName() + ", Version: " + info.getVersion() + ", Game: " + info.getGameVersion() + ", Authors: " + info.getAuthors());
|
||||
chkEnabled.setSelected(info.isActive());
|
||||
chkEnabled.selectedProperty().addListener((a, b, newValue) -> {
|
||||
info.activeProperty().set(newValue);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 javafx.util.StringConverter;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class SafeIntStringConverter extends StringConverter<Integer> {
|
||||
@Override
|
||||
public Integer fromString(String string) {
|
||||
return Optional.ofNullable(string).map(Lang::toIntOrNull).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(Integer object) {
|
||||
return Optional.ofNullable(object).map(Object::toString).orElse("");
|
||||
}
|
||||
}
|
||||
93
HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionItem.java
Normal file
93
HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionItem.java
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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 javafx.beans.binding.Bindings;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.effect.BlurType;
|
||||
import javafx.scene.effect.DropShadow;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.paint.Color;
|
||||
|
||||
public final class VersionItem extends StackPane {
|
||||
@FXML
|
||||
private Pane icon;
|
||||
@FXML
|
||||
private VBox content;
|
||||
@FXML
|
||||
private StackPane header;
|
||||
@FXML
|
||||
private StackPane body;
|
||||
@FXML
|
||||
private JFXButton btnDelete;
|
||||
@FXML
|
||||
private JFXButton btnSettings;
|
||||
@FXML
|
||||
private JFXButton btnLaunch;
|
||||
@FXML
|
||||
private Label lblVersionName;
|
||||
@FXML
|
||||
private Label lblGameVersion;
|
||||
@FXML
|
||||
private ImageView iconView;
|
||||
|
||||
public VersionItem() {
|
||||
FXUtilsKt.loadFXML(this, "/assets/fxml/version-item.fxml");
|
||||
FXUtilsKt.limitWidth(this, 160);
|
||||
FXUtilsKt.limitHeight(this, 156);
|
||||
setEffect(new DropShadow(BlurType.GAUSSIAN, Color.rgb(0, 0, 0, 0.26), 5.0, 0.12, -1.0, 1.0));
|
||||
btnSettings.setGraphic(SVG.gear("black", 15, 15));
|
||||
btnDelete.setGraphic(SVG.delete("black", 15, 15));
|
||||
btnLaunch.setGraphic(SVG.launch("black", 15, 15));
|
||||
|
||||
icon.translateYProperty().bind(Bindings.createDoubleBinding(() -> header.getBoundsInParent().getHeight() - icon.getHeight() / 2 - 16, header.boundsInParentProperty(), icon.heightProperty()));
|
||||
FXUtilsKt.limitSize(iconView, 32, 32);
|
||||
}
|
||||
|
||||
public void setVersionName(String versionName) {
|
||||
lblVersionName.setText(versionName);
|
||||
}
|
||||
|
||||
public void setGameVersion(String gameVersion) {
|
||||
lblGameVersion.setText(gameVersion);
|
||||
}
|
||||
|
||||
public void setImage(Image image) {
|
||||
iconView.setImage(image);
|
||||
}
|
||||
|
||||
public void setOnSettingsButtonClicked(EventHandler<? super MouseEvent> handler) {
|
||||
btnSettings.setOnMouseClicked(handler);
|
||||
}
|
||||
|
||||
public void setOnDeleteButtonClicked(EventHandler<? super MouseEvent> handler) {
|
||||
btnDelete.setOnMouseClicked(handler);
|
||||
}
|
||||
|
||||
public void setOnLaunchButtonClicked(EventHandler<? super MouseEvent> handler) {
|
||||
btnLaunch.setOnMouseClicked(handler);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui;
|
||||
|
||||
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.layout.StackPane;
|
||||
|
||||
public final class VersionListItem extends StackPane {
|
||||
@FXML
|
||||
private StackPane imageViewContainer;
|
||||
@FXML
|
||||
private Label lblVersionName;
|
||||
@FXML
|
||||
private Label lblGameVersion;
|
||||
@FXML
|
||||
private ImageView imageView;
|
||||
private Runnable handler;
|
||||
|
||||
public VersionListItem(String versionName) {
|
||||
this(versionName, "");
|
||||
}
|
||||
|
||||
public VersionListItem(String versionName, String gameVersion) {
|
||||
FXUtilsKt.loadFXML(this, "/assets/fxml/version-list-item.fxml");
|
||||
|
||||
lblVersionName.setText(versionName);
|
||||
lblGameVersion.setText(gameVersion);
|
||||
|
||||
FXUtilsKt.limitSize(imageView, 32, 32);
|
||||
FXUtilsKt.limitWidth(imageViewContainer, 32);
|
||||
FXUtilsKt.limitHeight(imageViewContainer, 32);
|
||||
}
|
||||
|
||||
public void onSettings() {
|
||||
handler.run();
|
||||
}
|
||||
|
||||
public void onSettingsButtonClicked(Runnable handler) {
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
public void setVersionName(String versionName) {
|
||||
lblVersionName.setText(versionName);
|
||||
}
|
||||
|
||||
public void setGameVersion(String gameVersion) {
|
||||
lblGameVersion.setText(gameVersion);
|
||||
}
|
||||
|
||||
public void setImage(Image image, Rectangle2D viewport) {
|
||||
imageView.setImage(image);
|
||||
imageView.setViewport(viewport);
|
||||
}
|
||||
}
|
||||
37
HMCL/src/main/java/org/jackhuang/hmcl/ui/WebStage.java
Normal file
37
HMCL/src/main/java/org/jackhuang/hmcl/ui/WebStage.java
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 javafx.scene.Scene;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.web.WebView;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
public class WebStage extends Stage {
|
||||
private final WebView webView = new WebView();
|
||||
|
||||
public WebStage() {
|
||||
setScene(new Scene(webView, 800, 480));
|
||||
getScene().getStylesheets().addAll(FXUtilsKt.getStylesheets());
|
||||
getIcons().add(new Image("/assets/img/icon.png"));
|
||||
}
|
||||
|
||||
public WebView getWebView() {
|
||||
return webView;
|
||||
}
|
||||
}
|
||||
@@ -24,5 +24,5 @@ import javafx.util.Duration;
|
||||
public interface AnimationHandler {
|
||||
Node getSnapshot();
|
||||
Duration getDuration();
|
||||
Pane getView();
|
||||
Pane getCurrentRoot();
|
||||
}
|
||||
|
||||
@@ -15,10 +15,14 @@
|
||||
* 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.wizard
|
||||
package org.jackhuang.hmcl.ui.animation;
|
||||
|
||||
interface WizardPage {
|
||||
fun onNavigate(settings: MutableMap<String, Any>) {}
|
||||
fun cleanup(settings: MutableMap<String, Any>)
|
||||
val title: String
|
||||
import javafx.animation.KeyFrame;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface AnimationProducer {
|
||||
|
||||
List<KeyFrame> animate(AnimationHandler handler);
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.animation;
|
||||
|
||||
import javafx.animation.Interpolator;
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.KeyValue;
|
||||
import javafx.util.Duration;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
public enum ContainerAnimations {
|
||||
NONE(c -> Collections.emptyList()),
|
||||
/**
|
||||
* A fade between the old and new view
|
||||
*/
|
||||
FADE(c ->
|
||||
Arrays.asList(new KeyFrame(Duration.ZERO, new KeyValue(c.getSnapshot().opacityProperty(), 1.0D, Interpolator.EASE_BOTH)),
|
||||
new KeyFrame(c.getDuration(), new KeyValue(c.getSnapshot().opacityProperty(), 0.0D, Interpolator.EASE_BOTH)))),
|
||||
/**
|
||||
* A zoom effect
|
||||
*/
|
||||
ZOOM_IN(c ->
|
||||
Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
new KeyValue(c.getSnapshot().scaleXProperty(), 1, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getSnapshot().scaleYProperty(), 1, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getSnapshot().opacityProperty(), 1.0D, Interpolator.EASE_BOTH)),
|
||||
new KeyFrame(c.getDuration(),
|
||||
new KeyValue(c.getSnapshot().scaleXProperty(), 4, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getSnapshot().scaleYProperty(), 4, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getSnapshot().opacityProperty(), 0, Interpolator.EASE_BOTH)))),
|
||||
/**
|
||||
* A zoom effect
|
||||
*/
|
||||
ZOOM_OUT(c ->
|
||||
(Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
new KeyValue(c.getSnapshot().scaleXProperty(), 1, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getSnapshot().scaleYProperty(), 1, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getSnapshot().opacityProperty(), 1.0D, Interpolator.EASE_BOTH)),
|
||||
new KeyFrame(c.getDuration(),
|
||||
new KeyValue(c.getSnapshot().scaleXProperty(), 0, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getSnapshot().scaleYProperty(), 0, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getSnapshot().opacityProperty(), 0, Interpolator.EASE_BOTH))))),
|
||||
/**
|
||||
* A swipe effect
|
||||
*/
|
||||
SWIPE_LEFT(c ->
|
||||
Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
new KeyValue(c.getCurrentRoot().translateXProperty(), c.getCurrentRoot().getWidth(), Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getSnapshot().translateXProperty(), -c.getCurrentRoot().getWidth(), Interpolator.EASE_BOTH)),
|
||||
new KeyFrame(c.getDuration(),
|
||||
new KeyValue(c.getCurrentRoot().translateXProperty(), 0, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getSnapshot().translateXProperty(), -c.getCurrentRoot().getWidth(), Interpolator.EASE_BOTH)))),
|
||||
|
||||
/**
|
||||
* A swipe effect
|
||||
*/
|
||||
SWIPE_RIGHT(c ->
|
||||
Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
new KeyValue(c.getCurrentRoot().translateXProperty(), -c.getCurrentRoot().getWidth(), Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getSnapshot().translateXProperty(), c.getCurrentRoot().getWidth(), Interpolator.EASE_BOTH)),
|
||||
new KeyFrame(c.getDuration(),
|
||||
new KeyValue(c.getCurrentRoot().translateXProperty(), 0, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getSnapshot().translateXProperty(), c.getCurrentRoot().getWidth(), Interpolator.EASE_BOTH))));
|
||||
|
||||
private AnimationProducer animationProducer;
|
||||
|
||||
ContainerAnimations(AnimationProducer animationProducer) {
|
||||
this.animationProducer = animationProducer;
|
||||
}
|
||||
|
||||
public AnimationProducer getAnimationProducer() {
|
||||
return animationProducer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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.animation;
|
||||
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.SnapshotParameters;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.image.WritableImage;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.util.Duration;
|
||||
import org.jackhuang.hmcl.ui.FXUtilsKt;
|
||||
|
||||
public final class TransitionHandler implements AnimationHandler {
|
||||
private final StackPane view;
|
||||
private Timeline animation;
|
||||
private Duration duration;
|
||||
private final ImageView snapshot;
|
||||
|
||||
/**
|
||||
* @param view A stack pane that contains another control that is [Parent]
|
||||
*/
|
||||
public TransitionHandler(StackPane view) {
|
||||
this.view = view;
|
||||
|
||||
snapshot = new ImageView();
|
||||
snapshot.setPreserveRatio(true);
|
||||
snapshot.setSmooth(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getSnapshot() {
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StackPane getCurrentRoot() {
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public void setContent(Node newView, AnimationProducer transition) {
|
||||
setContent(newView, transition, Duration.millis(320));
|
||||
}
|
||||
|
||||
public void setContent(Node newView, AnimationProducer transition, Duration duration) {
|
||||
this.duration = duration;
|
||||
|
||||
Timeline prev = animation;
|
||||
if (prev != null)
|
||||
prev.stop();
|
||||
|
||||
updateContent(newView);
|
||||
|
||||
Timeline nowAnimation = new Timeline();
|
||||
nowAnimation.getKeyFrames().addAll(transition.animate(this));
|
||||
nowAnimation.getKeyFrames().add(new KeyFrame(duration, e -> {
|
||||
snapshot.setImage(null);
|
||||
snapshot.setX(0);
|
||||
snapshot.setY(0);
|
||||
snapshot.setVisible(false);
|
||||
}));
|
||||
nowAnimation.play();
|
||||
animation = nowAnimation;
|
||||
}
|
||||
|
||||
private void updateContent(Node newView) {
|
||||
if (view.getWidth() > 0 && view.getHeight() > 0) {
|
||||
Node content = view.getChildren().stream().findFirst().orElse(null);
|
||||
WritableImage image;
|
||||
if (content != null && content instanceof Parent) {
|
||||
view.getChildren().setAll();
|
||||
image = FXUtilsKt.takeSnapshot((Parent) content, view.getWidth(), view.getHeight());
|
||||
view.getChildren().setAll(content);
|
||||
} else
|
||||
image = view.snapshot(new SnapshotParameters(), new WritableImage((int) view.getWidth(), (int) view.getHeight()));
|
||||
snapshot.setImage(image);
|
||||
snapshot.setFitWidth(view.getWidth());
|
||||
snapshot.setFitHeight(view.getHeight());
|
||||
} else
|
||||
snapshot.setImage(null);
|
||||
|
||||
snapshot.setVisible(true);
|
||||
snapshot.setOpacity(1.0);
|
||||
view.getChildren().setAll(snapshot, newView);
|
||||
snapshot.toFront();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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.beans.DefaultProperty;
|
||||
import javafx.beans.property.IntegerProperty;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
@DefaultProperty("content")
|
||||
public class ComponentList extends StackPane {
|
||||
private final VBox vbox = new VBox();
|
||||
private final StringProperty title = new SimpleStringProperty(this, "title", "Group");
|
||||
private final StringProperty subtitle = new SimpleStringProperty(this, "subtitle", "");
|
||||
private final IntegerProperty depth = new SimpleIntegerProperty(this, "depth", 0);
|
||||
private boolean hasSubtitle = false;
|
||||
public final ObservableList<Node> content = FXCollections.observableArrayList();
|
||||
|
||||
public ComponentList() {
|
||||
getChildren().setAll(vbox);
|
||||
content.addListener((ListChangeListener<? super Node>) change -> {
|
||||
while (change.next()) {
|
||||
for (int i = change.getFrom(); i < change.getTo(); ++i)
|
||||
addChildren(change.getList().get(i));
|
||||
}
|
||||
});
|
||||
getStyleClass().add("options-list");
|
||||
}
|
||||
|
||||
public void addChildren(Node node) {
|
||||
if (node instanceof ComponentList) {
|
||||
node.getProperties().put("title", ((ComponentList) node).getTitle());
|
||||
node.getProperties().put("subtitle", ((ComponentList) node).getSubtitle());
|
||||
}
|
||||
StackPane child = new StackPane();
|
||||
child.getChildren().add(new ComponentListCell(node));
|
||||
if (vbox.getChildren().isEmpty())
|
||||
child.getStyleClass().add("options-list-item-ahead");
|
||||
else
|
||||
child.getStyleClass().add("options-list-item");
|
||||
vbox.getChildren().add(child);
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title.get();
|
||||
}
|
||||
|
||||
public StringProperty titleProperty() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title.set(title);
|
||||
}
|
||||
|
||||
public String getSubtitle() {
|
||||
return subtitle.get();
|
||||
}
|
||||
|
||||
public StringProperty subtitleProperty() {
|
||||
return subtitle;
|
||||
}
|
||||
|
||||
public void setSubtitle(String subtitle) {
|
||||
this.subtitle.set(subtitle);
|
||||
}
|
||||
|
||||
public int getDepth() {
|
||||
return depth.get();
|
||||
}
|
||||
|
||||
public IntegerProperty depthProperty() {
|
||||
return depth;
|
||||
}
|
||||
|
||||
public void setDepth(int depth) {
|
||||
this.depth.set(depth);
|
||||
}
|
||||
|
||||
public boolean isHasSubtitle() {
|
||||
return hasSubtitle;
|
||||
}
|
||||
|
||||
public void setHasSubtitle(boolean hasSubtitle) {
|
||||
this.hasSubtitle = hasSubtitle;
|
||||
}
|
||||
|
||||
public ObservableList<Node> getContent() {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* 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 com.jfoenix.controls.JFXButton;
|
||||
import javafx.animation.*;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
import javafx.util.Duration;
|
||||
import org.jackhuang.hmcl.ui.FXUtilsKt;
|
||||
import org.jackhuang.hmcl.ui.SVG;
|
||||
|
||||
/**
|
||||
* @author
|
||||
*/
|
||||
public class ComponentListCell extends StackPane {
|
||||
private final Node content;
|
||||
private Animation expandAnimation;
|
||||
private Rectangle clipRect;
|
||||
private double animatedHeight;
|
||||
private final BooleanProperty expanded = new SimpleBooleanProperty(this, "expanded", false);
|
||||
|
||||
public ComponentListCell(Node content) {
|
||||
this.content = content;
|
||||
|
||||
updateLayout();
|
||||
}
|
||||
|
||||
private void updateClip(double newHeight) {
|
||||
if (clipRect != null)
|
||||
clipRect.setHeight(newHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void layoutChildren() {
|
||||
super.layoutChildren();
|
||||
|
||||
if (clipRect == null)
|
||||
clipRect = new Rectangle(0, 0, getWidth(), getHeight());
|
||||
else {
|
||||
clipRect.setX(0);
|
||||
clipRect.setY(0);
|
||||
clipRect.setHeight(getHeight());
|
||||
clipRect.setWidth(getWidth());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateLayout() {
|
||||
if (content instanceof ComponentList) {
|
||||
ComponentList list = (ComponentList) content;
|
||||
content.getStyleClass().remove("options-list");
|
||||
content.getStyleClass().add("options-sublist");
|
||||
|
||||
StackPane groupNode = new StackPane();
|
||||
groupNode.getStyleClass().add("options-list-item-header");
|
||||
|
||||
Node expandIcon = SVG.expand("black", 10, 10);
|
||||
JFXButton expandButton = new JFXButton();
|
||||
expandButton.setGraphic(expandIcon);
|
||||
expandButton.getStyleClass().add("options-list-item-expand-button");
|
||||
StackPane.setAlignment(expandButton, Pos.CENTER_RIGHT);
|
||||
|
||||
VBox labelVBox = new VBox();
|
||||
Label label = new Label();
|
||||
label.textProperty().bind(list.titleProperty());
|
||||
label.setMouseTransparent(true);
|
||||
labelVBox.getChildren().add(label);
|
||||
|
||||
if (list.isHasSubtitle()) {
|
||||
Label subtitleLabel = new Label();
|
||||
subtitleLabel.textProperty().bind(list.subtitleProperty());
|
||||
subtitleLabel.setMouseTransparent(true);
|
||||
subtitleLabel.getStyleClass().add("subtitle-label");
|
||||
labelVBox.getChildren().add(subtitleLabel);
|
||||
}
|
||||
|
||||
StackPane.setAlignment(labelVBox, Pos.CENTER_LEFT);
|
||||
groupNode.getChildren().setAll(labelVBox, expandButton);
|
||||
|
||||
VBox container = new VBox();
|
||||
container.setStyle("-fx-padding: 8 0 0 0;");
|
||||
FXUtilsKt.limitHeight(container, 0);
|
||||
Rectangle clipRect = new Rectangle();
|
||||
clipRect.widthProperty().bind(container.widthProperty());
|
||||
clipRect.heightProperty().bind(container.heightProperty());
|
||||
container.setClip(clipRect);
|
||||
container.getChildren().setAll(content);
|
||||
|
||||
VBox holder = new VBox();
|
||||
holder.getChildren().setAll(groupNode, container);
|
||||
holder.getStyleClass().add("options-list-item-container");
|
||||
|
||||
expandButton.setOnMouseClicked(e -> {
|
||||
if (expandAnimation != null && expandAnimation.getStatus() == Animation.Status.RUNNING) {
|
||||
expandAnimation.stop();
|
||||
}
|
||||
|
||||
setExpanded(!isExpanded());
|
||||
|
||||
double newAnimatedHeight = content.prefHeight(-1) * (isExpanded() ? 1 : -1);
|
||||
double newHeight = isExpanded() ? getHeight() + newAnimatedHeight : prefHeight(-1);
|
||||
double contentHeight = isExpanded() ? newAnimatedHeight : 0;
|
||||
|
||||
if (isExpanded()) {
|
||||
updateClip(newHeight);
|
||||
}
|
||||
|
||||
animatedHeight = newAnimatedHeight;
|
||||
|
||||
expandAnimation = new Timeline(new KeyFrame(new Duration(320.0),
|
||||
new KeyValue(container.minHeightProperty(), contentHeight, FXUtilsKt.SINE),
|
||||
new KeyValue(container.maxHeightProperty(), contentHeight, FXUtilsKt.SINE)
|
||||
));
|
||||
|
||||
if (!isExpanded()) {
|
||||
expandAnimation.setOnFinished(e2 -> {
|
||||
updateClip(newHeight);
|
||||
animatedHeight = 0.0;
|
||||
});
|
||||
}
|
||||
|
||||
expandAnimation.play();
|
||||
});
|
||||
|
||||
expandedProperty().addListener((a, b, newValue) -> {
|
||||
expandIcon.setRotate(newValue ? 180 : 0);
|
||||
});
|
||||
|
||||
getChildren().setAll(holder);
|
||||
} else
|
||||
getChildren().setAll(content);
|
||||
}
|
||||
|
||||
public boolean isExpanded() {
|
||||
return expanded.get();
|
||||
}
|
||||
|
||||
public BooleanProperty expandedProperty() {
|
||||
return expanded;
|
||||
}
|
||||
|
||||
public void setExpanded(boolean expanded) {
|
||||
this.expanded.set(expanded);
|
||||
}
|
||||
}
|
||||
110
HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/FileItem.java
Normal file
110
HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/FileItem.java
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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 com.jfoenix.controls.JFXButton;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.stage.DirectoryChooser;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.SVG;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class FileItem extends BorderPane {
|
||||
private Property<String> property;
|
||||
private final Label x = new Label();
|
||||
|
||||
private final SimpleStringProperty name = new SimpleStringProperty(this, "name");
|
||||
private final SimpleStringProperty title = new SimpleStringProperty(this, "title");
|
||||
private final SimpleStringProperty tooltip = new SimpleStringProperty(this, "tooltip");
|
||||
|
||||
public FileItem() {
|
||||
VBox left = new VBox();
|
||||
Label name = new Label();
|
||||
name.textProperty().bind(nameProperty());
|
||||
x.getStyleClass().addAll("subtitle-label");
|
||||
left.getChildren().addAll(name, x);
|
||||
setLeft(left);
|
||||
|
||||
JFXButton right = new JFXButton();
|
||||
right.setGraphic(SVG.pencil("black", 15, 15));
|
||||
right.getStyleClass().add("toggle-icon4");
|
||||
right.setOnMouseClicked(e -> onExplore());
|
||||
setRight(right);
|
||||
|
||||
Tooltip tip = new Tooltip();
|
||||
tip.textProperty().bind(tooltipProperty());
|
||||
Tooltip.install(this, tip);
|
||||
}
|
||||
|
||||
public void onExplore() {
|
||||
DirectoryChooser chooser = new DirectoryChooser();
|
||||
chooser.titleProperty().bind(titleProperty());
|
||||
File selectedDir = chooser.showDialog(Controllers.INSTANCE.getStage());
|
||||
if (selectedDir != null)
|
||||
property.setValue(selectedDir.getAbsolutePath());
|
||||
chooser.titleProperty().unbind();
|
||||
}
|
||||
|
||||
public void setProperty(Property<String> property) {
|
||||
this.property = property;
|
||||
x.textProperty().bind(property);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name.get();
|
||||
}
|
||||
|
||||
public StringProperty nameProperty() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name.set(name);
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title.get();
|
||||
}
|
||||
|
||||
public StringProperty titleProperty() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title.set(title);
|
||||
}
|
||||
|
||||
public String getTooltip() {
|
||||
return tooltip.get();
|
||||
}
|
||||
|
||||
public StringProperty tooltipProperty() {
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
public void setTooltip(String tooltip) {
|
||||
this.tooltip.set(tooltip);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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 com.jfoenix.controls.JFXComboBox;
|
||||
import javafx.beans.NamedArg;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.text.Font;
|
||||
|
||||
public class FontComboBox extends JFXComboBox<String> {
|
||||
|
||||
public FontComboBox(@NamedArg(value = "fontSize", defaultValue = "12.0") double fontSize,
|
||||
@NamedArg(value = "enableStyle", defaultValue = "false") boolean enableStyle) {
|
||||
super(FXCollections.observableArrayList(Font.getFamilies()));
|
||||
|
||||
valueProperty().addListener((a, b, newValue) -> {
|
||||
if (enableStyle)
|
||||
setStyle("-fx-font-family: \"" + newValue + "\";");
|
||||
});
|
||||
|
||||
setCellFactory(listView -> new ListCell<String>() {
|
||||
@Override
|
||||
protected void updateItem(String item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (item != null) {
|
||||
setText(item);
|
||||
setFont(new Font(item, fontSize));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.HBox;
|
||||
|
||||
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.setAlignment(Pos.CENTER);
|
||||
textLabel.setMouseTransparent(true);
|
||||
hBox.getChildren().addAll(icon, textLabel);
|
||||
hBox.setStyle("-fx-padding: 10 16 10 16; -fx-spacing: 10; -fx-font-size: 14;");
|
||||
hBox.setAlignment(Pos.CENTER_LEFT);
|
||||
return hBox;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.control.MultipleSelectionModel;
|
||||
|
||||
public final class NoneMultipleSelectionModel<T> extends MultipleSelectionModel<T> {
|
||||
private static final NoneMultipleSelectionModel INSTANCE = new NoneMultipleSelectionModel();
|
||||
private NoneMultipleSelectionModel() {}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> NoneMultipleSelectionModel<T> getInstance() {
|
||||
return (NoneMultipleSelectionModel<T>) INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableList<Integer> getSelectedIndices() {
|
||||
return FXCollections.emptyObservableList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableList<T> getSelectedItems() {
|
||||
return FXCollections.emptyObservableList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectIndices(int index, int... indices) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectAll() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAndSelect(int index) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void select(int index) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void select(T obj) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearSelection(int index) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearSelection() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSelected(int index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectPrevious() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectNext() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectFirst() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectLast() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 com.jfoenix.validation.base.ValidatorBase;
|
||||
import javafx.scene.control.TextInputControl;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
|
||||
public class NumberValidator extends ValidatorBase {
|
||||
private boolean nullable;
|
||||
|
||||
public NumberValidator() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
public NumberValidator(boolean nullable) {
|
||||
this.nullable = nullable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void eval() {
|
||||
if (srcControl.get() instanceof TextInputControl) {
|
||||
evalTextInputField();
|
||||
}
|
||||
}
|
||||
|
||||
private void evalTextInputField() {
|
||||
TextInputControl textField = ((TextInputControl) srcControl.get());
|
||||
|
||||
if (StringUtils.isBlank(textField.getText()))
|
||||
hasErrors.set(false);
|
||||
else
|
||||
try {
|
||||
Integer.parseInt(textField.getText());
|
||||
hasErrors.set(false);
|
||||
} catch (NumberFormatException e) {
|
||||
hasErrors.set(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.download;
|
||||
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.jackhuang.hmcl.download.DownloadProvider;
|
||||
import org.jackhuang.hmcl.game.GameRepository;
|
||||
import org.jackhuang.hmcl.ui.FXUtilsKt;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardPage;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
class AdditionalInstallersPage extends StackPane implements WizardPage {
|
||||
private final InstallerWizardProvider provider;
|
||||
private final WizardController controller;
|
||||
private final GameRepository repository;
|
||||
private final DownloadProvider downloadProvider;
|
||||
|
||||
@FXML private VBox list;
|
||||
@FXML private JFXButton btnForge;
|
||||
@FXML private JFXButton btnLiteLoader;
|
||||
@FXML private JFXButton btnOptiFine;
|
||||
@FXML private Label lblGameVersion;
|
||||
@FXML private Label lblVersionName;
|
||||
@FXML private Label lblForge;
|
||||
@FXML private Label lblLiteLoader;
|
||||
@FXML
|
||||
private Label lblOptiFine;
|
||||
@FXML private JFXButton btnInstall;
|
||||
|
||||
public AdditionalInstallersPage(InstallerWizardProvider provider, WizardController controller, GameRepository repository, DownloadProvider downloadProvider) {
|
||||
this.provider = provider;
|
||||
this.controller = controller;
|
||||
this.repository = repository;
|
||||
this.downloadProvider = downloadProvider;
|
||||
|
||||
FXUtilsKt.loadFXML(this, "/assets/fxml/download/additional-installers.fxml");
|
||||
|
||||
lblGameVersion.setText(provider.getGameVersion());
|
||||
lblVersionName.setText(provider.getVersion().getId());
|
||||
|
||||
btnForge.setOnMouseClicked(e -> {
|
||||
controller.getSettings().put(INSTALLER_TYPE, 0);
|
||||
controller.onNext(new VersionsPage(controller, provider.getGameVersion(), downloadProvider, "forge", () -> { controller.onPrev(false); }));
|
||||
});
|
||||
|
||||
btnLiteLoader.setOnMouseClicked(e -> {
|
||||
controller.getSettings().put(INSTALLER_TYPE, 1);
|
||||
controller.onNext(new VersionsPage(controller, provider.getGameVersion(), downloadProvider, "liteloader", () -> { controller.onPrev(false); }));
|
||||
});
|
||||
|
||||
btnOptiFine.setOnMouseClicked(e -> {
|
||||
controller.getSettings().put(INSTALLER_TYPE, 2);
|
||||
controller.onNext(new VersionsPage(controller, provider.getGameVersion(), downloadProvider, "optifine", () -> { controller.onPrev(false); }));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return "Choose a game version";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNavigate(Map<String, Object> settings) {
|
||||
lblGameVersion.setText("Current Game Version: " + provider.getGameVersion());
|
||||
btnForge.setDisable(provider.getForge() != null);
|
||||
if (provider.getForge() != null || controller.getSettings().containsKey("forge"))
|
||||
lblForge.setText("Forge Versoin: " + Lang.nonNull(provider.getForge(), controller.getSettings().get("forge")));
|
||||
else
|
||||
lblForge.setText("Forge not installed");
|
||||
|
||||
btnLiteLoader.setDisable(provider.getLiteLoader() != null);
|
||||
if (provider.getLiteLoader() != null || controller.getSettings().containsKey("liteloader"))
|
||||
lblLiteLoader.setText("LiteLoader Versoin: " + Lang.nonNull(provider.getLiteLoader(), controller.getSettings().get("liteloader")));
|
||||
else
|
||||
lblLiteLoader.setText("LiteLoader not installed");
|
||||
|
||||
btnOptiFine.setDisable(provider.getOptiFine() != null);
|
||||
if (provider.getOptiFine() != null || controller.getSettings().containsKey("optifine"))
|
||||
lblOptiFine.setText("OptiFine Versoin: " + Lang.nonNull(provider.getOptiFine(), controller.getSettings().get("optifine")));
|
||||
else
|
||||
lblOptiFine.setText("OptiFine not installed");
|
||||
|
||||
}
|
||||
|
||||
@Override public void cleanup(Map<String, Object> settings) {
|
||||
settings.remove(INSTALLER_TYPE);
|
||||
}
|
||||
|
||||
public void onInstall() {
|
||||
controller.onFinish();
|
||||
}
|
||||
|
||||
public static final String INSTALLER_TYPE = "INSTALLER_TYPE";
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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.download;
|
||||
|
||||
import javafx.scene.Node;
|
||||
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardProvider;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public final class InstallerWizardProvider implements WizardProvider {
|
||||
private final Profile profile;
|
||||
private final String gameVersion;
|
||||
private final Version version;
|
||||
private final String forge;
|
||||
private final String liteLoader;
|
||||
private final String optiFine;
|
||||
|
||||
public InstallerWizardProvider(Profile profile, String gameVersion, Version version) {
|
||||
this(profile, gameVersion, version, null, null, null);
|
||||
}
|
||||
|
||||
public InstallerWizardProvider(Profile profile, String gameVersion, Version version, String forge, String liteLoader, String optiFine) {
|
||||
this.profile = profile;
|
||||
this.gameVersion = gameVersion;
|
||||
this.version = version;
|
||||
this.forge = forge;
|
||||
this.liteLoader = liteLoader;
|
||||
this.optiFine = optiFine;
|
||||
}
|
||||
|
||||
public Profile getProfile() {
|
||||
return profile;
|
||||
}
|
||||
|
||||
public String getGameVersion() {
|
||||
return gameVersion;
|
||||
}
|
||||
|
||||
public Version getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public String getForge() {
|
||||
return forge;
|
||||
}
|
||||
|
||||
public String getLiteLoader() {
|
||||
return liteLoader;
|
||||
}
|
||||
|
||||
public String getOptiFine() {
|
||||
return optiFine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Map<String, Object> settings) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object finish(Map<String, Object> settings) {
|
||||
Task ret = Task.empty();
|
||||
|
||||
if (settings.containsKey("forge"))
|
||||
ret = ret.with(profile.getDependency().installLibraryAsync(gameVersion, version, "forge", (String) settings.get("forge")));
|
||||
|
||||
if (settings.containsKey("liteloader"))
|
||||
ret = ret.with(profile.getDependency().installLibraryAsync(gameVersion, version, "liteloader", (String) settings.get("liteloader")));
|
||||
|
||||
if (settings.containsKey("optifine"))
|
||||
ret = ret.with(profile.getDependency().installLibraryAsync(gameVersion, version, "optifine", (String) settings.get("optifine")));
|
||||
|
||||
return ret.with(Task.of(v -> {
|
||||
profile.getRepository().refreshVersions();
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node createPage(WizardController controller, int step, Map<String, Object> settings) {
|
||||
switch (step) {
|
||||
case 0:
|
||||
return new AdditionalInstallersPage(this, controller, profile.getRepository(), BMCLAPIDownloadProvider.INSTANCE);
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.download;
|
||||
|
||||
import com.jfoenix.controls.JFXListView;
|
||||
import com.jfoenix.controls.JFXSpinner;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import org.jackhuang.hmcl.download.DownloadProvider;
|
||||
import org.jackhuang.hmcl.download.RemoteVersion;
|
||||
import org.jackhuang.hmcl.download.VersionList;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.TaskExecutor;
|
||||
import org.jackhuang.hmcl.ui.FXUtilsKt;
|
||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
||||
import org.jackhuang.hmcl.ui.animation.TransitionHandler;
|
||||
import org.jackhuang.hmcl.ui.wizard.Refreshable;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardPage;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
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;
|
||||
|
||||
@FXML
|
||||
private JFXListView<VersionsPageItem> list;
|
||||
@FXML private JFXSpinner spinner;
|
||||
|
||||
private final TransitionHandler transitionHandler = new TransitionHandler(this);
|
||||
private final VersionList<?> versionList;
|
||||
private TaskExecutor executor;
|
||||
|
||||
public VersionsPage(WizardController controller, String gameVersion, DownloadProvider downloadProvider, String libraryId, Runnable callback) {
|
||||
this.controller = controller;
|
||||
this.gameVersion = gameVersion;
|
||||
this.downloadProvider = downloadProvider;
|
||||
this.libraryId = libraryId;
|
||||
this.callback = callback;
|
||||
|
||||
this.versionList = downloadProvider.getVersionListById(libraryId);
|
||||
|
||||
FXUtilsKt.loadFXML(this, "/assets/fxml/download/versions.fxml");
|
||||
getChildren().setAll(spinner);
|
||||
list.getSelectionModel().selectedItemProperty().addListener((a, b, newValue) -> {
|
||||
controller.getSettings().put(libraryId, newValue.getRemoteVersion().getSelfVersion());
|
||||
callback.run();
|
||||
});
|
||||
refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
executor = versionList.refreshAsync(downloadProvider).subscribe(Schedulers.javafx(), v -> {
|
||||
versionList.getVersions(gameVersion).stream()
|
||||
.sorted(RemoteVersion.RemoteVersionComparator.INSTANCE)
|
||||
.forEach(version -> {
|
||||
list.getItems().add(new VersionsPageItem(version));
|
||||
});
|
||||
|
||||
transitionHandler.setContent(list, ContainerAnimations.FADE.getAnimationProducer());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return "Choose a game version";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup(Map<String, Object> settings) {
|
||||
settings.remove(libraryId);
|
||||
if (executor != null)
|
||||
executor.cancel();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.download;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import org.jackhuang.hmcl.download.RemoteVersion;
|
||||
import org.jackhuang.hmcl.ui.FXUtilsKt;
|
||||
|
||||
/**
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class VersionsPageItem extends StackPane {
|
||||
private final RemoteVersion<?> remoteVersion;
|
||||
@FXML
|
||||
private Label lblSelfVersion;
|
||||
@FXML
|
||||
private Label lblGameVersion;
|
||||
|
||||
public VersionsPageItem(RemoteVersion<?> remoteVersion) {
|
||||
this.remoteVersion = remoteVersion;
|
||||
|
||||
FXUtilsKt.loadFXML(this, "/assets/fxml/download/versions-list-item.fxml");
|
||||
lblSelfVersion.setText(remoteVersion.getSelfVersion());
|
||||
lblGameVersion.setText(remoteVersion.getGameVersion());
|
||||
}
|
||||
|
||||
public RemoteVersion<?> getRemoteVersion() {
|
||||
return remoteVersion;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.export;
|
||||
|
||||
import javafx.scene.Node;
|
||||
import kotlin.Suppress;
|
||||
import org.jackhuang.hmcl.game.HMCLModpackExportTask;
|
||||
import org.jackhuang.hmcl.mod.Modpack;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardProvider;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.jackhuang.hmcl.game.HMCLModpackManager.MODPACK_PREDICATE;
|
||||
|
||||
public final class ExportWizardProvider implements WizardProvider {
|
||||
private final Profile profile;
|
||||
private final String version;
|
||||
|
||||
public ExportWizardProvider(Profile profile, String version) {
|
||||
this.profile = profile;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Map<String, Object> settings) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object finish(Map<String, Object> settings) {
|
||||
return new HMCLModpackExportTask(profile.getRepository(), version, (List<String>) settings.get(ModpackFileSelectionPage.MODPACK_FILE_SELECTION),
|
||||
new Modpack(
|
||||
(String) settings.get(ModpackInfoPage.MODPACK_NAME),
|
||||
(String) settings.get(ModpackInfoPage.MODPACK_AUTHOR),
|
||||
(String) settings.get(ModpackInfoPage.MODPACK_VERSION),
|
||||
null,
|
||||
(String) settings.get(ModpackInfoPage.MODPACK_DESCRIPTION),
|
||||
null
|
||||
), (File) settings.get(ModpackInfoPage.MODPACK_FILE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node createPage(WizardController controller, int step, Map<String, Object> settings) {
|
||||
switch (step) {
|
||||
case 0: return new ModpackInfoPage(controller, version);
|
||||
case 1: return new ModpackFileSelectionPage(controller, profile, version, MODPACK_PREDICATE);
|
||||
default: throw new IllegalArgumentException("step");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* 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.export;
|
||||
|
||||
import com.jfoenix.controls.JFXTreeView;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.CheckBoxTreeItem;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TreeItem;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import org.jackhuang.hmcl.MainKt;
|
||||
import org.jackhuang.hmcl.game.ModAdviser;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
import org.jackhuang.hmcl.ui.FXUtilsKt;
|
||||
import org.jackhuang.hmcl.ui.construct.NoneMultipleSelectionModel;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardPage;
|
||||
import org.jackhuang.hmcl.util.FileUtils;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.Pair;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public final class ModpackFileSelectionPage extends StackPane implements WizardPage {
|
||||
private final WizardController controller;
|
||||
private final String version;
|
||||
private final ModAdviser adviser;
|
||||
@FXML
|
||||
private JFXTreeView<String> treeView;
|
||||
private CheckBoxTreeItem<String> rootNode;
|
||||
|
||||
public ModpackFileSelectionPage(WizardController controller, Profile profile, String version, ModAdviser adviser) {
|
||||
this.controller = controller;
|
||||
this.version = version;
|
||||
this.adviser = adviser;
|
||||
|
||||
FXUtilsKt.loadFXML(this, "/assets/fxml/modpack/selection.fxml");
|
||||
rootNode = getTreeItem(profile.getRepository().getRunDirectory(version), "minecraft");
|
||||
treeView.setRoot(rootNode);
|
||||
treeView.setSelectionModel(NoneMultipleSelectionModel.getInstance());
|
||||
}
|
||||
|
||||
private CheckBoxTreeItem<String> getTreeItem(File file, String basePath) {
|
||||
ModAdviser.ModSuggestion state = ModAdviser.ModSuggestion.SUGGESTED;
|
||||
if (basePath.length() > "minecraft/".length()) {
|
||||
state = adviser.advise(StringUtils.substringAfter(basePath, "minecraft/") + (file.isDirectory() ? "/" : ""), file.isDirectory());
|
||||
if (file.isFile() && Objects.equals(FileUtils.getNameWithoutExtension(file), version))
|
||||
state = ModAdviser.ModSuggestion.HIDDEN;
|
||||
if (file.isDirectory() && Objects.equals(file.getName(), version + "-natives"))
|
||||
state = ModAdviser.ModSuggestion.HIDDEN;
|
||||
if (state == ModAdviser.ModSuggestion.HIDDEN)
|
||||
return null;
|
||||
}
|
||||
|
||||
CheckBoxTreeItem<String> node = new CheckBoxTreeItem<>(StringUtils.substringAfterLast(basePath, "/"));
|
||||
if (state == ModAdviser.ModSuggestion.SUGGESTED)
|
||||
node.setSelected(true);
|
||||
|
||||
if (file.isDirectory()) {
|
||||
File[] files = file.listFiles();
|
||||
if (files != null)
|
||||
for (File it : files) {
|
||||
CheckBoxTreeItem<String> subNode = getTreeItem(it, basePath + "/" + it.getName());
|
||||
if (subNode != null) {
|
||||
node.setSelected(subNode.isSelected() || node.isSelected());
|
||||
if (!subNode.isSelected())
|
||||
node.setIndeterminate(true);
|
||||
node.getChildren().add(subNode);
|
||||
}
|
||||
}
|
||||
if (!node.isSelected()) node.setIndeterminate(false);
|
||||
|
||||
// Empty folder need not to be displayed.
|
||||
if (node.getChildren().isEmpty())
|
||||
return null;
|
||||
}
|
||||
|
||||
HBox graphic = new HBox();
|
||||
CheckBox checkBox = new CheckBox();
|
||||
checkBox.selectedProperty().bindBidirectional(node.selectedProperty());
|
||||
checkBox.indeterminateProperty().bindBidirectional(node.indeterminateProperty());
|
||||
graphic.getChildren().add(checkBox);
|
||||
|
||||
if (TRANSLATION.containsKey(basePath)) {
|
||||
Label comment = new Label();
|
||||
comment.setText(TRANSLATION.get(basePath));
|
||||
comment.setStyle("-fx-text-fill: gray;");
|
||||
comment.setMouseTransparent(true);
|
||||
graphic.getChildren().add(comment);
|
||||
}
|
||||
graphic.setPickOnBounds(false);
|
||||
node.setExpanded("minecraft".equals(basePath));
|
||||
node.setGraphic(graphic);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private void getFilesNeeded(CheckBoxTreeItem<String> node, String basePath, List<String> list) {
|
||||
if (node == null) return;
|
||||
if (node.isSelected()) {
|
||||
if (basePath.length() > "minecraft/".length())
|
||||
list.add(StringUtils.substringAfter(basePath, "minecraft/"));
|
||||
for (TreeItem<String> child : node.getChildren()) {
|
||||
if (child instanceof CheckBoxTreeItem)
|
||||
getFilesNeeded(((CheckBoxTreeItem<String>) child), basePath + "/" + child.getValue(), list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup(Map<String, Object> settings) {
|
||||
controller.getSettings().remove(MODPACK_FILE_SELECTION);
|
||||
}
|
||||
|
||||
public void onNext() {
|
||||
LinkedList<String> list = new LinkedList<>();
|
||||
getFilesNeeded(rootNode, "minecraft", list);
|
||||
controller.getSettings().put(MODPACK_FILE_SELECTION, list);
|
||||
controller.onFinish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return MainKt.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", MainKt.i18n("modpack.files.servers_dat")),
|
||||
new Pair<>("minecraft/saves", MainKt.i18n("modpack.files.saves")),
|
||||
new Pair<>("minecraft/mods", MainKt.i18n("modpack.files.mods")),
|
||||
new Pair<>("minecraft/config", MainKt.i18n("modpack.files.config")),
|
||||
new Pair<>("minecraft/liteconfig", MainKt.i18n("modpack.files.liteconfig")),
|
||||
new Pair<>("minecraft/resourcepacks", MainKt.i18n("modpack.files.resourcepacks")),
|
||||
new Pair<>("minecraft/resources", MainKt.i18n("modpack.files.resourcepacks")),
|
||||
new Pair<>("minecraft/options.txt", MainKt.i18n("modpack.files.options_txt")),
|
||||
new Pair<>("minecraft/optionsshaders.txt", MainKt.i18n("modpack.files.optionsshaders_txt")),
|
||||
new Pair<>("minecraft/mods/VoxelMods", MainKt.i18n("modpack.files.mods.voxelmods")),
|
||||
new Pair<>("minecraft/dumps", MainKt.i18n("modpack.files.dumps")),
|
||||
new Pair<>("minecraft/blueprints", MainKt.i18n("modpack.files.blueprints")),
|
||||
new Pair<>("minecraft/scripts", MainKt.i18n("modpack.files.scripts"))
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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.export;
|
||||
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import com.jfoenix.controls.JFXTextArea;
|
||||
import com.jfoenix.controls.JFXTextField;
|
||||
import com.jfoenix.controls.JFXToggleButton;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.stage.FileChooser;
|
||||
import org.jackhuang.hmcl.MainKt;
|
||||
import org.jackhuang.hmcl.auth.Account;
|
||||
import org.jackhuang.hmcl.setting.Settings;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.FXUtilsKt;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardPage;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public final class ModpackInfoPage extends StackPane implements WizardPage {
|
||||
private final WizardController controller;
|
||||
@FXML
|
||||
private Label lblVersionName;
|
||||
@FXML
|
||||
private JFXTextField txtModpackName;
|
||||
@FXML
|
||||
private JFXTextField txtModpackAuthor;@FXML
|
||||
private JFXTextField txtModpackVersion;@FXML
|
||||
private JFXTextArea txtModpackDescription;
|
||||
@FXML
|
||||
private JFXToggleButton chkIncludeLauncher;@FXML
|
||||
private JFXButton btnNext;@FXML
|
||||
private ScrollPane scroll;
|
||||
|
||||
public ModpackInfoPage(WizardController controller, String version) {
|
||||
this.controller = controller;
|
||||
FXUtilsKt.loadFXML(this, "/assets/fxml/modpack/info.fxml");
|
||||
FXUtilsKt.smoothScrolling(scroll);
|
||||
txtModpackName.setText(version);
|
||||
txtModpackName.textProperty().addListener(e -> checkValidation());
|
||||
txtModpackAuthor.textProperty().addListener(e -> checkValidation());
|
||||
txtModpackVersion.textProperty().addListener(e -> checkValidation());
|
||||
txtModpackAuthor.setText(Optional.ofNullable(Settings.INSTANCE.getSelectedAccount()).map(Account::getUsername).orElse(""));
|
||||
lblVersionName.setText(version);
|
||||
}
|
||||
|
||||
private void checkValidation() {
|
||||
btnNext.setDisable(!txtModpackName.validate() || !txtModpackVersion.validate() || !txtModpackAuthor.validate());
|
||||
}
|
||||
|
||||
public void onNext() {
|
||||
FileChooser fileChooser = new FileChooser();
|
||||
fileChooser.setTitle(MainKt.i18n("modpack.wizard.step.initialization.save"));
|
||||
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(MainKt.i18n("modpack"), "*.zip"));
|
||||
File file = fileChooser.showSaveDialog(Controllers.INSTANCE.getStage());
|
||||
if (file == null) {
|
||||
Controllers.INSTANCE.navigate(null);
|
||||
return;
|
||||
}
|
||||
controller.getSettings().put(MODPACK_NAME, txtModpackName.getText());
|
||||
controller.getSettings().put(MODPACK_VERSION, txtModpackVersion.getText());
|
||||
controller.getSettings().put(MODPACK_AUTHOR, txtModpackAuthor.getText());
|
||||
controller.getSettings().put(MODPACK_FILE, file);
|
||||
controller.getSettings().put(MODPACK_DESCRIPTION, txtModpackDescription.getText());
|
||||
controller.getSettings().put(MODPACK_INCLUDE_LAUNCHER, chkIncludeLauncher.isSelected());
|
||||
controller.onNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup(Map<String, Object> settings) {
|
||||
controller.getSettings().remove(MODPACK_NAME);
|
||||
controller.getSettings().remove(MODPACK_VERSION);
|
||||
controller.getSettings().remove(MODPACK_AUTHOR);
|
||||
controller.getSettings().remove(MODPACK_DESCRIPTION);
|
||||
controller.getSettings().remove(MODPACK_INCLUDE_LAUNCHER);
|
||||
controller.getSettings().remove(MODPACK_FILE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return MainKt.i18n("modpack.wizard.step.1.title");
|
||||
}
|
||||
|
||||
public static final String MODPACK_NAME = "modpack.name";
|
||||
public static final String MODPACK_VERSION = "modpack.version";
|
||||
public static final String MODPACK_AUTHOR = "modpack.author";
|
||||
public static final String MODPACK_DESCRIPTION = "modpack.description";
|
||||
public static final String MODPACK_INCLUDE_LAUNCHER = "modpack.include_launcher";
|
||||
public static final String MODPACK_FILE = "modpack.file";
|
||||
}
|
||||
@@ -15,12 +15,13 @@
|
||||
* 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.wizard
|
||||
package org.jackhuang.hmcl.ui.wizard;
|
||||
|
||||
import javafx.beans.property.StringProperty
|
||||
import javafx.beans.property.StringProperty;
|
||||
|
||||
interface DecoratorPage {
|
||||
val titleProperty: StringProperty
|
||||
public interface DecoratorPage {
|
||||
StringProperty titleProperty();
|
||||
|
||||
fun onClose() {}
|
||||
default void onClose() {
|
||||
}
|
||||
}
|
||||
@@ -15,22 +15,26 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui
|
||||
package org.jackhuang.hmcl.ui.wizard;
|
||||
|
||||
import com.jfoenix.controls.JFXProgressBar
|
||||
import javafx.fxml.FXML
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.layout.StackPane
|
||||
import java.util.Map;
|
||||
|
||||
class LaunchingStepsPane(): StackPane() {
|
||||
@FXML lateinit var pgsTasks: JFXProgressBar
|
||||
@FXML lateinit var lblCurrentState: Label
|
||||
@FXML lateinit var lblSteps: Label
|
||||
init {
|
||||
loadFXML("/assets/fxml/launching-steps.fxml")
|
||||
/**
|
||||
* @author huangyuhui
|
||||
*/
|
||||
public abstract class DeferredWizardResult {
|
||||
private final boolean canAbort;
|
||||
|
||||
limitHeight(200.0)
|
||||
limitWidth(400.0)
|
||||
public DeferredWizardResult() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
public DeferredWizardResult(boolean canAbort) {
|
||||
this.canAbort = canAbort;
|
||||
}
|
||||
|
||||
public abstract void start(Map<String, Object> settings, ResultProgressHandle progressHandle);
|
||||
|
||||
public void abort() {
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.wizard
|
||||
package org.jackhuang.hmcl.ui.wizard;
|
||||
|
||||
/**
|
||||
* A controller for the progress bar shown in the user interface. Used in
|
||||
@@ -24,21 +24,21 @@ package org.jackhuang.hmcl.ui.wizard
|
||||
* will take a while and needs to happen on a background thread.
|
||||
* @author Tim Boudreau
|
||||
*/
|
||||
interface ResultProgressHandle {
|
||||
public interface ResultProgressHandle {
|
||||
|
||||
/**
|
||||
* Set the current position and total number of steps. Note it is
|
||||
* inadvisable to be holding any locks when calling this method, as it
|
||||
* may immediately update the GUI using
|
||||
* `EventQueue.invokeAndWait()`.
|
||||
|
||||
*
|
||||
* @param currentStep the current step in the progress of computing the
|
||||
* * result.
|
||||
* *
|
||||
* @param totalSteps the total number of steps. Must be greater than
|
||||
* * or equal to currentStep.
|
||||
*/
|
||||
fun setProgress(currentStep: Int, totalSteps: Int)
|
||||
void setProgress(int currentStep, int totalSteps);
|
||||
|
||||
/**
|
||||
* Set the current position and total number of steps, and description
|
||||
@@ -55,7 +55,7 @@ interface ResultProgressHandle {
|
||||
* @param totalSteps the total number of steps. Must be greater than
|
||||
* * or equal to currentStep.
|
||||
*/
|
||||
fun setProgress(description: String, currentStep: Int, totalSteps: Int)
|
||||
void setProgress(String description, int currentStep, int totalSteps);
|
||||
|
||||
/**
|
||||
* Set the status as "busy" - a rotating icon will be displayed instead
|
||||
@@ -67,7 +67,7 @@ interface ResultProgressHandle {
|
||||
* @param description Text to describe what is being done, which can
|
||||
* * be displayed in the UI.
|
||||
*/
|
||||
fun setBusy(description: String)
|
||||
void setBusy(String description);
|
||||
|
||||
/**
|
||||
* Call this method when the computation is complete, and pass in the
|
||||
@@ -78,7 +78,7 @@ interface ResultProgressHandle {
|
||||
* called, a runtime exception may be thrown.
|
||||
* @param result the Object which was computed, if any.
|
||||
*/
|
||||
fun finished(result: Any)
|
||||
void finished(Object result);
|
||||
|
||||
/**
|
||||
* Call this method if computation fails. The message may be some text
|
||||
@@ -93,12 +93,12 @@ interface ResultProgressHandle {
|
||||
* @param canNavigateBack whether or not the Prev button should be
|
||||
* * enabled.
|
||||
*/
|
||||
fun failed(message: String, canNavigateBack: Boolean)
|
||||
void failed(String message, boolean canNavigateBack);
|
||||
|
||||
/**
|
||||
* Returns true if the computation is still running, i.e., if neither finished or failed have been called.
|
||||
|
||||
*
|
||||
* @return true if there is no result yet.
|
||||
*/
|
||||
val isRunning: Boolean
|
||||
boolean isRunning();
|
||||
}
|
||||
@@ -15,19 +15,18 @@
|
||||
* 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.wizard
|
||||
package org.jackhuang.hmcl.ui.wizard;
|
||||
|
||||
import javafx.scene.Node
|
||||
import org.jackhuang.hmcl.task.Task
|
||||
import javafx.scene.Node;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
|
||||
interface WizardDisplayer {
|
||||
fun onStart()
|
||||
fun onEnd()
|
||||
fun onCancel()
|
||||
import java.util.Map;
|
||||
|
||||
fun navigateTo(page: Node, nav: Navigation.NavigationDirection)
|
||||
|
||||
fun handleDeferredWizardResult(settings: Map<String, Any>, deferredResult: DeferredWizardResult)
|
||||
|
||||
fun handleTask(settings: Map<String, Any>, task: Task)
|
||||
public interface WizardDisplayer {
|
||||
void onStart();
|
||||
void onEnd();
|
||||
void onCancel();
|
||||
void navigateTo(Node page, Navigation.NavigationDirection nav);
|
||||
void handleDeferredWizardResult(Map<String, Object> settings, DeferredWizardResult deferredWizardResult);
|
||||
void handleTask(Map<String, Object> settings, Task task);
|
||||
}
|
||||
@@ -15,15 +15,21 @@
|
||||
* 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.wizard
|
||||
package org.jackhuang.hmcl.ui.wizard;
|
||||
|
||||
enum class WizardNavigationResult {
|
||||
public enum WizardNavigationResult {
|
||||
PROCEED {
|
||||
override val deferredComputation = false
|
||||
@Override
|
||||
public boolean getDeferredComputation() {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
DENY {
|
||||
override val deferredComputation = false
|
||||
@Override
|
||||
public boolean getDeferredComputation() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
abstract val deferredComputation: Boolean
|
||||
public abstract boolean getDeferredComputation();
|
||||
}
|
||||
@@ -15,10 +15,15 @@
|
||||
* 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.wizard
|
||||
package org.jackhuang.hmcl.ui.wizard;
|
||||
|
||||
abstract class DeferredWizardResult(val canAbort: Boolean = false) {
|
||||
import java.util.Map;
|
||||
|
||||
abstract fun start(settings: Map<String, Any>, progressHandle: ResultProgressHandle)
|
||||
open fun abort() { }
|
||||
public interface WizardPage {
|
||||
default void onNavigate(Map<String, Object> settings) {
|
||||
}
|
||||
|
||||
void cleanup(Map<String, Object> settings);
|
||||
|
||||
String getTitle();
|
||||
}
|
||||
@@ -15,14 +15,15 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui
|
||||
package org.jackhuang.hmcl.ui.wizard;
|
||||
|
||||
import javafx.util.StringConverter
|
||||
import javafx.scene.Node;
|
||||
|
||||
class SafeIntStringConverter : StringConverter<Int>() {
|
||||
/** {@inheritDoc} */
|
||||
override fun fromString(value: String?) = value?.toIntOrNull()
|
||||
import java.util.Map;
|
||||
|
||||
/** {@inheritDoc} */
|
||||
override fun toString(value: Int?) = value?.toString() ?: ""
|
||||
public interface WizardProvider {
|
||||
void start(Map<String, Object> settings);
|
||||
Object finish(Map<String, Object> settings);
|
||||
Node createPage(WizardController controller, int step, Map<String, Object> settings);
|
||||
boolean cancel();
|
||||
}
|
||||
@@ -97,6 +97,6 @@ class Main : Application() {
|
||||
Schedulers.shutdown()
|
||||
}
|
||||
|
||||
val RESOURCE_BUNDLE = Settings.locale.resourceBundle
|
||||
val RESOURCE_BUNDLE = Settings.INSTANCE.locale.resourceBundle
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.setting
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import org.jackhuang.hmcl.Main
|
||||
import org.jackhuang.hmcl.util.JavaVersion
|
||||
import java.util.*
|
||||
|
||||
class Config {
|
||||
@SerializedName("last")
|
||||
var selectedProfile: String = ""
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("bgpath")
|
||||
var bgpath: String? = null
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("commonpath")
|
||||
var commonpath: String = Main.getMinecraftDirectory().absolutePath
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("proxyType")
|
||||
var proxyType: Int = 0
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("proxyHost")
|
||||
var proxyHost: String? = null
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("proxyPort")
|
||||
var proxyPort: String? = null
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("proxyUserName")
|
||||
var proxyUserName: String? = null
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("proxyPassword")
|
||||
var proxyPassword: String? = null
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("theme")
|
||||
var theme: String? = null
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("java")
|
||||
var java: List<JavaVersion>? = null
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("localization")
|
||||
var localization: String? = null
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("downloadtype")
|
||||
var downloadtype: Int = 0
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("configurations")
|
||||
var configurations: MutableMap<String, Profile> = TreeMap()
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("accounts")
|
||||
var accounts: MutableMap<String, MutableMap<Any, Any>> = TreeMap()
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("selectedAccount")
|
||||
var selectedAccount: String = ""
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("fontFamily")
|
||||
var fontFamily: String? = "Consolas"
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("fontSize")
|
||||
var fontSize: Double = 12.0
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
@SerializedName("logLines")
|
||||
var logLines: Int = 100
|
||||
set(value) {
|
||||
field = value
|
||||
Settings.save()
|
||||
}
|
||||
}
|
||||
@@ -1,349 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.setting
|
||||
|
||||
import com.google.gson.GsonBuilder
|
||||
import javafx.beans.InvalidationListener
|
||||
import javafx.scene.text.Font
|
||||
import org.jackhuang.hmcl.Main
|
||||
import org.jackhuang.hmcl.ProfileChangedEvent
|
||||
import org.jackhuang.hmcl.ProfileLoadingEvent
|
||||
import org.jackhuang.hmcl.auth.Account
|
||||
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider
|
||||
import org.jackhuang.hmcl.download.DownloadProvider
|
||||
import org.jackhuang.hmcl.download.MojangDownloadProvider
|
||||
import org.jackhuang.hmcl.event.EventBus
|
||||
import org.jackhuang.hmcl.task.Schedulers
|
||||
import org.jackhuang.hmcl.util.*
|
||||
import org.jackhuang.hmcl.util.Logging.LOG
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.net.Authenticator
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.PasswordAuthentication
|
||||
import java.net.Proxy
|
||||
import java.util.*
|
||||
import java.util.logging.Level
|
||||
|
||||
object Settings {
|
||||
val GSON = GsonBuilder()
|
||||
.registerTypeAdapter(VersionSetting::class.java, VersionSetting.Serializer.INSTANCE)
|
||||
.registerTypeAdapter(Profile::class.java, Profile.Serializer.INSTANCE)
|
||||
.registerTypeAdapter(File::class.java, FileTypeAdapter.INSTANCE)
|
||||
.setPrettyPrinting().create()
|
||||
|
||||
const val DEFAULT_PROFILE = "Default"
|
||||
const val HOME_PROFILE = "Home"
|
||||
|
||||
val SETTINGS_FILE: File = File("hmcl.json").absoluteFile
|
||||
|
||||
private val SETTINGS: Config
|
||||
|
||||
private val accounts = mutableMapOf<String, Account>()
|
||||
|
||||
init {
|
||||
SETTINGS = initSettings();
|
||||
|
||||
loop@for ((name, settings) in SETTINGS.accounts) {
|
||||
val factory = Accounts.ACCOUNT_FACTORY[settings["type"] ?: ""]
|
||||
if (factory == null) {
|
||||
SETTINGS.accounts.remove(name)
|
||||
continue@loop
|
||||
}
|
||||
|
||||
val account: Account
|
||||
try {
|
||||
account = factory.fromStorage(settings)
|
||||
} catch (e: Exception) {
|
||||
SETTINGS.accounts.remove(name)
|
||||
// storage is malformed, delete.
|
||||
continue@loop
|
||||
}
|
||||
|
||||
if (account.username != name) {
|
||||
SETTINGS.accounts.remove(name)
|
||||
continue
|
||||
}
|
||||
|
||||
accounts[name] = account
|
||||
}
|
||||
|
||||
save()
|
||||
|
||||
if (!getProfileMap().containsKey(DEFAULT_PROFILE))
|
||||
getProfileMap().put(DEFAULT_PROFILE, Profile());
|
||||
|
||||
for ((name, profile) in getProfileMap().entries) {
|
||||
profile.name = name
|
||||
profile.addPropertyChangedListener(InvalidationListener { save() })
|
||||
}
|
||||
|
||||
ignoreException {
|
||||
Runtime.getRuntime().addShutdownHook(Thread(this::save))
|
||||
}
|
||||
}
|
||||
|
||||
private fun initSettings(): Config {
|
||||
var c = Config()
|
||||
if (SETTINGS_FILE.exists())
|
||||
try {
|
||||
val str = SETTINGS_FILE.readText()
|
||||
if (str.trim() == "")
|
||||
LOG.finer("Settings file is empty, use the default settings.")
|
||||
else {
|
||||
val d = GSON.fromJson(str, Config::class.java)
|
||||
if (d != null)
|
||||
c = d
|
||||
}
|
||||
LOG.finest("Initialized settings.")
|
||||
} catch (e: Exception) {
|
||||
LOG.log(Level.WARNING, "Something happened wrongly when load settings.", e)
|
||||
}
|
||||
else {
|
||||
LOG.config("No settings file here, may be first loading.")
|
||||
if (!c.configurations.containsKey(HOME_PROFILE))
|
||||
c.configurations[HOME_PROFILE] = Profile(HOME_PROFILE, Main.getMinecraftDirectory())
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
fun save() {
|
||||
try {
|
||||
SETTINGS.accounts.clear()
|
||||
for ((name, account) in accounts) {
|
||||
val storage = account.toStorage()
|
||||
storage["type"] = Accounts.getAccountType(account)
|
||||
SETTINGS.accounts[name] = storage
|
||||
}
|
||||
|
||||
SETTINGS_FILE.writeText(GSON.toJson(SETTINGS))
|
||||
} catch (ex: IOException) {
|
||||
LOG.log(Level.SEVERE, "Failed to save config", ex)
|
||||
}
|
||||
}
|
||||
|
||||
val commonPathProperty = object : ImmediateStringProperty(this, "commonPath", SETTINGS.commonpath) {
|
||||
override fun invalidated() {
|
||||
super.invalidated()
|
||||
|
||||
SETTINGS.commonpath = get()
|
||||
}
|
||||
}
|
||||
var commonPath: String by commonPathProperty
|
||||
|
||||
var locale: Locales.SupportedLocale = Locales.getLocaleByName(SETTINGS.localization)
|
||||
set(value) {
|
||||
field = value
|
||||
SETTINGS.localization = Locales.getNameByLocale(value)
|
||||
}
|
||||
|
||||
var proxy: Proxy = Proxy.NO_PROXY
|
||||
var proxyType: Proxy.Type? = Proxies.getProxyType(SETTINGS.proxyType)
|
||||
set(value) {
|
||||
field = value
|
||||
SETTINGS.proxyType = Proxies.PROXIES.indexOf(value)
|
||||
loadProxy()
|
||||
}
|
||||
|
||||
var proxyHost: String? get() = SETTINGS.proxyHost; set(value) { SETTINGS.proxyHost = value }
|
||||
var proxyPort: String? get() = SETTINGS.proxyPort; set(value) { SETTINGS.proxyPort = value }
|
||||
var proxyUser: String? get() = SETTINGS.proxyUserName; set(value) { SETTINGS.proxyUserName = value }
|
||||
var proxyPass: String? get() = SETTINGS.proxyPassword; set(value) { SETTINGS.proxyPassword = value }
|
||||
|
||||
private fun loadProxy() {
|
||||
val host = proxyHost
|
||||
val port = proxyPort?.toIntOrNull()
|
||||
if (host == null || host.isBlank() || port == null)
|
||||
proxy = Proxy.NO_PROXY
|
||||
else {
|
||||
System.setProperty("http.proxyHost", proxyHost)
|
||||
System.setProperty("http.proxyPort", proxyPort)
|
||||
if (proxyType == Proxy.Type.DIRECT)
|
||||
proxy = Proxy.NO_PROXY
|
||||
else
|
||||
proxy = Proxy(proxyType, InetSocketAddress(host, port))
|
||||
|
||||
val user = proxyUser
|
||||
val pass = proxyPass
|
||||
if (user != null && user.isNotBlank() && pass != null && pass.isNotBlank()) {
|
||||
System.setProperty("http.proxyUser", user)
|
||||
System.setProperty("http.proxyPassword", pass)
|
||||
|
||||
Authenticator.setDefault(object : Authenticator() {
|
||||
override fun getPasswordAuthentication(): PasswordAuthentication {
|
||||
return PasswordAuthentication(user, pass.toCharArray())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init { loadProxy() }
|
||||
|
||||
var font: Font
|
||||
get() = Font.font(SETTINGS.fontFamily, SETTINGS.fontSize)
|
||||
set(value) {
|
||||
SETTINGS.fontFamily = value.family
|
||||
SETTINGS.fontSize = value.size
|
||||
}
|
||||
|
||||
var logLines: Int
|
||||
get() = maxOf(SETTINGS.logLines, 100)
|
||||
set(value) {
|
||||
SETTINGS.logLines = value
|
||||
}
|
||||
|
||||
var downloadProvider: DownloadProvider
|
||||
get() = when (SETTINGS.downloadtype) {
|
||||
0 -> MojangDownloadProvider.INSTANCE
|
||||
1 -> BMCLAPIDownloadProvider.INSTANCE
|
||||
else -> MojangDownloadProvider.INSTANCE
|
||||
}
|
||||
set(value) {
|
||||
SETTINGS.downloadtype = when (value) {
|
||||
MojangDownloadProvider.INSTANCE -> 0
|
||||
BMCLAPIDownloadProvider.INSTANCE -> 1
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************
|
||||
* ACCOUNTS *
|
||||
****************************************/
|
||||
|
||||
val selectedAccountProperty = object : ImmediateObjectProperty<Account?>(this, "selectedAccount", getAccount(SETTINGS.selectedAccount)) {
|
||||
override fun get(): Account? {
|
||||
val a = super.get()
|
||||
if (a == null || !accounts.containsKey(a.username)) {
|
||||
val acc = if (accounts.isEmpty()) null else accounts.values.first()
|
||||
set(acc)
|
||||
return acc
|
||||
} else return a
|
||||
}
|
||||
|
||||
override fun set(newValue: Account?) {
|
||||
if (newValue == null || accounts.containsKey(newValue.username)) {
|
||||
super.set(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
override fun invalidated() {
|
||||
super.invalidated()
|
||||
|
||||
SETTINGS.selectedAccount = value?.username ?: ""
|
||||
}
|
||||
}
|
||||
var selectedAccount: Account? by selectedAccountProperty
|
||||
|
||||
fun addAccount(account: Account) {
|
||||
accounts[account.username] = account
|
||||
}
|
||||
|
||||
fun getAccount(name: String): Account? {
|
||||
return accounts[name]
|
||||
}
|
||||
|
||||
fun getAccounts(): Map<String, Account> {
|
||||
return Collections.unmodifiableMap(accounts)
|
||||
}
|
||||
|
||||
fun deleteAccount(name: String) {
|
||||
accounts.remove(name)
|
||||
|
||||
selectedAccountProperty.get()
|
||||
}
|
||||
|
||||
/****************************************
|
||||
* PROFILES *
|
||||
****************************************/
|
||||
|
||||
var selectedProfile: Profile
|
||||
get() {
|
||||
if (!hasProfile(SETTINGS.selectedProfile)) {
|
||||
SETTINGS.selectedProfile = DEFAULT_PROFILE
|
||||
Schedulers.computation().schedule { onProfileChanged() }
|
||||
}
|
||||
return getProfile(SETTINGS.selectedProfile)
|
||||
}
|
||||
set(value) {
|
||||
if (hasProfile(value.name) && value.name != SETTINGS.selectedProfile) {
|
||||
SETTINGS.selectedProfile = value.name
|
||||
Schedulers.computation().schedule { onProfileChanged() }
|
||||
}
|
||||
}
|
||||
|
||||
fun getProfile(name: String?): Profile {
|
||||
var p: Profile? = getProfileMap()[name ?: DEFAULT_PROFILE]
|
||||
if (p == null)
|
||||
if (getProfileMap().containsKey(DEFAULT_PROFILE))
|
||||
p = getProfileMap()[DEFAULT_PROFILE]!!
|
||||
else {
|
||||
p = Profile()
|
||||
getProfileMap().put(DEFAULT_PROFILE, p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
fun hasProfile(name: String?): Boolean {
|
||||
return getProfileMap().containsKey(name ?: DEFAULT_PROFILE)
|
||||
}
|
||||
|
||||
fun getProfileMap(): MutableMap<String, Profile> {
|
||||
return SETTINGS.configurations
|
||||
}
|
||||
|
||||
fun getProfiles(): Collection<Profile> {
|
||||
return getProfileMap().values.filter { t -> t.name.isNotBlank() }
|
||||
}
|
||||
|
||||
fun putProfile(ver: Profile?): Boolean {
|
||||
if (ver == null || ver.name.isBlank() || getProfileMap().containsKey(ver.name))
|
||||
return false
|
||||
getProfileMap().put(ver.name, ver)
|
||||
return true
|
||||
}
|
||||
|
||||
fun deleteProfile(ver: Profile): Boolean {
|
||||
return deleteProfile(ver.name)
|
||||
}
|
||||
|
||||
fun deleteProfile(ver: String): Boolean {
|
||||
if (DEFAULT_PROFILE == ver) {
|
||||
return false
|
||||
}
|
||||
val flag = getProfileMap().remove(ver) != null
|
||||
if (flag)
|
||||
Schedulers.computation().schedule { onProfileLoading() }
|
||||
|
||||
return flag
|
||||
}
|
||||
|
||||
internal fun onProfileChanged() {
|
||||
selectedProfile.repository.refreshVersions()
|
||||
EventBus.EVENT_BUS.fireEvent(ProfileChangedEvent(SETTINGS, selectedProfile))
|
||||
}
|
||||
|
||||
/**
|
||||
* Start profiles loading process.
|
||||
* Invoked by loading GUI phase.
|
||||
*/
|
||||
fun onProfileLoading() {
|
||||
EventBus.EVENT_BUS.fireEvent(ProfileLoadingEvent(SETTINGS))
|
||||
onProfileChanged()
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ class AccountItem(i: Int, val account: Account, group: ToggleGroup) : StackPane(
|
||||
icon.translateYProperty().bind(Bindings.createDoubleBinding(Callable { header.boundsInParent.height - icon.height / 2 - 32.0 }, header.boundsInParentProperty(), icon.heightProperty()))
|
||||
|
||||
chkSelected.properties["account"] = account
|
||||
chkSelected.isSelected = Settings.selectedAccount == account
|
||||
chkSelected.isSelected = Settings.INSTANCE.selectedAccount == account
|
||||
lblUser.text = account.username
|
||||
lblType.text = accountType(account)
|
||||
|
||||
|
||||
@@ -44,7 +44,8 @@ import org.jackhuang.hmcl.util.onChangeAndOperate
|
||||
import org.jackhuang.hmcl.util.taskResult
|
||||
|
||||
class AccountsPage() : StackPane(), DecoratorPage {
|
||||
override val titleProperty: StringProperty = SimpleStringProperty(this, "title", "Accounts")
|
||||
private val titleProperty: StringProperty = SimpleStringProperty(this, "title", "Accounts")
|
||||
override fun titleProperty() = titleProperty
|
||||
|
||||
@FXML lateinit var scrollPane: ScrollPane
|
||||
@FXML lateinit var masonryPane: JFXMasonryPane
|
||||
@@ -74,7 +75,7 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
||||
txtPassword.setOnAction { onCreationAccept() }
|
||||
txtUsername.setOnAction { onCreationAccept() }
|
||||
|
||||
Settings.selectedAccountProperty.onChangeAndOperate { account ->
|
||||
Settings.INSTANCE.selectedAccountProperty().onChangeAndOperate { account ->
|
||||
masonryPane.children.forEach { node ->
|
||||
if (node is AccountItem) {
|
||||
node.chkSelected.isSelected = account?.username == node.lblUser.text
|
||||
@@ -84,7 +85,7 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
||||
|
||||
loadAccounts()
|
||||
|
||||
if (Settings.getAccounts().isEmpty())
|
||||
if (Settings.INSTANCE.getAccounts().isEmpty())
|
||||
addNewAccount()
|
||||
}
|
||||
|
||||
@@ -92,12 +93,12 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
||||
val children = mutableListOf<Node>()
|
||||
var i = 0
|
||||
val group = ToggleGroup()
|
||||
for ((_, account) in Settings.getAccounts()) {
|
||||
for ((_, account) in Settings.INSTANCE.getAccounts()) {
|
||||
children += buildNode(++i, account, group)
|
||||
}
|
||||
group.selectedToggleProperty().onChange {
|
||||
if (it != null)
|
||||
Settings.selectedAccount = it.properties["account"] as Account
|
||||
Settings.INSTANCE.selectedAccount = it.properties["account"] as Account
|
||||
}
|
||||
masonryPane.resetChildren(children)
|
||||
Platform.runLater {
|
||||
@@ -109,7 +110,7 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
||||
private fun buildNode(i: Int, account: Account, group: ToggleGroup): Node {
|
||||
return AccountItem(i, account, group).apply {
|
||||
btnDelete.setOnMouseClicked {
|
||||
Settings.deleteAccount(account.username)
|
||||
Settings.INSTANCE.deleteAccount(account.username)
|
||||
Platform.runLater(this@AccountsPage::loadAccounts)
|
||||
}
|
||||
}
|
||||
@@ -135,7 +136,7 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
||||
else -> throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
account.logIn(HMCLMultiCharacterSelector.INSTANCE, Settings.proxy)
|
||||
account.logIn(HMCLMultiCharacterSelector.INSTANCE, Settings.INSTANCE.proxy)
|
||||
account
|
||||
} catch (e: Exception) {
|
||||
e
|
||||
@@ -143,7 +144,7 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
||||
}.subscribe(Schedulers.javafx()) {
|
||||
val account: Any = it["create_account"]
|
||||
if (account is Account) {
|
||||
Settings.addAccount(account)
|
||||
Settings.INSTANCE.addAccount(account)
|
||||
dialog.close()
|
||||
loadAccounts()
|
||||
} else if (account is InvalidCredentialsException) {
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui
|
||||
|
||||
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
|
||||
|
||||
class ClassTitle(val text: String) : StackPane() {
|
||||
|
||||
init {
|
||||
val vbox = VBox()
|
||||
vbox.children += Text(text).apply {
|
||||
}
|
||||
vbox.children += Rectangle().apply {
|
||||
widthProperty().bind(vbox.widthProperty())
|
||||
height = 1.0
|
||||
fill = Color.GRAY
|
||||
}
|
||||
children.setAll(vbox)
|
||||
styleClass += "class-title"
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ object Controllers {
|
||||
decorator.showPage(null)
|
||||
leftPaneController = LeftPaneController(decorator.leftPane)
|
||||
|
||||
Settings.onProfileLoading()
|
||||
Settings.INSTANCE.onProfileLoading()
|
||||
task { JavaVersion.initialize() }.start()
|
||||
|
||||
decorator.isCustomMaximize = false
|
||||
|
||||
@@ -388,7 +388,7 @@ class Decorator @JvmOverloads constructor(private val primaryStage: Stage, priva
|
||||
titleLabel.text = prefix + content.title
|
||||
|
||||
if (content is DecoratorPage)
|
||||
titleLabel.textProperty().bind(content.titleProperty)
|
||||
titleLabel.textProperty().bind(content.titleProperty())
|
||||
}
|
||||
|
||||
var category: String? = null
|
||||
@@ -418,6 +418,7 @@ class Decorator @JvmOverloads constructor(private val primaryStage: Stage, priva
|
||||
return dialog
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun startWizard(wizardProvider: WizardProvider, category: String? = null) {
|
||||
this.category = category
|
||||
wizardController.provider = wizardProvider
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui
|
||||
|
||||
import org.jackhuang.hmcl.auth.Account
|
||||
import org.jackhuang.hmcl.auth.AuthInfo
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
||||
import org.jackhuang.hmcl.task.SilentException
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
object DialogController {
|
||||
|
||||
fun logIn(account: Account): AuthInfo? {
|
||||
if (account is YggdrasilAccount) {
|
||||
val latch = CountDownLatch(1)
|
||||
val res = AtomicReference<AuthInfo>(null)
|
||||
runOnUiThread {
|
||||
val pane = YggdrasilAccountLoginPane(account, success = {
|
||||
res.set(it)
|
||||
latch.countDown()
|
||||
Controllers.closeDialog()
|
||||
}, failed = {
|
||||
latch.countDown()
|
||||
Controllers.closeDialog()
|
||||
})
|
||||
pane.dialog = Controllers.dialog(pane)
|
||||
}
|
||||
latch.await()
|
||||
return res.get() ?: throw SilentException()
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,8 @@ import javafx.util.Duration
|
||||
import org.jackhuang.hmcl.Main
|
||||
import org.jackhuang.hmcl.util.*
|
||||
import org.jackhuang.hmcl.util.Logging.LOG
|
||||
import org.jackhuang.hmcl.util.ReflectionHelper.call
|
||||
import org.jackhuang.hmcl.util.ReflectionHelper.construct
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.logging.Level
|
||||
@@ -178,7 +180,7 @@ fun unbindEnum(comboBox: JFXComboBox<*>) {
|
||||
* return value of `interpolate()` is `endValue` only when the
|
||||
* input `fraction` is 1.0, and `startValue` otherwise.
|
||||
*/
|
||||
val SINE: Interpolator = object : Interpolator() {
|
||||
@JvmField val SINE: Interpolator = object : Interpolator() {
|
||||
override fun curve(t: Double): Double {
|
||||
return Math.sin(t * Math.PI / 2)
|
||||
}
|
||||
@@ -234,9 +236,8 @@ fun inputDialog(title: String, contentText: String, headerText: String? = null,
|
||||
|
||||
fun Node.installTooltip(openDelay: Double = 1000.0, visibleDelay: Double = 5000.0, closeDelay: Double = 200.0, tooltip: Tooltip) {
|
||||
try {
|
||||
Class.forName("javafx.scene.control.Tooltip\$TooltipBehavior")
|
||||
.construct(Duration(openDelay), Duration(visibleDelay), Duration(closeDelay), false)!!
|
||||
.call("install", this, tooltip)
|
||||
call(construct(Class.forName("javafx.scene.control.Tooltip\$TooltipBehavior"), Duration(openDelay), Duration(visibleDelay), Duration(closeDelay), false),
|
||||
"install", this, tooltip);
|
||||
} catch (e: Throwable) {
|
||||
LOG.log(Level.SEVERE, "Cannot install tooltip by reflection", e)
|
||||
Tooltip.install(this, tooltip)
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui
|
||||
|
||||
import javafx.fxml.FXML
|
||||
import javafx.scene.control.ScrollPane
|
||||
import javafx.scene.layout.VBox
|
||||
import org.jackhuang.hmcl.download.game.VersionJsonSaveTask
|
||||
import org.jackhuang.hmcl.game.GameVersion.minecraftVersion
|
||||
import org.jackhuang.hmcl.game.Version
|
||||
import org.jackhuang.hmcl.setting.Profile
|
||||
import org.jackhuang.hmcl.task.Schedulers
|
||||
import org.jackhuang.hmcl.ui.download.InstallWizardProvider
|
||||
import org.jackhuang.hmcl.util.task
|
||||
import java.util.*
|
||||
|
||||
class InstallerController {
|
||||
private lateinit var profile: Profile
|
||||
private lateinit var versionId: String
|
||||
private lateinit var version: Version
|
||||
|
||||
@FXML lateinit var scrollPane: ScrollPane
|
||||
@FXML lateinit var contentPane: VBox
|
||||
|
||||
private var forge: String? = null
|
||||
private var liteloader: String? = null
|
||||
private var optifine: String? = null
|
||||
|
||||
fun initialize() {
|
||||
scrollPane.smoothScrolling()
|
||||
}
|
||||
|
||||
fun loadVersion(profile: Profile, versionId: String) {
|
||||
this.profile = profile
|
||||
this.versionId = versionId
|
||||
this.version = profile.repository.getVersion(versionId).resolve(profile.repository)
|
||||
|
||||
contentPane.children.clear()
|
||||
forge = null
|
||||
liteloader = null
|
||||
optifine = null
|
||||
|
||||
for (library in version.libraries) {
|
||||
val removeAction = { _: InstallerItem ->
|
||||
val newList = LinkedList(version.libraries)
|
||||
newList.remove(library)
|
||||
VersionJsonSaveTask(profile.repository, version.setLibraries(newList))
|
||||
.with(task { profile.repository.refreshVersions() })
|
||||
.with(task(Schedulers.javafx()) { loadVersion(this.profile, this.versionId) })
|
||||
.start()
|
||||
}
|
||||
if (library.groupId.equals("net.minecraftforge", ignoreCase = true) && library.artifactId.equals("forge", ignoreCase = true)) {
|
||||
contentPane.children += InstallerItem("Forge", library.version, removeAction)
|
||||
forge = library.version
|
||||
} else if (library.groupId.equals("com.mumfrey", ignoreCase = true) && library.artifactId.equals("liteloader", ignoreCase = true)) {
|
||||
contentPane.children += InstallerItem("LiteLoader", library.version, removeAction)
|
||||
liteloader = library.version
|
||||
} else if ((library.groupId.equals("net.optifine", ignoreCase = true) || library.groupId.equals("optifine", ignoreCase = true)) && library.artifactId.equals("optifine", ignoreCase = true)) {
|
||||
contentPane.children += InstallerItem("OptiFine", library.version, removeAction)
|
||||
optifine = library.version
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onAdd() {
|
||||
// TODO: if minecraftVersion returns null.
|
||||
val gameVersion = minecraftVersion(profile.repository.getVersionJar(version)) ?: return
|
||||
|
||||
Controllers.decorator.startWizard(InstallWizardProvider(profile, gameVersion, version, forge, liteloader, optifine))
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package org.jackhuang.hmcl.ui
|
||||
|
||||
import com.jfoenix.effects.JFXDepthManager
|
||||
import javafx.fxml.FXML
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.layout.BorderPane
|
||||
|
||||
class InstallerItem(artifact: String, version: String, private val deleteCallback: (InstallerItem) -> Unit) : BorderPane() {
|
||||
@FXML lateinit var lblInstallerArtifact: Label
|
||||
@FXML lateinit var lblInstallerVersion: Label
|
||||
|
||||
init {
|
||||
loadFXML("/assets/fxml/version/installer-item.fxml")
|
||||
|
||||
style = "-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;"
|
||||
JFXDepthManager.setDepth(this, 1)
|
||||
lblInstallerArtifact.text = artifact
|
||||
lblInstallerVersion.text = version
|
||||
}
|
||||
|
||||
fun onDelete() {
|
||||
deleteCallback(this)
|
||||
}
|
||||
}
|
||||
@@ -19,10 +19,10 @@ package org.jackhuang.hmcl.ui
|
||||
|
||||
import javafx.scene.layout.VBox
|
||||
import javafx.scene.paint.Paint
|
||||
import org.jackhuang.hmcl.ProfileChangedEvent
|
||||
import org.jackhuang.hmcl.ProfileLoadingEvent
|
||||
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.game.AccountHelper
|
||||
import org.jackhuang.hmcl.i18n
|
||||
import org.jackhuang.hmcl.setting.Settings
|
||||
@@ -65,29 +65,27 @@ class LeftPaneController(private val leftPane: AdvancedListBox) {
|
||||
Controllers.decorator.showPage(ProfilePage(null))
|
||||
}
|
||||
|
||||
Settings.selectedAccountProperty.onChangeAndOperate {
|
||||
Settings.INSTANCE.selectedAccountProperty().onChangeAndOperate {
|
||||
if (it == null) {
|
||||
accountItem.lblVersionName.text = "mojang@mojang.com"
|
||||
accountItem.lblGameVersion.text = "Yggdrasil"
|
||||
accountItem.setVersionName("mojang@mojang.com")
|
||||
accountItem.setGameVersion("Yggdrasil")
|
||||
} else {
|
||||
accountItem.lblVersionName.text = it.username
|
||||
accountItem.lblGameVersion.text = accountType(it)
|
||||
accountItem.setVersionName(it.username)
|
||||
accountItem.setGameVersion(accountType(it))
|
||||
}
|
||||
if (it is YggdrasilAccount) {
|
||||
accountItem.imageView.image = AccountHelper.getSkin(it, 4.0)
|
||||
accountItem.imageView.viewport = AccountHelper.getViewport(4.0)
|
||||
accountItem.setImage(AccountHelper.getSkin(it, 4.0), AccountHelper.getViewport(4.0))
|
||||
} else {
|
||||
accountItem.imageView.image = DEFAULT_ICON
|
||||
accountItem.imageView.viewport = null
|
||||
accountItem.setImage(DEFAULT_ICON, null)
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings.getAccounts().isEmpty())
|
||||
if (Settings.INSTANCE.getAccounts().isEmpty())
|
||||
Controllers.navigate(AccountsPage())
|
||||
}
|
||||
|
||||
fun onProfileChanged(event: ProfileChangedEvent) {
|
||||
val profile = event.value
|
||||
val profile = event.profile
|
||||
|
||||
profilePane.children
|
||||
.filter { it is RipplerContainer && it.properties["profile"] is Pair<*, *> }
|
||||
@@ -96,7 +94,7 @@ class LeftPaneController(private val leftPane: AdvancedListBox) {
|
||||
|
||||
fun onProfilesLoading() {
|
||||
val list = LinkedList<RipplerContainer>()
|
||||
Settings.getProfiles().forEach { profile ->
|
||||
Settings.INSTANCE.profiles.forEach { profile ->
|
||||
val item = VersionListItem(profile.name)
|
||||
val ripplerContainer = RipplerContainer(item)
|
||||
item.onSettingsButtonClicked {
|
||||
@@ -107,7 +105,7 @@ class LeftPaneController(private val leftPane: AdvancedListBox) {
|
||||
// clean selected property
|
||||
profilePane.children.forEach { if (it is RipplerContainer) it.selected = false }
|
||||
ripplerContainer.selected = true
|
||||
Settings.selectedProfile = profile
|
||||
Settings.INSTANCE.selectedProfile = profile
|
||||
}
|
||||
ripplerContainer.properties["profile"] = profile.name to item
|
||||
ripplerContainer.maxWidthProperty().bind(leftPane.widthProperty())
|
||||
|
||||
@@ -21,13 +21,14 @@ import com.jfoenix.controls.JFXButton
|
||||
import com.jfoenix.controls.JFXMasonryPane
|
||||
import javafx.application.Platform
|
||||
import javafx.beans.property.SimpleStringProperty
|
||||
import javafx.beans.property.StringProperty
|
||||
import javafx.fxml.FXML
|
||||
import javafx.scene.Node
|
||||
import javafx.scene.image.Image
|
||||
import javafx.scene.layout.StackPane
|
||||
import org.jackhuang.hmcl.ProfileChangedEvent
|
||||
import org.jackhuang.hmcl.ProfileLoadingEvent
|
||||
import org.jackhuang.hmcl.event.EventBus
|
||||
import org.jackhuang.hmcl.event.ProfileChangedEvent
|
||||
import org.jackhuang.hmcl.event.ProfileLoadingEvent
|
||||
import org.jackhuang.hmcl.event.RefreshedVersionsEvent
|
||||
import org.jackhuang.hmcl.game.GameVersion.minecraftVersion
|
||||
import org.jackhuang.hmcl.game.LauncherHelper
|
||||
@@ -43,7 +44,8 @@ import org.jackhuang.hmcl.util.plusAssign
|
||||
* @see /assets/fxml/main.fxml
|
||||
*/
|
||||
class MainPage : StackPane(), DecoratorPage {
|
||||
override val titleProperty = SimpleStringProperty(this, "title", i18n("launcher.title.main"))
|
||||
private val titleProperty = SimpleStringProperty(this, "title", i18n("launcher.title.main"))
|
||||
override fun titleProperty() = titleProperty
|
||||
|
||||
@FXML lateinit var btnRefresh: JFXButton
|
||||
@FXML lateinit var btnAdd: JFXButton
|
||||
@@ -57,30 +59,31 @@ class MainPage : StackPane(), DecoratorPage {
|
||||
EventBus.EVENT_BUS.channel<ProfileChangedEvent>() += this::onProfileChanged
|
||||
|
||||
btnAdd.setOnMouseClicked { Controllers.decorator.startWizard(DownloadWizardProvider(), "Install New Game") }
|
||||
btnRefresh.setOnMouseClicked { Settings.selectedProfile.repository.refreshVersions() }
|
||||
btnRefresh.setOnMouseClicked { Settings.INSTANCE.selectedProfile.repository.refreshVersions() }
|
||||
}
|
||||
|
||||
private fun buildNode(i: Int, profile: Profile, version: String, game: String): Node {
|
||||
return VersionItem().apply {
|
||||
lblGameVersion.text = game
|
||||
lblVersionName.text = version
|
||||
btnLaunch.setOnMouseClicked {
|
||||
if (Settings.selectedAccount == null) {
|
||||
setGameVersion(game)
|
||||
setVersionName(version)
|
||||
|
||||
setOnLaunchButtonClicked {
|
||||
if (Settings.INSTANCE.selectedAccount == null) {
|
||||
Controllers.dialog(i18n("login.no_Player007"))
|
||||
} else
|
||||
LauncherHelper.INSTANCE.launch(version)
|
||||
}
|
||||
btnDelete.setOnMouseClicked {
|
||||
setOnDeleteButtonClicked {
|
||||
profile.repository.removeVersionFromDisk(version)
|
||||
Platform.runLater { loadVersions() }
|
||||
}
|
||||
btnSettings.setOnMouseClicked {
|
||||
setOnSettingsButtonClicked {
|
||||
Controllers.decorator.showPage(Controllers.versionPane)
|
||||
Controllers.versionPane.load(version, profile)
|
||||
}
|
||||
val iconFile = profile.repository.getVersionIcon(version)
|
||||
if (iconFile.exists())
|
||||
iconView.image = Image("file:" + iconFile.absolutePath)
|
||||
setImage(Image("file:" + iconFile.absolutePath))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,11 +92,11 @@ class MainPage : StackPane(), DecoratorPage {
|
||||
}
|
||||
|
||||
fun onProfileChanged(event: ProfileChangedEvent) = runOnUiThread {
|
||||
val profile = event.value
|
||||
val profile = event.profile
|
||||
loadVersions(profile)
|
||||
}
|
||||
|
||||
private fun loadVersions(profile: Profile = Settings.selectedProfile) {
|
||||
private fun loadVersions(profile: Profile = Settings.INSTANCE.selectedProfile) {
|
||||
val children = mutableListOf<Node>()
|
||||
var i = 0
|
||||
profile.repository.versions.forEach { version ->
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui
|
||||
|
||||
import com.jfoenix.controls.JFXButton
|
||||
import com.jfoenix.controls.JFXDialog
|
||||
import javafx.fxml.FXML
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.layout.StackPane
|
||||
|
||||
class MessageDialogPane(val text: String, val dialog: JFXDialog): StackPane() {
|
||||
@FXML lateinit var acceptButton: JFXButton
|
||||
@FXML lateinit var content: Label
|
||||
init {
|
||||
loadFXML("/assets/fxml/message-dialog.fxml")
|
||||
content.text = text
|
||||
acceptButton.setOnMouseClicked {
|
||||
dialog.close()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui
|
||||
|
||||
import com.jfoenix.controls.JFXSpinner
|
||||
import com.jfoenix.controls.JFXTabPane
|
||||
import javafx.fxml.FXML
|
||||
import javafx.scene.control.ScrollPane
|
||||
@@ -38,6 +39,7 @@ class ModController {
|
||||
@FXML lateinit var rootPane: StackPane
|
||||
@FXML lateinit var modPane: VBox
|
||||
@FXML lateinit var contentPane: StackPane
|
||||
@FXML lateinit var spinner: JFXSpinner
|
||||
lateinit var parentTab: JFXTabPane
|
||||
private lateinit var modManager: ModManager
|
||||
private lateinit var versionId: String
|
||||
@@ -67,7 +69,7 @@ class ModController {
|
||||
this.versionId = versionId
|
||||
task {
|
||||
synchronized(contentPane) {
|
||||
runOnUiThread { rootPane.children -= contentPane }
|
||||
runOnUiThread { rootPane.children -= contentPane; spinner.isVisible = true }
|
||||
modManager.refreshMods(versionId)
|
||||
|
||||
// Surprisingly, if there are a great number of mods, this processing will cause a UI pause.
|
||||
@@ -89,7 +91,7 @@ class ModController {
|
||||
styleClass += "disabled"
|
||||
}
|
||||
}
|
||||
runOnUiThread { rootPane.children += contentPane }
|
||||
runOnUiThread { rootPane.children += contentPane; spinner.isVisible = false }
|
||||
it["list"] = list
|
||||
}
|
||||
}.subscribe(Schedulers.javafx()) { variables ->
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui
|
||||
|
||||
import com.jfoenix.controls.JFXButton
|
||||
import com.jfoenix.controls.JFXCheckBox
|
||||
import com.jfoenix.effects.JFXDepthManager
|
||||
import javafx.geometry.Pos
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.layout.BorderPane
|
||||
import javafx.scene.layout.VBox
|
||||
import org.jackhuang.hmcl.mod.ModInfo
|
||||
import org.jackhuang.hmcl.util.onChange
|
||||
|
||||
class ModItem(info: ModInfo, private val deleteCallback: (ModItem) -> Unit) : BorderPane() {
|
||||
val lblModFileName = Label().apply { style = "-fx-font-size: 15;" }
|
||||
val lblModAuthor = Label().apply { style = "-fx-font-size: 10;" }
|
||||
val chkEnabled = JFXCheckBox().apply { BorderPane.setAlignment(this, Pos.CENTER) }
|
||||
|
||||
init {
|
||||
left = chkEnabled
|
||||
|
||||
center = VBox().apply {
|
||||
BorderPane.setAlignment(this, Pos.CENTER)
|
||||
|
||||
children += lblModFileName
|
||||
children += lblModAuthor
|
||||
}
|
||||
|
||||
right = JFXButton().apply {
|
||||
setOnMouseClicked { onDelete() }
|
||||
styleClass += "toggle-icon4"
|
||||
|
||||
BorderPane.setAlignment(this, Pos.CENTER)
|
||||
graphic = SVG.close("black", 15.0, 15.0)
|
||||
}
|
||||
|
||||
style = "-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;"
|
||||
JFXDepthManager.setDepth(this, 1)
|
||||
lblModFileName.text = info.fileName
|
||||
lblModAuthor.text = "${info.name}, Version: ${info.version}, Game Version: ${info.gameVersion}, Authors: ${info.authors}"
|
||||
chkEnabled.isSelected = info.isActive
|
||||
chkEnabled.selectedProperty().onChange {
|
||||
info.activeProperty().set(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun onDelete() {
|
||||
deleteCallback(this)
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ package org.jackhuang.hmcl.ui
|
||||
import com.jfoenix.controls.JFXButton
|
||||
import com.jfoenix.controls.JFXTextField
|
||||
import javafx.beans.property.SimpleStringProperty
|
||||
import javafx.beans.property.StringProperty
|
||||
import javafx.fxml.FXML
|
||||
import javafx.scene.layout.StackPane
|
||||
import org.jackhuang.hmcl.i18n
|
||||
@@ -34,8 +35,11 @@ import java.io.File
|
||||
* @param profile null if creating a new profile.
|
||||
*/
|
||||
class ProfilePage(private val profile: Profile?): StackPane(), DecoratorPage {
|
||||
override val titleProperty = SimpleStringProperty(this, "title",
|
||||
private val titleProperty = SimpleStringProperty(this, "title",
|
||||
if (profile == null) i18n("ui.newProfileWindow.title") else i18n("ui.label.profile") + " - " + profile.name)
|
||||
|
||||
override fun titleProperty() = titleProperty
|
||||
|
||||
private val locationProperty = SimpleStringProperty(this, "location",
|
||||
profile?.gameDir?.absolutePath ?: "")
|
||||
@FXML lateinit var txtProfileName: JFXTextField
|
||||
@@ -61,7 +65,7 @@ class ProfilePage(private val profile: Profile?): StackPane(), DecoratorPage {
|
||||
|
||||
fun onDelete() {
|
||||
if (profile != null) {
|
||||
Settings.deleteProfile(profile)
|
||||
Settings.INSTANCE.deleteProfile(profile)
|
||||
Controllers.navigate(null)
|
||||
}
|
||||
}
|
||||
@@ -75,10 +79,10 @@ class ProfilePage(private val profile: Profile?): StackPane(), DecoratorPage {
|
||||
if (locationProperty.get().isNullOrBlank()) {
|
||||
gameDir.onExplore()
|
||||
}
|
||||
Settings.putProfile(Profile(txtProfileName.text, File(locationProperty.get())))
|
||||
Settings.INSTANCE.putProfile(Profile(txtProfileName.text, File(locationProperty.get())))
|
||||
}
|
||||
|
||||
Settings.onProfileLoading()
|
||||
Settings.INSTANCE.onProfileLoading()
|
||||
Controllers.navigate(null)
|
||||
}
|
||||
}
|
||||
@@ -49,17 +49,17 @@ object SVG {
|
||||
return svg
|
||||
}
|
||||
|
||||
fun gear(fill: String = "white", width: Double = 20.0, height: Double = 20.0): Node = createSVGPath("M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.67 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z", fill, width, height)
|
||||
fun back(fill: String = "white", width: Double = 20.0, height: Double = 20.0): Node = createSVGPath("M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z", fill, width, height)
|
||||
fun close(fill: String = "white", width: Double = 20.0, height: Double = 20.0): Node = createSVGPath("M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z", fill, width, height)
|
||||
fun dotsVertical(fill: String = "white", width: Double = 20.0, height: Double = 20.0): Node = createSVGPath("M12,16A2,2 0 0,1 14,18A2,2 0 0,1 12,20A2,2 0 0,1 10,18A2,2 0 0,1 12,16M12,10A2,2 0 0,1 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12A2,2 0 0,1 12,10M12,4A2,2 0 0,1 14,6A2,2 0 0,1 12,8A2,2 0 0,1 10,6A2,2 0 0,1 12,4Z", fill, width, height)
|
||||
fun delete(fill: String = "white", width: Double = 20.0, height: Double = 20.0): Node = createSVGPath("M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z", fill, width, height)
|
||||
fun accountEdit(fill: String = "white", width: Double = 20.0, height: Double = 20.0) = createSVGPath("M21.7,13.35L20.7,14.35L18.65,12.3L19.65,11.3C19.86,11.09 20.21,11.09 20.42,11.3L21.7,12.58C21.91,12.79 21.91,13.14 21.7,13.35M12,18.94L18.06,12.88L20.11,14.93L14.06,21H12V18.94M12,14C7.58,14 4,15.79 4,18V20H10V18.11L14,14.11C13.34,14.03 12.67,14 12,14M12,4A4,4 0 0,0 8,8A4,4 0 0,0 12,12A4,4 0 0,0 16,8A4,4 0 0,0 12,4Z", fill, width, height)
|
||||
fun expand(fill: String = "white", width: Double = 20.0, height: Double = 20.0) = createSVGPath("M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z", fill, width, height)
|
||||
fun collapse(fill: String = "white", width: Double = 20.0, height: Double = 20.0) = createSVGPath("M12 8l-6 6 1.41 1.41L12 10.83l4.59 4.58L18 14z", fill, width, height)
|
||||
fun navigate(fill: String = "white", width: Double = 20.0, height: Double = 20.0) = createSVGPath("M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z", fill, width, height)
|
||||
fun launch(fill: String = "white", width: Double = 20.0, height: Double = 20.0) = createSVGPath("M1008 6.286q18.857 13.714 15.429 36.571l-146.286 877.714q-2.857 16.571-18.286 25.714-8 4.571-17.714 4.571-6.286 0-13.714-2.857l-258.857-105.714-138.286 168.571q-10.286 13.143-28 13.143-7.429 0-12.571-2.286-10.857-4-17.429-13.429t-6.571-20.857v-199.429l493.714-605.143-610.857 528.571-225.714-92.571q-21.143-8-22.857-31.429-1.143-22.857 18.286-33.714l950.857-548.571q8.571-5.143 18.286-5.14311.429 0 20.571 6.286z", fill, width, height)
|
||||
fun pencil(fill: String = "white", width: Double = 20.0, height: Double = 20.0) = createSVGPath("M20.71,4.04C21.1,3.65 21.1,3 20.71,2.63L18.37,0.29C18,-0.1 17.35,-0.1 16.96,0.29L15,2.25L18.75,6M17.75,7L14,3.25L4,13.25V17H7.75L17.75,7Z", fill, width, height)
|
||||
fun refresh(fill: String = "white", width: Double = 20.0, height: Double = 20.0) = createSVGPath("M17.65,6.35C16.2,4.9 14.21,4 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20C15.73,20 18.84,17.45 19.73,14H17.65C16.83,16.33 14.61,18 12,18A6,6 0 0,1 6,12A6,6 0 0,1 12,6C13.66,6 15.14,6.69 16.22,7.78L13,11H20V4L17.65,6.35Z", fill, width, height)
|
||||
fun folderOpen(fill: String = "white", width: Double = 20.0, height: Double = 20.0) = createSVGPath("M19,20H4C2.89,20 2,19.1 2,18V6C2,4.89 2.89,4 4,4H10L12,6H19A2,2 0 0,1 21,8H21L4,8V18L6.14,10H23.21L20.93,18.5C20.7,19.37 19.92,20 19,20Z", fill, width, height)
|
||||
@JvmStatic fun gear(fill: String = "white", width: Double = 20.0, height: Double = 20.0): Node = createSVGPath("M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.67 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z", fill, width, height)
|
||||
@JvmStatic fun back(fill: String = "white", width: Double = 20.0, height: Double = 20.0): Node = createSVGPath("M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z", fill, width, height)
|
||||
@JvmStatic fun close(fill: String = "white", width: Double = 20.0, height: Double = 20.0): Node = createSVGPath("M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z", fill, width, height)
|
||||
@JvmStatic fun dotsVertical(fill: String = "white", width: Double = 20.0, height: Double = 20.0): Node = createSVGPath("M12,16A2,2 0 0,1 14,18A2,2 0 0,1 12,20A2,2 0 0,1 10,18A2,2 0 0,1 12,16M12,10A2,2 0 0,1 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12A2,2 0 0,1 12,10M12,4A2,2 0 0,1 14,6A2,2 0 0,1 12,8A2,2 0 0,1 10,6A2,2 0 0,1 12,4Z", fill, width, height)
|
||||
@JvmStatic fun delete(fill: String = "white", width: Double = 20.0, height: Double = 20.0): Node = createSVGPath("M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z", fill, width, height)
|
||||
@JvmStatic fun accountEdit(fill: String = "white", width: Double = 20.0, height: Double = 20.0) = createSVGPath("M21.7,13.35L20.7,14.35L18.65,12.3L19.65,11.3C19.86,11.09 20.21,11.09 20.42,11.3L21.7,12.58C21.91,12.79 21.91,13.14 21.7,13.35M12,18.94L18.06,12.88L20.11,14.93L14.06,21H12V18.94M12,14C7.58,14 4,15.79 4,18V20H10V18.11L14,14.11C13.34,14.03 12.67,14 12,14M12,4A4,4 0 0,0 8,8A4,4 0 0,0 12,12A4,4 0 0,0 16,8A4,4 0 0,0 12,4Z", fill, width, height)
|
||||
@JvmStatic fun expand(fill: String = "white", width: Double = 20.0, height: Double = 20.0) = createSVGPath("M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z", fill, width, height)
|
||||
@JvmStatic fun collapse(fill: String = "white", width: Double = 20.0, height: Double = 20.0) = createSVGPath("M12 8l-6 6 1.41 1.41L12 10.83l4.59 4.58L18 14z", fill, width, height)
|
||||
@JvmStatic fun navigate(fill: String = "white", width: Double = 20.0, height: Double = 20.0) = createSVGPath("M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z", fill, width, height)
|
||||
@JvmStatic fun launch(fill: String = "white", width: Double = 20.0, height: Double = 20.0) = createSVGPath("M1008 6.286q18.857 13.714 15.429 36.571l-146.286 877.714q-2.857 16.571-18.286 25.714-8 4.571-17.714 4.571-6.286 0-13.714-2.857l-258.857-105.714-138.286 168.571q-10.286 13.143-28 13.143-7.429 0-12.571-2.286-10.857-4-17.429-13.429t-6.571-20.857v-199.429l493.714-605.143-610.857 528.571-225.714-92.571q-21.143-8-22.857-31.429-1.143-22.857 18.286-33.714l950.857-548.571q8.571-5.143 18.286-5.14311.429 0 20.571 6.286z", fill, width, height)
|
||||
@JvmStatic fun pencil(fill: String = "white", width: Double = 20.0, height: Double = 20.0) = createSVGPath("M20.71,4.04C21.1,3.65 21.1,3 20.71,2.63L18.37,0.29C18,-0.1 17.35,-0.1 16.96,0.29L15,2.25L18.75,6M17.75,7L14,3.25L4,13.25V17H7.75L17.75,7Z", fill, width, height)
|
||||
@JvmStatic fun refresh(fill: String = "white", width: Double = 20.0, height: Double = 20.0) = createSVGPath("M17.65,6.35C16.2,4.9 14.21,4 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20C15.73,20 18.84,17.45 19.73,14H17.65C16.83,16.33 14.61,18 12,18A6,6 0 0,1 6,12A6,6 0 0,1 12,6C13.66,6 15.14,6.69 16.22,7.78L13,11H20V4L17.65,6.35Z", fill, width, height)
|
||||
@JvmStatic fun folderOpen(fill: String = "white", width: Double = 20.0, height: Double = 20.0) = createSVGPath("M19,20H4C2.89,20 2,19.1 2,18V6C2,4.89 2.89,4 4,4H10L12,6H19A2,2 0 0,1 21,8H21L4,8V18L6.14,10H23.21L20.93,18.5C20.7,19.37 19.92,20 19,20Z", fill, width, height)
|
||||
}
|
||||
@@ -38,7 +38,9 @@ import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
||||
import org.jackhuang.hmcl.util.onChange
|
||||
|
||||
class SettingsPage : StackPane(), DecoratorPage {
|
||||
override val titleProperty: StringProperty = SimpleStringProperty(this, "title", i18n("launcher.title.launcher"))
|
||||
private val titleProperty: StringProperty = SimpleStringProperty(this, "title", i18n("launcher.title.launcher"))
|
||||
|
||||
override fun titleProperty() = titleProperty
|
||||
|
||||
@FXML lateinit var txtProxyHost: JFXTextField
|
||||
@FXML lateinit var txtProxyPort: JFXTextField
|
||||
@@ -59,56 +61,56 @@ class SettingsPage : StackPane(), DecoratorPage {
|
||||
cboLanguage.limitWidth(400.0)
|
||||
cboDownloadSource.limitWidth(400.0)
|
||||
|
||||
txtProxyHost.text = Settings.proxyHost
|
||||
txtProxyHost.textProperty().onChange { Settings.proxyHost = it }
|
||||
txtProxyHost.text = Settings.INSTANCE.proxyHost
|
||||
txtProxyHost.textProperty().onChange { Settings.INSTANCE.proxyHost = it }
|
||||
|
||||
txtProxyPort.text = Settings.proxyPort
|
||||
txtProxyPort.textProperty().onChange { Settings.proxyPort = it }
|
||||
txtProxyPort.text = Settings.INSTANCE.proxyPort
|
||||
txtProxyPort.textProperty().onChange { Settings.INSTANCE.proxyPort = it }
|
||||
|
||||
txtProxyUsername.text = Settings.proxyUser
|
||||
txtProxyUsername.textProperty().onChange { Settings.proxyUser = it }
|
||||
txtProxyUsername.text = Settings.INSTANCE.proxyUser
|
||||
txtProxyUsername.textProperty().onChange { Settings.INSTANCE.proxyUser = it }
|
||||
|
||||
txtProxyPassword.text = Settings.proxyPass
|
||||
txtProxyPassword.textProperty().onChange { Settings.proxyPass = it }
|
||||
txtProxyPassword.text = Settings.INSTANCE.proxyPass
|
||||
txtProxyPassword.textProperty().onChange { Settings.INSTANCE.proxyPass = it }
|
||||
|
||||
cboDownloadSource.selectionModel.select(DownloadProviders.DOWNLOAD_PROVIDERS.indexOf(Settings.downloadProvider))
|
||||
cboDownloadSource.selectionModel.select(DownloadProviders.DOWNLOAD_PROVIDERS.indexOf(Settings.INSTANCE.downloadProvider))
|
||||
cboDownloadSource.selectionModel.selectedIndexProperty().onChange {
|
||||
Settings.downloadProvider = DownloadProviders.getDownloadProvider(it)
|
||||
Settings.INSTANCE.downloadProvider = DownloadProviders.getDownloadProvider(it)
|
||||
}
|
||||
|
||||
cboFont.selectionModel.select(Settings.font.family)
|
||||
cboFont.selectionModel.select(Settings.INSTANCE.font.family)
|
||||
cboFont.valueProperty().onChange {
|
||||
val font = Font.font(it, Settings.font.size)
|
||||
Settings.font = font
|
||||
lblDisplay.style = "-fx-font: ${Settings.font.size} \"${font.family}\";"
|
||||
val font = Font.font(it, Settings.INSTANCE.font.size)
|
||||
Settings.INSTANCE.font = font
|
||||
lblDisplay.style = "-fx-font: ${Settings.INSTANCE.font.size} \"${font.family}\";"
|
||||
}
|
||||
txtFontSize.text = Settings.font.size.toString()
|
||||
txtFontSize.text = Settings.INSTANCE.font.size.toString()
|
||||
txtFontSize.validators += Validator { it.toDoubleOrNull() != null }
|
||||
txtFontSize.textProperty().onChange {
|
||||
if (txtFontSize.validate()) {
|
||||
val font = Font.font(Settings.font.family, it!!.toDouble())
|
||||
Settings.font = font
|
||||
lblDisplay.style = "-fx-font: ${font.size} \"${Settings.font.family}\";"
|
||||
val font = Font.font(Settings.INSTANCE.font.family, it!!.toDouble())
|
||||
Settings.INSTANCE.font = font
|
||||
lblDisplay.style = "-fx-font: ${font.size} \"${Settings.INSTANCE.font.family}\";"
|
||||
}
|
||||
}
|
||||
lblDisplay.style = "-fx-font: ${Settings.font.size} \"${Settings.font.family}\";"
|
||||
lblDisplay.style = "-fx-font: ${Settings.INSTANCE.font.size} \"${Settings.INSTANCE.font.family}\";"
|
||||
|
||||
val list = FXCollections.observableArrayList<Label>()
|
||||
for (locale in Locales.LOCALES) {
|
||||
list += Label(locale.getName(Settings.locale.resourceBundle))
|
||||
list += Label(locale.getName(Settings.INSTANCE.locale.resourceBundle))
|
||||
}
|
||||
cboLanguage.items = list
|
||||
cboLanguage.selectionModel.select(Locales.LOCALES.indexOf(Settings.locale))
|
||||
cboLanguage.selectionModel.select(Locales.LOCALES.indexOf(Settings.INSTANCE.locale))
|
||||
cboLanguage.selectionModel.selectedIndexProperty().onChange {
|
||||
Settings.locale = Locales.getLocale(it)
|
||||
Settings.INSTANCE.locale = Locales.getLocale(it)
|
||||
}
|
||||
|
||||
cboProxyType.selectionModel.select(Proxies.PROXIES.indexOf(Settings.proxyType))
|
||||
cboProxyType.selectionModel.select(Proxies.PROXIES.indexOf(Settings.INSTANCE.proxyType))
|
||||
cboProxyType.selectionModel.selectedIndexProperty().onChange {
|
||||
Settings.proxyType = Proxies.getProxyType(it)
|
||||
Settings.INSTANCE.proxyType = Proxies.getProxyType(it)
|
||||
}
|
||||
|
||||
fileCommonLocation.setProperty(Settings.commonPathProperty)
|
||||
fileCommonLocation.setProperty(Settings.INSTANCE.commonPathProperty())
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui
|
||||
|
||||
import com.jfoenix.controls.JFXButton
|
||||
import com.jfoenix.controls.JFXRadioButton
|
||||
import javafx.beans.binding.Bindings
|
||||
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.layout.Pane
|
||||
import javafx.scene.layout.StackPane
|
||||
import javafx.scene.layout.VBox
|
||||
import javafx.scene.paint.Color
|
||||
import java.util.concurrent.Callable
|
||||
|
||||
class VersionItem() : StackPane() {
|
||||
@FXML lateinit var icon: Pane
|
||||
@FXML lateinit var content: VBox
|
||||
@FXML lateinit var header: StackPane
|
||||
@FXML lateinit var body: StackPane
|
||||
@FXML lateinit var btnDelete: JFXButton
|
||||
@FXML lateinit var btnSettings: JFXButton
|
||||
@FXML lateinit var btnLaunch: JFXButton
|
||||
@FXML lateinit var lblVersionName: Label
|
||||
@FXML lateinit var lblGameVersion: Label
|
||||
@FXML lateinit var iconView: ImageView
|
||||
|
||||
init {
|
||||
loadFXML("/assets/fxml/version-item.fxml")
|
||||
|
||||
limitWidth(160.0)
|
||||
limitHeight(156.0)
|
||||
|
||||
effect = DropShadow(BlurType.GAUSSIAN, Color.rgb(0, 0, 0, 0.26), 5.0, 0.12, -1.0, 1.0)
|
||||
|
||||
btnSettings.graphic = SVG.gear("black", 15.0, 15.0)
|
||||
btnDelete.graphic = SVG.delete("black", 15.0, 15.0)
|
||||
btnLaunch.graphic = SVG.launch("black", 15.0, 15.0)
|
||||
|
||||
// create content
|
||||
//val headerColor = getDefaultColor(i % 12)
|
||||
//header.style = "-fx-background-radius: 2 2 0 0; -fx-background-color: " + headerColor
|
||||
|
||||
// create image view
|
||||
icon.translateYProperty().bind(Bindings.createDoubleBinding(Callable { header.boundsInParent.height - icon.height / 2 - 16.0 }, header.boundsInParentProperty(), icon.heightProperty()))
|
||||
iconView.limitSize(32.0, 32.0)
|
||||
}
|
||||
|
||||
/*private fun getDefaultColor(i: Int): String {
|
||||
var color = "#FFFFFF"
|
||||
when (i) {
|
||||
0 -> color = "#8F3F7E"
|
||||
1 -> color = "#B5305F"
|
||||
2 -> color = "#CE584A"
|
||||
3 -> color = "#DB8D5C"
|
||||
4 -> color = "#DA854E"
|
||||
5 -> color = "#E9AB44"
|
||||
6 -> color = "#FEE435"
|
||||
7 -> color = "#99C286"
|
||||
8 -> color = "#01A05E"
|
||||
9 -> color = "#4A8895"
|
||||
10 -> color = "#16669B"
|
||||
11 -> color = "#2F65A5"
|
||||
12 -> color = "#4E6A9C"
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
return color
|
||||
}*/
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui
|
||||
|
||||
import javafx.fxml.FXML
|
||||
import javafx.scene.control.Button
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.image.ImageView
|
||||
import javafx.scene.layout.StackPane
|
||||
|
||||
class VersionListItem(versionName: String, gameVersion: String = "") : StackPane() {
|
||||
|
||||
@FXML lateinit var lblVersionName: Label
|
||||
@FXML lateinit var lblGameVersion: Label
|
||||
@FXML lateinit var imageView: ImageView
|
||||
@FXML lateinit var imageViewContainer: StackPane
|
||||
|
||||
private var handler: () -> Unit = {}
|
||||
|
||||
init {
|
||||
loadFXML("/assets/fxml/version-list-item.fxml")
|
||||
lblVersionName.text = versionName
|
||||
lblGameVersion.text = gameVersion
|
||||
|
||||
imageView.limitSize(32.0, 32.0)
|
||||
imageViewContainer.limitWidth(32.0)
|
||||
imageViewContainer.limitHeight(32.0)
|
||||
}
|
||||
|
||||
fun onSettings() {
|
||||
handler()
|
||||
}
|
||||
|
||||
fun onSettingsButtonClicked(handler: () -> Unit) {
|
||||
this.handler = handler
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,8 @@ import org.jackhuang.hmcl.ui.export.ExportWizardProvider
|
||||
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
||||
|
||||
class VersionPage : StackPane(), DecoratorPage {
|
||||
override val titleProperty: StringProperty = SimpleStringProperty(this, "title", null)
|
||||
private val titleProperty: StringProperty = SimpleStringProperty(this, "title", null)
|
||||
override fun titleProperty() = titleProperty
|
||||
|
||||
@FXML lateinit var versionSettingsController: VersionSettingsController
|
||||
@FXML lateinit var modTab: Tab
|
||||
|
||||
@@ -57,7 +57,7 @@ class YggdrasilAccountLoginPane(private val oldAccount: YggdrasilAccount, privat
|
||||
taskResult("login") {
|
||||
try {
|
||||
val account = YggdrasilAccountFactory.INSTANCE.fromUsername(username, password)
|
||||
account.logIn(HMCLMultiCharacterSelector.INSTANCE, Settings.proxy)
|
||||
account.logIn(HMCLMultiCharacterSelector.INSTANCE, Settings.INSTANCE.proxy)
|
||||
} catch (e: Exception) {
|
||||
e
|
||||
}
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.animation
|
||||
|
||||
import javafx.animation.Interpolator
|
||||
import javafx.animation.KeyFrame
|
||||
import javafx.animation.KeyValue
|
||||
import javafx.util.Duration
|
||||
|
||||
typealias AnimationProducer = (AnimationHandler) -> List<KeyFrame>
|
||||
|
||||
enum class ContainerAnimations(val animationProducer: AnimationProducer) {
|
||||
/**
|
||||
* None
|
||||
*/
|
||||
NONE({
|
||||
emptyList()
|
||||
}),
|
||||
/**
|
||||
* A fade between the old and new view
|
||||
*/
|
||||
FADE({ c ->
|
||||
listOf(
|
||||
KeyFrame(Duration.ZERO,
|
||||
KeyValue(c.snapshot.opacityProperty(), 1.0, Interpolator.EASE_BOTH)),
|
||||
KeyFrame(c.duration,
|
||||
KeyValue(c.snapshot.opacityProperty(), 0.0, Interpolator.EASE_BOTH))
|
||||
)
|
||||
}),
|
||||
/**
|
||||
* A zoom effect
|
||||
*/
|
||||
ZOOM_IN({ c ->
|
||||
listOf(
|
||||
KeyFrame(Duration.ZERO,
|
||||
KeyValue(c.snapshot.scaleXProperty(), 1, Interpolator.EASE_BOTH),
|
||||
KeyValue(c.snapshot.scaleYProperty(), 1, Interpolator.EASE_BOTH),
|
||||
KeyValue(c.snapshot.opacityProperty(), 1.0, Interpolator.EASE_BOTH)),
|
||||
KeyFrame(c.duration,
|
||||
KeyValue(c.snapshot.scaleXProperty(), 4, Interpolator.EASE_BOTH),
|
||||
KeyValue(c.snapshot.scaleYProperty(), 4, Interpolator.EASE_BOTH),
|
||||
KeyValue(c.snapshot.opacityProperty(), 0, Interpolator.EASE_BOTH))
|
||||
)
|
||||
}),
|
||||
/**
|
||||
* A zoom effect
|
||||
*/
|
||||
ZOOM_OUT({ c ->
|
||||
listOf(
|
||||
KeyFrame(Duration.ZERO,
|
||||
KeyValue(c.snapshot.scaleXProperty(), 1, Interpolator.EASE_BOTH),
|
||||
KeyValue(c.snapshot.scaleYProperty(), 1, Interpolator.EASE_BOTH),
|
||||
KeyValue(c.snapshot.opacityProperty(), 1.0, Interpolator.EASE_BOTH)),
|
||||
KeyFrame(c.duration,
|
||||
KeyValue(c.snapshot.scaleXProperty(), 0, Interpolator.EASE_BOTH),
|
||||
KeyValue(c.snapshot.scaleYProperty(), 0, Interpolator.EASE_BOTH),
|
||||
KeyValue(c.snapshot.opacityProperty(), 0, Interpolator.EASE_BOTH))
|
||||
)
|
||||
}),
|
||||
/**
|
||||
* A swipe effect
|
||||
*/
|
||||
SWIPE_LEFT({ c ->
|
||||
listOf(
|
||||
KeyFrame(Duration.ZERO,
|
||||
KeyValue(c.view.translateXProperty(), c.view.getWidth(), Interpolator.EASE_BOTH),
|
||||
KeyValue(c.snapshot.translateXProperty(), -c.view.getWidth(), Interpolator.EASE_BOTH)),
|
||||
KeyFrame(c.duration,
|
||||
KeyValue(c.view.translateXProperty(), 0, Interpolator.EASE_BOTH),
|
||||
KeyValue(c.snapshot.translateXProperty(), -c.view.getWidth(), Interpolator.EASE_BOTH)
|
||||
))
|
||||
}),
|
||||
/**
|
||||
* A swipe effect
|
||||
*/
|
||||
SWIPE_RIGHT({ c ->
|
||||
listOf(
|
||||
KeyFrame(Duration.ZERO,
|
||||
KeyValue(c.view.translateXProperty(), -c.view.getWidth(), Interpolator.EASE_BOTH),
|
||||
KeyValue(c.snapshot.translateXProperty(), c.view.getWidth(), Interpolator.EASE_BOTH)),
|
||||
KeyFrame(c.duration,
|
||||
KeyValue(c.view.translateXProperty(), 0, Interpolator.EASE_BOTH),
|
||||
KeyValue(c.snapshot.translateXProperty(), c.view.getWidth(), Interpolator.EASE_BOTH))
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.animation
|
||||
|
||||
import javafx.animation.KeyFrame
|
||||
import javafx.animation.Timeline
|
||||
import javafx.event.EventHandler
|
||||
import javafx.scene.Node
|
||||
import javafx.scene.Parent
|
||||
import javafx.scene.SnapshotParameters
|
||||
import javafx.scene.image.ImageView
|
||||
import javafx.scene.image.WritableImage
|
||||
import javafx.scene.layout.Pane
|
||||
import javafx.scene.layout.StackPane
|
||||
import javafx.util.Duration
|
||||
import org.jackhuang.hmcl.ui.takeSnapshot
|
||||
|
||||
/**
|
||||
* @param view A stack pane that contains another control that is [Parent]
|
||||
*/
|
||||
class TransitionHandler(private val view: StackPane): AnimationHandler {
|
||||
private var animation: Timeline? = null
|
||||
|
||||
override fun getSnapshot() = ImageView().apply {
|
||||
isPreserveRatio = true
|
||||
isSmooth = true
|
||||
}
|
||||
|
||||
override fun getView() = view
|
||||
private lateinit var duration: Duration
|
||||
override fun getDuration() = duration
|
||||
|
||||
fun setContent(newView: Node, transition: AnimationProducer, duration: Duration = Duration.millis(320.0)) {
|
||||
this.duration = duration
|
||||
|
||||
val prevAnimation = animation
|
||||
if (prevAnimation != null)
|
||||
prevAnimation.stop()
|
||||
|
||||
updateContent(newView)
|
||||
|
||||
val nowAnimation = Timeline().apply {
|
||||
keyFrames.addAll(transition(this@TransitionHandler))
|
||||
keyFrames.add(KeyFrame(duration, EventHandler {
|
||||
snapshot.image = null
|
||||
snapshot.x = 0.0
|
||||
snapshot.y = 0.0
|
||||
snapshot.isVisible = false
|
||||
}))
|
||||
}
|
||||
nowAnimation.play()
|
||||
animation = nowAnimation
|
||||
}
|
||||
|
||||
private fun updateContent(newView: Node) {
|
||||
if (view.width > 0 && view.height > 0) {
|
||||
val content = view.children.firstOrNull()
|
||||
val image: WritableImage
|
||||
if (content != null && content is Parent) {
|
||||
view.children.setAll()
|
||||
image = takeSnapshot(content, view.width, view.height)
|
||||
view.children.setAll(content)
|
||||
} else image = view.snapshot(SnapshotParameters(), WritableImage(view.width.toInt(), view.height.toInt()))
|
||||
snapshot.image = image
|
||||
snapshot.fitWidth = view.width
|
||||
snapshot.fitHeight = view.height
|
||||
} else
|
||||
snapshot.image = null
|
||||
|
||||
snapshot.isVisible = true
|
||||
snapshot.opacity = 1.0
|
||||
view.children.setAll(snapshot, newView)
|
||||
snapshot.toFront()
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.construct
|
||||
|
||||
import javafx.beans.DefaultProperty
|
||||
import javafx.beans.property.SimpleIntegerProperty
|
||||
import javafx.beans.property.SimpleStringProperty
|
||||
import javafx.collections.FXCollections
|
||||
import javafx.collections.ListChangeListener
|
||||
import javafx.collections.ObservableList
|
||||
import javafx.scene.Node
|
||||
import javafx.scene.layout.StackPane
|
||||
import javafx.scene.layout.VBox
|
||||
import org.jackhuang.hmcl.util.getValue
|
||||
import org.jackhuang.hmcl.util.setValue
|
||||
import kotlin.collections.plusAssign
|
||||
import kotlin.collections.set
|
||||
|
||||
@DefaultProperty("content")
|
||||
open class ComponentList: StackPane() {
|
||||
|
||||
val vbox = VBox()
|
||||
|
||||
val content: ObservableList<Node> = FXCollections.observableArrayList<Node>().apply {
|
||||
addListener { change: ListChangeListener.Change<out Node> ->
|
||||
while (change.next()) {
|
||||
for (i in change.from until change.to) {
|
||||
addChildren(change.list[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
children.setAll(vbox)
|
||||
|
||||
styleClass += "options-list"
|
||||
}
|
||||
|
||||
fun addChildren(node: Node) {
|
||||
if (node is ComponentList) {
|
||||
node.properties["title"] = node.title
|
||||
node.properties["subtitle"] = node.subtitle
|
||||
}
|
||||
vbox.children += StackPane().apply {
|
||||
children += ComponentListCell(node)
|
||||
if (vbox.children.isEmpty())
|
||||
styleClass += "options-list-item-ahead"
|
||||
else {
|
||||
styleClass += "options-list-item"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val titleProperty = SimpleStringProperty(this, "title", "Group")
|
||||
var title: String by titleProperty
|
||||
|
||||
val subtitleProperty = SimpleStringProperty(this, "subtitle", "")
|
||||
var subtitle: String by subtitleProperty
|
||||
|
||||
var hasSubtitle: Boolean = false
|
||||
|
||||
val depthProperty = SimpleIntegerProperty(this, "depth", 0)
|
||||
var depth: Int by depthProperty
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.construct
|
||||
|
||||
import com.jfoenix.controls.JFXButton
|
||||
import javafx.animation.Animation
|
||||
import javafx.animation.KeyFrame
|
||||
import javafx.animation.KeyValue
|
||||
import javafx.animation.Timeline
|
||||
import javafx.beans.property.SimpleBooleanProperty
|
||||
import javafx.geometry.Pos
|
||||
import javafx.scene.Node
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.layout.StackPane
|
||||
import javafx.scene.layout.VBox
|
||||
import javafx.scene.shape.Rectangle
|
||||
import javafx.util.Duration
|
||||
import org.jackhuang.hmcl.ui.SINE
|
||||
import org.jackhuang.hmcl.ui.SVG
|
||||
import org.jackhuang.hmcl.ui.limitHeight
|
||||
import org.jackhuang.hmcl.util.getValue
|
||||
import org.jackhuang.hmcl.util.onChange
|
||||
import org.jackhuang.hmcl.util.setValue
|
||||
|
||||
class ComponentListCell(private val content: Node) : StackPane() {
|
||||
|
||||
var expandAnimation: Animation? = null
|
||||
private var clipRect: Rectangle? = null
|
||||
private var animatedHeight = 0.0
|
||||
|
||||
private val expandedProperty = SimpleBooleanProperty(this, "expanded", false)
|
||||
var expanded: Boolean by expandedProperty
|
||||
|
||||
init {
|
||||
updateLayout()
|
||||
}
|
||||
|
||||
private fun updateClip(newHeight: Double) {
|
||||
clipRect?.height = newHeight
|
||||
}
|
||||
|
||||
override fun layoutChildren() {
|
||||
super.layoutChildren()
|
||||
|
||||
if (clipRect == null) {
|
||||
clipRect = Rectangle(0.0, 0.0, width, height)
|
||||
//clip = clipRect
|
||||
} else {
|
||||
clipRect?.x = 0.0
|
||||
clipRect?.y = 0.0
|
||||
clipRect?.height = height
|
||||
clipRect?.width = width
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateLayout() {
|
||||
if (content is ComponentList) {
|
||||
content.styleClass -= "options-list"
|
||||
content.styleClass += "options-sublist"
|
||||
|
||||
val groupNode = StackPane()
|
||||
groupNode.styleClass += "options-list-item-header"
|
||||
|
||||
val expandIcon = SVG.expand("black", 10.0, 10.0)
|
||||
val expandButton = JFXButton()
|
||||
expandButton.graphic = expandIcon
|
||||
expandButton.styleClass += "options-list-item-expand-button"
|
||||
StackPane.setAlignment(expandButton, Pos.CENTER_RIGHT)
|
||||
|
||||
val labelVBox = VBox()
|
||||
Label().apply {
|
||||
textProperty().bind(content.titleProperty)
|
||||
isMouseTransparent = true
|
||||
labelVBox.children += this
|
||||
}
|
||||
|
||||
if (content.hasSubtitle)
|
||||
Label().apply {
|
||||
textProperty().bind(content.subtitleProperty)
|
||||
isMouseTransparent = true
|
||||
styleClass += "subtitle-label"
|
||||
labelVBox.children += this
|
||||
}
|
||||
|
||||
StackPane.setAlignment(labelVBox, Pos.CENTER_LEFT)
|
||||
groupNode.children.setAll(
|
||||
labelVBox,
|
||||
expandButton)
|
||||
|
||||
val container = VBox().apply {
|
||||
style += "-fx-padding: 8 0 0 0;"
|
||||
limitHeight(0.0)
|
||||
val clipRect = Rectangle()
|
||||
clipRect.widthProperty().bind(widthProperty())
|
||||
clipRect.heightProperty().bind(heightProperty())
|
||||
clip = clipRect
|
||||
children.setAll(content)
|
||||
}
|
||||
|
||||
val holder = VBox()
|
||||
holder.children.setAll(groupNode, container)
|
||||
holder.styleClass += "options-list-item-container"
|
||||
|
||||
expandButton.setOnMouseClicked {
|
||||
if (expandAnimation != null && expandAnimation!!.status == Animation.Status.RUNNING) {
|
||||
expandAnimation!!.stop()
|
||||
}
|
||||
|
||||
expanded = !expanded
|
||||
|
||||
val newAnimatedHeight = content.prefHeight(-1.0) * (if (expanded) 1.0 else -1.0)
|
||||
val newHeight = if (expanded) height + newAnimatedHeight else prefHeight(-1.0)
|
||||
val contentHeight = if (expanded) newAnimatedHeight else 0.0
|
||||
|
||||
if (expanded) {
|
||||
updateClip(newHeight)
|
||||
}
|
||||
|
||||
animatedHeight = newAnimatedHeight
|
||||
|
||||
expandAnimation = Timeline(KeyFrame(Duration(320.0),
|
||||
KeyValue(container.minHeightProperty(), contentHeight, SINE),
|
||||
KeyValue(container.maxHeightProperty(), contentHeight, SINE)
|
||||
))
|
||||
|
||||
if (!expanded) {
|
||||
expandAnimation?.setOnFinished {
|
||||
updateClip(newHeight)
|
||||
animatedHeight = 0.0
|
||||
}
|
||||
}
|
||||
|
||||
expandAnimation?.play()
|
||||
}
|
||||
|
||||
expandedProperty.onChange {
|
||||
if (it) {
|
||||
expandIcon.rotate = 180.0
|
||||
} else {
|
||||
expandIcon.rotate = 0.0
|
||||
}
|
||||
}
|
||||
|
||||
children.setAll(holder)
|
||||
} else {
|
||||
children.setAll(content)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.construct
|
||||
|
||||
import com.jfoenix.controls.JFXButton
|
||||
import javafx.beans.property.Property
|
||||
import javafx.beans.property.SimpleStringProperty
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.control.Tooltip
|
||||
import javafx.scene.layout.BorderPane
|
||||
import javafx.scene.layout.VBox
|
||||
import javafx.stage.DirectoryChooser
|
||||
import org.jackhuang.hmcl.ui.Controllers
|
||||
import org.jackhuang.hmcl.ui.SVG
|
||||
import org.jackhuang.hmcl.util.getValue
|
||||
import org.jackhuang.hmcl.util.setValue
|
||||
|
||||
class FileItem : BorderPane() {
|
||||
val nameProperty = SimpleStringProperty(this, "name")
|
||||
var name: String by nameProperty
|
||||
|
||||
val titleProperty = SimpleStringProperty(this, "title")
|
||||
var title: String by titleProperty
|
||||
|
||||
val tooltipProperty = SimpleStringProperty(this, "tooltip")
|
||||
var tooltip: String by tooltipProperty
|
||||
|
||||
private lateinit var property: Property<String>
|
||||
|
||||
private val x = Label()
|
||||
init {
|
||||
left = VBox().apply {
|
||||
children += Label().apply { textProperty().bind(nameProperty) }
|
||||
children += x.apply { styleClass += "subtitle-label" }
|
||||
}
|
||||
|
||||
right = JFXButton().apply {
|
||||
graphic = SVG.pencil("black", 15.0, 15.0)
|
||||
styleClass += "toggle-icon4"
|
||||
setOnMouseClicked { onExplore() }
|
||||
}
|
||||
|
||||
Tooltip.install(this, Tooltip().apply {
|
||||
textProperty().bind(tooltipProperty)
|
||||
})
|
||||
}
|
||||
|
||||
fun onExplore() {
|
||||
val chooser = DirectoryChooser()
|
||||
chooser.titleProperty().bind(titleProperty)
|
||||
val selectedDir = chooser.showDialog(Controllers.stage)
|
||||
if (selectedDir != null)
|
||||
property.value = selectedDir.absolutePath
|
||||
chooser.titleProperty().unbind()
|
||||
}
|
||||
|
||||
fun setProperty(property: Property<String>) {
|
||||
this.property = property
|
||||
x.textProperty().bind(property)
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.construct
|
||||
|
||||
import com.jfoenix.controls.JFXComboBox
|
||||
import javafx.beans.NamedArg
|
||||
import javafx.collections.FXCollections
|
||||
import javafx.scene.control.ListCell
|
||||
import javafx.scene.text.Font
|
||||
import javafx.util.Callback
|
||||
import org.jackhuang.hmcl.util.onChange
|
||||
|
||||
class FontComboBox(@NamedArg("fontSize") fontSize: Double = 12.0, @NamedArg("enableStyle") enableStyle: Boolean = false) : JFXComboBox<String>(FXCollections.observableArrayList(Font.getFamilies())) {
|
||||
init {
|
||||
valueProperty().onChange {
|
||||
if (enableStyle)
|
||||
style = "-fx-font-family: \"$it\";"
|
||||
}
|
||||
cellFactory = Callback {
|
||||
object : ListCell<String>() {
|
||||
override fun updateItem(item: String?, empty: Boolean) {
|
||||
super.updateItem(item, empty)
|
||||
item?.apply {
|
||||
text = item
|
||||
font = Font(item, fontSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.construct
|
||||
|
||||
import javafx.geometry.Pos
|
||||
import javafx.scene.Node
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.layout.HBox
|
||||
|
||||
class IconedItem(val icon: Node, val text: String)
|
||||
: RipplerContainer(HBox().apply {
|
||||
children += icon.apply { isMouseTransparent = true }
|
||||
children += Label(text).apply { alignment = Pos.CENTER; isMouseTransparent = true }
|
||||
style += "-fx-padding: 10 16 10 16; -fx-spacing: 10; -fx-font-size: 14; "
|
||||
alignment = Pos.CENTER_LEFT
|
||||
})
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.construct
|
||||
|
||||
import javafx.collections.FXCollections
|
||||
import javafx.collections.ObservableList
|
||||
import javafx.scene.control.MultipleSelectionModel
|
||||
|
||||
class NoneMultipleSelectionModel<T> : MultipleSelectionModel<T>() {
|
||||
override fun isEmpty() = true
|
||||
override fun selectAll() = Unit
|
||||
override fun selectIndices(index: Int, vararg indices: Int) = Unit
|
||||
override fun select(obj: T) = Unit
|
||||
override fun select(index: Int) = Unit
|
||||
override fun selectLast() = Unit
|
||||
override fun selectFirst() = Unit
|
||||
override fun selectNext() = Unit
|
||||
override fun clearSelection(index: Int) = Unit
|
||||
override fun clearSelection() = Unit
|
||||
override fun clearAndSelect(index: Int) = Unit
|
||||
override fun selectPrevious() = Unit
|
||||
override fun isSelected(index: Int) = false
|
||||
override fun getSelectedItems(): ObservableList<T> = FXCollections.emptyObservableList<T>()
|
||||
override fun getSelectedIndices(): ObservableList<Int> = FXCollections.emptyObservableList<Int>()
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.construct
|
||||
|
||||
import com.jfoenix.validation.base.ValidatorBase
|
||||
import javafx.scene.control.TextInputControl
|
||||
|
||||
class NumberValidator @JvmOverloads constructor(val nullable: Boolean = false) : ValidatorBase() {
|
||||
|
||||
override fun eval() {
|
||||
if (this.srcControl.get() is TextInputControl) {
|
||||
this.evalTextInputField()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun evalTextInputField() {
|
||||
val textField = this.srcControl.get() as TextInputControl
|
||||
|
||||
if (textField.text.isBlank())
|
||||
hasErrors.set(false)
|
||||
else
|
||||
try {
|
||||
Integer.parseInt(textField.text)
|
||||
this.hasErrors.set(false)
|
||||
} catch (var3: Exception) {
|
||||
this.hasErrors.set(true)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.download
|
||||
|
||||
import com.jfoenix.controls.JFXButton
|
||||
import com.jfoenix.controls.JFXListView
|
||||
import javafx.fxml.FXML
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.layout.StackPane
|
||||
import javafx.scene.layout.VBox
|
||||
import org.jackhuang.hmcl.download.DownloadProvider
|
||||
import org.jackhuang.hmcl.game.GameRepository
|
||||
import org.jackhuang.hmcl.ui.loadFXML
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardController
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardPage
|
||||
|
||||
class AdditionalInstallersPage(private val provider: InstallWizardProvider, private val controller: WizardController, private val repository: GameRepository, private val downloadProvider: DownloadProvider): StackPane(), WizardPage {
|
||||
|
||||
@FXML lateinit var list: VBox
|
||||
@FXML lateinit var btnForge: JFXButton
|
||||
@FXML lateinit var btnLiteLoader: JFXButton
|
||||
@FXML lateinit var btnOptiFine: JFXButton
|
||||
@FXML lateinit var lblGameVersion: Label
|
||||
@FXML lateinit var lblVersionName: Label
|
||||
@FXML lateinit var lblForge: Label
|
||||
@FXML lateinit var lblLiteLoader: Label
|
||||
@FXML lateinit var lblOptiFine: Label
|
||||
@FXML lateinit var btnInstall: JFXButton
|
||||
|
||||
init {
|
||||
loadFXML("/assets/fxml/download/additional-installers.fxml")
|
||||
|
||||
lblGameVersion.text = provider.gameVersion
|
||||
lblVersionName.text = provider.version.id
|
||||
|
||||
btnForge.setOnMouseClicked {
|
||||
controller.settings[INSTALLER_TYPE] = 0
|
||||
controller.onNext(VersionsPage(controller, provider.gameVersion, downloadProvider, "forge") { controller.onPrev(false) })
|
||||
}
|
||||
|
||||
btnLiteLoader.setOnMouseClicked {
|
||||
controller.settings[INSTALLER_TYPE] = 1
|
||||
controller.onNext(VersionsPage(controller, provider.gameVersion, downloadProvider, "liteloader") { controller.onPrev(false) })
|
||||
}
|
||||
|
||||
btnOptiFine.setOnMouseClicked {
|
||||
controller.settings[INSTALLER_TYPE] = 2
|
||||
controller.onNext(VersionsPage(controller, provider.gameVersion, downloadProvider, "optifine") { controller.onPrev(false) })
|
||||
}
|
||||
}
|
||||
|
||||
override val title: String
|
||||
get() = "Choose a game version"
|
||||
|
||||
override fun onNavigate(settings: MutableMap<String, Any>) {
|
||||
lblGameVersion.text = "Current Game Version: ${provider.gameVersion}"
|
||||
btnForge.isDisable = provider.forge != null
|
||||
if (provider.forge != null || controller.settings.containsKey("forge"))
|
||||
lblForge.text = "Forge Versoin: ${provider.forge ?: controller.settings["forge"]}"
|
||||
else
|
||||
lblForge.text = "Forge not installed"
|
||||
|
||||
btnLiteLoader.isDisable = provider.liteloader != null
|
||||
if (provider.liteloader != null || controller.settings.containsKey("liteloader"))
|
||||
lblLiteLoader.text = "LiteLoader Versoin: ${provider.liteloader ?: controller.settings["liteloader"]}"
|
||||
else
|
||||
lblLiteLoader.text = "LiteLoader not installed"
|
||||
|
||||
btnOptiFine.isDisable = provider.optifine != null
|
||||
if (provider.optifine != null || controller.settings.containsKey("optifine"))
|
||||
lblOptiFine.text = "OptiFine Versoin: ${provider.optifine ?: controller.settings["optifine"]}"
|
||||
else
|
||||
lblOptiFine.text = "OptiFine not installed"
|
||||
|
||||
}
|
||||
|
||||
override fun cleanup(settings: MutableMap<String, Any>) {
|
||||
settings.remove(INSTALLER_TYPE)
|
||||
}
|
||||
|
||||
fun onInstall() {
|
||||
controller.onFinish()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val INSTALLER_TYPE = "INSTALLER_TYPE"
|
||||
}
|
||||
}
|
||||
@@ -31,11 +31,11 @@ import org.jackhuang.hmcl.ui.wizard.WizardProvider
|
||||
import org.jackhuang.hmcl.util.task
|
||||
import java.io.File
|
||||
|
||||
class DownloadWizardProvider(): WizardProvider() {
|
||||
class DownloadWizardProvider(): WizardProvider {
|
||||
lateinit var profile: Profile
|
||||
|
||||
override fun start(settings: MutableMap<String, Any>) {
|
||||
profile = Settings.selectedProfile
|
||||
profile = Settings.INSTANCE.selectedProfile
|
||||
settings[PROFILE] = profile
|
||||
}
|
||||
|
||||
|
||||
@@ -41,8 +41,7 @@ class InstallTypePage(private val controller: WizardController): StackPane(), Wi
|
||||
settings.remove(INSTALL_TYPE)
|
||||
}
|
||||
|
||||
override val title: String
|
||||
get() = "Select an operation"
|
||||
override fun getTitle() = "Select an operation"
|
||||
|
||||
companion object {
|
||||
const val INSTALL_TYPE: String = "INSTALL_TYPE"
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
package org.jackhuang.hmcl.ui.download
|
||||
|
||||
import javafx.scene.Node
|
||||
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider
|
||||
import org.jackhuang.hmcl.game.Version
|
||||
import org.jackhuang.hmcl.mod.Modpack
|
||||
import org.jackhuang.hmcl.setting.Profile
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardController
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardProvider
|
||||
import org.jackhuang.hmcl.util.task
|
||||
import java.io.File
|
||||
|
||||
class InstallWizardProvider(val profile: Profile, val gameVersion: String, val version: Version, val forge: String? = null, val liteloader: String? = null, val optifine: String? = null): WizardProvider() {
|
||||
|
||||
override fun start(settings: MutableMap<String, Any>) {
|
||||
}
|
||||
|
||||
override fun finish(settings: MutableMap<String, Any>): Any? {
|
||||
var ret = task {}
|
||||
|
||||
if (settings.containsKey("forge"))
|
||||
ret = ret.with(profile.dependency.installLibraryAsync(gameVersion, version, "forge", settings["forge"] as String))
|
||||
|
||||
if (settings.containsKey("liteloader"))
|
||||
ret = ret.with(profile.dependency.installLibraryAsync(gameVersion, version, "liteloader", settings["liteloader"] as String))
|
||||
|
||||
if (settings.containsKey("optifine"))
|
||||
ret = ret.with(profile.dependency.installLibraryAsync(gameVersion, version, "optifine", settings["optifine"] as String))
|
||||
|
||||
return ret.with(task { profile.repository.refreshVersions() })
|
||||
}
|
||||
|
||||
override fun createPage(controller: WizardController, step: Int, settings: MutableMap<String, Any>): Node {
|
||||
return when (step) {
|
||||
0 -> AdditionalInstallersPage(this, controller, profile.repository, BMCLAPIDownloadProvider.INSTANCE)
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
|
||||
override fun cancel(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
@@ -70,8 +70,7 @@ class InstallersPage(private val controller: WizardController, private val repos
|
||||
}
|
||||
}
|
||||
|
||||
override val title: String
|
||||
get() = "Choose a game version"
|
||||
override fun getTitle() = "Choose a game version"
|
||||
|
||||
override fun onNavigate(settings: MutableMap<String, Any>) {
|
||||
lblGameVersion.text = "Current Game Version: ${controller.settings["game"]}"
|
||||
|
||||
@@ -36,7 +36,8 @@ import org.jackhuang.hmcl.ui.wizard.WizardPage
|
||||
import org.jackhuang.hmcl.util.onInvalidated
|
||||
|
||||
class ModpackPage(private val controller: WizardController): StackPane(), WizardPage {
|
||||
override val title: String = i18n("modpack.task.install")
|
||||
private val title: String = i18n("modpack.task.install")
|
||||
override fun getTitle() = title
|
||||
|
||||
@FXML lateinit var borderPane: Region
|
||||
@FXML lateinit var lblName: Label
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.download
|
||||
|
||||
import com.jfoenix.controls.JFXListView
|
||||
import com.jfoenix.controls.JFXSpinner
|
||||
import javafx.fxml.FXML
|
||||
import javafx.scene.layout.StackPane
|
||||
import org.jackhuang.hmcl.download.DownloadProvider
|
||||
import org.jackhuang.hmcl.download.RemoteVersion
|
||||
import org.jackhuang.hmcl.task.Scheduler
|
||||
import org.jackhuang.hmcl.task.Schedulers
|
||||
import org.jackhuang.hmcl.task.TaskExecutor
|
||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations
|
||||
import org.jackhuang.hmcl.ui.animation.TransitionHandler
|
||||
import org.jackhuang.hmcl.ui.loadFXML
|
||||
import org.jackhuang.hmcl.ui.wizard.Refreshable
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardController
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardPage
|
||||
import org.jackhuang.hmcl.util.onChange
|
||||
|
||||
class VersionsPage(private val controller: WizardController, private val gameVersion: String, private val downloadProvider: DownloadProvider, private val libraryId: String, private val callback: () -> Unit): StackPane(), WizardPage, Refreshable {
|
||||
|
||||
@FXML lateinit var list: JFXListView<VersionsPageItem>
|
||||
@FXML lateinit var spinner: JFXSpinner
|
||||
|
||||
val transitionHandler = TransitionHandler(this)
|
||||
private val versionList = downloadProvider.getVersionListById(libraryId)
|
||||
private var executor: TaskExecutor? = null
|
||||
|
||||
init {
|
||||
loadFXML("/assets/fxml/download/versions.fxml")
|
||||
children.setAll(spinner)
|
||||
list.selectionModel.selectedItemProperty().onChange {
|
||||
controller.settings[libraryId] = it!!.remoteVersion.selfVersion
|
||||
callback()
|
||||
}
|
||||
refresh()
|
||||
}
|
||||
|
||||
override fun refresh() {
|
||||
executor = versionList.refreshAsync(downloadProvider).subscribe(Schedulers.javafx()) {
|
||||
val versions = ArrayList(versionList.getVersions(gameVersion))
|
||||
versions.sortWith(RemoteVersion.RemoteVersionComparator.INSTANCE)
|
||||
for (version in versions) {
|
||||
list.items.add(VersionsPageItem(version))
|
||||
}
|
||||
|
||||
transitionHandler.setContent(list, ContainerAnimations.FADE.animationProducer)
|
||||
}
|
||||
}
|
||||
|
||||
override val title: String
|
||||
get() = "Choose a game version"
|
||||
|
||||
override fun cleanup(settings: MutableMap<String, Any>) {
|
||||
settings.remove(libraryId)
|
||||
executor?.cancel()
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.export
|
||||
|
||||
import javafx.scene.Node
|
||||
import org.jackhuang.hmcl.game.HMCLModpackExportTask
|
||||
import org.jackhuang.hmcl.game.HMCLModpackManager.MODPACK_PREDICATE
|
||||
import org.jackhuang.hmcl.mod.Modpack
|
||||
import org.jackhuang.hmcl.setting.Profile
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardController
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardProvider
|
||||
import java.io.File
|
||||
|
||||
class ExportWizardProvider(private val profile: Profile, private val version: String) : WizardProvider() {
|
||||
override fun start(settings: MutableMap<String, Any>) {
|
||||
}
|
||||
|
||||
override fun finish(settings: MutableMap<String, Any>): Any? {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return HMCLModpackExportTask(profile.repository, version, settings[ModpackFileSelectionPage.MODPACK_FILE_SELECTION] as List<String>,
|
||||
Modpack(
|
||||
settings[ModpackInfoPage.MODPACK_NAME] as String,
|
||||
settings[ModpackInfoPage.MODPACK_AUTHOR] as String,
|
||||
settings[ModpackInfoPage.MODPACK_VERSION] as String,
|
||||
null,
|
||||
settings[ModpackInfoPage.MODPACK_DESCRIPTION] as String,
|
||||
null
|
||||
), settings[ModpackInfoPage.MODPACK_FILE] as File)
|
||||
}
|
||||
|
||||
override fun createPage(controller: WizardController, step: Int, settings: MutableMap<String, Any>): Node {
|
||||
return when(step) {
|
||||
0 -> ModpackInfoPage(controller, version)
|
||||
1 -> ModpackFileSelectionPage(controller, profile, version, MODPACK_PREDICATE)
|
||||
else -> throw IllegalArgumentException("step")
|
||||
}
|
||||
}
|
||||
|
||||
override fun cancel(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.export
|
||||
|
||||
import com.jfoenix.controls.JFXTreeView
|
||||
import javafx.fxml.FXML
|
||||
import javafx.scene.control.CheckBox
|
||||
import javafx.scene.control.CheckBoxTreeItem
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.control.TreeItem
|
||||
import javafx.scene.layout.HBox
|
||||
import javafx.scene.layout.StackPane
|
||||
import org.jackhuang.hmcl.game.ModAdviser
|
||||
import org.jackhuang.hmcl.i18n
|
||||
import org.jackhuang.hmcl.setting.Profile
|
||||
import org.jackhuang.hmcl.ui.construct.NoneMultipleSelectionModel
|
||||
import org.jackhuang.hmcl.ui.loadFXML
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardController
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardPage
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
class ModpackFileSelectionPage(private val controller: WizardController, profile: Profile, private val version: String, private val adviser: ModAdviser): StackPane(), WizardPage {
|
||||
override val title: String = i18n("modpack.wizard.step.2.title")
|
||||
@FXML lateinit var treeView: JFXTreeView<String>
|
||||
private val rootNode: CheckBoxTreeItem<String>?
|
||||
init {
|
||||
loadFXML("/assets/fxml/modpack/selection.fxml")
|
||||
|
||||
rootNode = getTreeItem(profile.repository.getRunDirectory(version), "minecraft")
|
||||
treeView.root = rootNode
|
||||
treeView.selectionModel = NoneMultipleSelectionModel<TreeItem<String>>()
|
||||
}
|
||||
|
||||
private fun getTreeItem(file: File, basePath: String): CheckBoxTreeItem<String>? {
|
||||
var state = ModAdviser.ModSuggestion.SUGGESTED
|
||||
if (basePath.length > "minecraft/".length) {
|
||||
state = adviser.advise(basePath.substringAfter("minecraft/") + (if (file.isDirectory) "/" else ""), file.isDirectory)
|
||||
if (file.isFile && file.nameWithoutExtension == version)
|
||||
state = ModAdviser.ModSuggestion.HIDDEN
|
||||
if (file.isDirectory && file.name == version + "-natives")
|
||||
state = ModAdviser.ModSuggestion.HIDDEN
|
||||
if (state == ModAdviser.ModSuggestion.HIDDEN)
|
||||
return null
|
||||
}
|
||||
|
||||
val node = CheckBoxTreeItem<String>(basePath.substringAfterLast("/"))
|
||||
if (state == ModAdviser.ModSuggestion.SUGGESTED)
|
||||
node.isSelected = true
|
||||
|
||||
if (file.isDirectory) {
|
||||
file.listFiles()?.forEach {
|
||||
val subNode = getTreeItem(it, basePath + "/" + it.name)
|
||||
if (subNode != null) {
|
||||
node.isSelected = subNode.isSelected or node.isSelected
|
||||
if (!subNode.isSelected)
|
||||
node.isIndeterminate = true
|
||||
node.children += subNode
|
||||
}
|
||||
}
|
||||
if (!node.isSelected) node.isIndeterminate = false
|
||||
|
||||
// Empty folder need not to be displayed.
|
||||
if (node.children.isEmpty())
|
||||
return null
|
||||
}
|
||||
|
||||
return node.apply {
|
||||
graphic = HBox().apply {
|
||||
val checkbox = CheckBox()
|
||||
checkbox.selectedProperty().bindBidirectional(node.selectedProperty())
|
||||
checkbox.indeterminateProperty().bindBidirectional(node.indeterminateProperty())
|
||||
children += checkbox
|
||||
if (TRANSLATION.containsKey(basePath))
|
||||
children += Label().apply {
|
||||
text = TRANSLATION[basePath]
|
||||
style = "-fx-text-fill: gray;"
|
||||
isMouseTransparent = true
|
||||
}
|
||||
isPickOnBounds = false
|
||||
isExpanded = basePath == "minecraft"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFilesNeeded(node: CheckBoxTreeItem<String>?, basePath: String, list: MutableList<String>) {
|
||||
if (node == null)
|
||||
return
|
||||
if (node.isSelected) {
|
||||
if (basePath.length > "minecraft/".length)
|
||||
list += basePath.substring("minecraft/".length)
|
||||
for (child in node.children)
|
||||
getFilesNeeded(child as? CheckBoxTreeItem<String>?, basePath + "/" + child.value, list)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
override fun cleanup(settings: MutableMap<String, Any>) {
|
||||
controller.settings.remove(MODPACK_FILE_SELECTION)
|
||||
}
|
||||
|
||||
fun onNext() {
|
||||
val list = LinkedList<String>()
|
||||
getFilesNeeded(rootNode, "minecraft", list)
|
||||
controller.settings[MODPACK_FILE_SELECTION] = list
|
||||
controller.onFinish()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val MODPACK_FILE_SELECTION = "modpack.accepted"
|
||||
|
||||
private val TRANSLATION = mapOf(
|
||||
"minecraft/servers.dat" to i18n("modpack.files.servers_dat"),
|
||||
"minecraft/saves" to i18n("modpack.files.saves"),
|
||||
"minecraft/mods" to i18n("modpack.files.mods"),
|
||||
"minecraft/config" to i18n("modpack.files.config"),
|
||||
"minecraft/liteconfig" to i18n("modpack.files.liteconfig"),
|
||||
"minecraft/resourcepacks" to i18n("modpack.files.resourcepacks"),
|
||||
"minecraft/resources" to i18n("modpack.files.resourcepacks"),
|
||||
"minecraft/options.txt" to i18n("modpack.files.options_txt"),
|
||||
"minecraft/optionsshaders.txt" to i18n("modpack.files.optionsshaders_txt"),
|
||||
"minecraft/mods/VoxelMods" to i18n("modpack.files.mods.voxelmods"),
|
||||
"minecraft/dumps" to i18n("modpack.files.dumps"),
|
||||
"minecraft/blueprints" to i18n("modpack.files.blueprints"),
|
||||
"minecraft/scripts" to i18n("modpack.files.scripts")
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.export
|
||||
|
||||
import com.jfoenix.controls.JFXButton
|
||||
import com.jfoenix.controls.JFXTextArea
|
||||
import com.jfoenix.controls.JFXTextField
|
||||
import com.jfoenix.controls.JFXToggleButton
|
||||
import javafx.fxml.FXML
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.control.ScrollPane
|
||||
import javafx.scene.layout.StackPane
|
||||
import javafx.stage.FileChooser
|
||||
import org.jackhuang.hmcl.i18n
|
||||
import org.jackhuang.hmcl.setting.Profile
|
||||
import org.jackhuang.hmcl.setting.Settings
|
||||
import org.jackhuang.hmcl.ui.Controllers
|
||||
import org.jackhuang.hmcl.ui.loadFXML
|
||||
import org.jackhuang.hmcl.ui.smoothScrolling
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardController
|
||||
import org.jackhuang.hmcl.ui.wizard.WizardPage
|
||||
import org.jackhuang.hmcl.util.onInvalidated
|
||||
|
||||
class ModpackInfoPage(private val controller: WizardController, version: String): StackPane(), WizardPage {
|
||||
override val title: String = i18n("modpack.wizard.step.1.title")
|
||||
@FXML lateinit var lblVersionName: Label
|
||||
@FXML lateinit var txtModpackName: JFXTextField
|
||||
@FXML lateinit var txtModpackAuthor: JFXTextField
|
||||
@FXML lateinit var txtModpackVersion: JFXTextField
|
||||
@FXML lateinit var txtModpackDescription: JFXTextArea
|
||||
@FXML lateinit var chkIncludeLauncher: JFXToggleButton
|
||||
@FXML lateinit var btnNext: JFXButton
|
||||
@FXML lateinit var scroll: ScrollPane
|
||||
|
||||
init {
|
||||
loadFXML("/assets/fxml/modpack/info.fxml")
|
||||
scroll.smoothScrolling()
|
||||
txtModpackName.text = version
|
||||
txtModpackName.textProperty().onInvalidated(this::checkValidation)
|
||||
txtModpackAuthor.textProperty().onInvalidated(this::checkValidation)
|
||||
txtModpackVersion.textProperty().onInvalidated(this::checkValidation)
|
||||
txtModpackAuthor.text = Settings.selectedAccount?.username ?: ""
|
||||
lblVersionName.text = version
|
||||
}
|
||||
|
||||
private fun checkValidation() {
|
||||
btnNext.isDisable = !txtModpackName.validate() || !txtModpackVersion.validate() || !txtModpackAuthor.validate()
|
||||
}
|
||||
|
||||
fun onNext() {
|
||||
val fileChooser = FileChooser()
|
||||
fileChooser.title = i18n("modpack.wizard.step.initialization.save")
|
||||
fileChooser.extensionFilters += FileChooser.ExtensionFilter(i18n("modpack"), "*.zip")
|
||||
val file = fileChooser.showSaveDialog(Controllers.stage)
|
||||
if (file == null) {
|
||||
Controllers.navigate(null)
|
||||
return
|
||||
}
|
||||
controller.settings[MODPACK_NAME] = txtModpackName.text
|
||||
controller.settings[MODPACK_VERSION] = txtModpackVersion.text
|
||||
controller.settings[MODPACK_AUTHOR] = txtModpackAuthor.text
|
||||
controller.settings[MODPACK_FILE] = file
|
||||
controller.settings[MODPACK_DESCRIPTION] = txtModpackDescription.text
|
||||
controller.settings[MODPACK_INCLUDE_LAUNCHER] = chkIncludeLauncher.isSelected
|
||||
controller.onNext()
|
||||
}
|
||||
|
||||
override fun cleanup(settings: MutableMap<String, Any>) {
|
||||
controller.settings.remove(MODPACK_NAME)
|
||||
controller.settings.remove(MODPACK_VERSION)
|
||||
controller.settings.remove(MODPACK_AUTHOR)
|
||||
controller.settings.remove(MODPACK_DESCRIPTION)
|
||||
controller.settings.remove(MODPACK_INCLUDE_LAUNCHER)
|
||||
controller.settings.remove(MODPACK_FILE)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val MODPACK_NAME = "modpack.name"
|
||||
const val MODPACK_VERSION = "modpack.version"
|
||||
const val MODPACK_AUTHOR = "modpack.author"
|
||||
const val MODPACK_DESCRIPTION = "modpack.description"
|
||||
const val MODPACK_INCLUDE_LAUNCHER = "modpack.include_launcher"
|
||||
const val MODPACK_FILE = "modpack.file"
|
||||
}
|
||||
}
|
||||
@@ -67,8 +67,7 @@ interface AbstractWizardDisplayer : WizardDisplayer {
|
||||
running = false
|
||||
}
|
||||
|
||||
override val isRunning: Boolean
|
||||
get() = running
|
||||
override fun isRunning() = running
|
||||
|
||||
})
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.wizard
|
||||
|
||||
import javafx.scene.Node
|
||||
|
||||
abstract class WizardProvider {
|
||||
abstract fun start(settings: MutableMap<String, Any>)
|
||||
abstract fun finish(settings: MutableMap<String, Any>): Any?
|
||||
abstract fun createPage(controller: WizardController, step: Int, settings: MutableMap<String, Any>): Node
|
||||
abstract fun cancel(): Boolean
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.util
|
||||
|
||||
import sun.misc.Unsafe
|
||||
import java.lang.reflect.AccessibleObject
|
||||
import java.lang.reflect.Executable
|
||||
import java.lang.reflect.Method
|
||||
import java.security.AccessController
|
||||
import java.security.PrivilegedExceptionAction
|
||||
|
||||
private val unsafe: Unsafe = AccessController.doPrivileged(PrivilegedExceptionAction {
|
||||
val theUnsafe = Unsafe::class.java.getDeclaredField("theUnsafe")
|
||||
theUnsafe.isAccessible = true
|
||||
theUnsafe.get(null) as Unsafe
|
||||
})
|
||||
private val objectFieldOffset = unsafe.objectFieldOffset(
|
||||
AccessibleObject::class.java.getDeclaredField("override"))
|
||||
|
||||
private fun AccessibleObject.setAccessibleForcibly() =
|
||||
unsafe.putBoolean(this, objectFieldOffset, true)
|
||||
|
||||
fun getMethod(obj: Any, methodName: String): Method? =
|
||||
getMethod(obj.javaClass, methodName)
|
||||
|
||||
fun getMethod(cls: Class<*>, methodName: String): Method? =
|
||||
try {
|
||||
cls.getDeclaredMethod(methodName).apply { setAccessibleForcibly() }
|
||||
} catch (ex: Throwable) {
|
||||
null
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a field, method or constructor by reflection.
|
||||
*
|
||||
* @param name the field or method name of [clazz], "new" if you are looking for a constructor.
|
||||
* @param args the arguments of the method, empty if you are looking for a field or non-argument method.
|
||||
*/
|
||||
fun Any.call(name: String, vararg args: Any?): Any? {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return javaClass.call(name, this, *args)
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a constructor by reflection.
|
||||
*
|
||||
* @param args the arguments of the method, empty if you are looking for a field or non-argument method.
|
||||
*/
|
||||
fun Class<*>.construct(vararg args: Any?) = call("new", null, *args)
|
||||
|
||||
/**
|
||||
* Call a field, method or constructor by reflection.
|
||||
*
|
||||
* @param name the field or method name of [clazz], "new" if you are looking for a constructor.
|
||||
* @param obj null for constructors or static/object methods/fields.
|
||||
* @param args the arguments of the method, empty if you are looking for a field or non-argument method.
|
||||
*/
|
||||
fun Class<*>.call(name: String, obj: Any? = null, vararg args: Any?): Any? {
|
||||
try {
|
||||
if (args.isEmpty())
|
||||
try {
|
||||
return getDeclaredField(name).get(obj)
|
||||
} catch(ignored: NoSuchFieldException) {
|
||||
}
|
||||
if (name == "new")
|
||||
declaredConstructors.forEach {
|
||||
if (checkParameter(it, *args)) return it.newInstance(*args)
|
||||
}
|
||||
else
|
||||
return forMethod(name, *args)!!.invoke(obj, *args)
|
||||
throw RuntimeException()
|
||||
} catch(e: Exception) {
|
||||
throw IllegalArgumentException("Cannot find `$name` in Class `${this.name}`, please check your code.", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun Class<*>.forMethod(name: String, vararg args: Any?): Method? =
|
||||
declaredMethods.filter { it.name == name }.filter { checkParameter(it, *args) }.firstOrNull()
|
||||
|
||||
fun checkParameter(exec: Executable, vararg args: Any?): Boolean {
|
||||
val cArgs = exec.parameterTypes
|
||||
if (args.size == cArgs.size) {
|
||||
for (i in 0 until args.size) {
|
||||
val arg = args[i]
|
||||
// primitive variable cannot be null
|
||||
if (if (arg != null) !isInstance(cArgs[i], arg) else cArgs[i].isPrimitive)
|
||||
return false
|
||||
}
|
||||
exec.setAccessibleForcibly()
|
||||
return true
|
||||
} else
|
||||
return false
|
||||
}
|
||||
|
||||
fun isInstance(superClass: Class<*>, obj: Any): Boolean {
|
||||
if (superClass.isInstance(obj)) return true
|
||||
else if (PRIMITIVES[superClass.name] == obj.javaClass) return true
|
||||
return false
|
||||
}
|
||||
|
||||
fun isInstance(superClass: Class<*>, clazz: Class<*>): Boolean {
|
||||
for (i in clazz.interfaces)
|
||||
if (isInstance(superClass, i) || PRIMITIVES[superClass.name] == clazz)
|
||||
return true
|
||||
return isSubClass(superClass, clazz)
|
||||
}
|
||||
|
||||
fun isSubClass(superClass: Class<*>, clazz: Class<*>): Boolean {
|
||||
var clz: Class<*>? = clazz
|
||||
do {
|
||||
if (superClass == clz) return true
|
||||
clz = clz?.superclass
|
||||
} while (clz != null)
|
||||
return false
|
||||
}
|
||||
|
||||
fun <T> Class<T>.objectInstance() = call("INSTANCE")
|
||||
|
||||
val PRIMITIVES = mapOf(
|
||||
"byte" to java.lang.Byte::class.java,
|
||||
"short" to java.lang.Short::class.java,
|
||||
"int" to java.lang.Integer::class.java,
|
||||
"long" to java.lang.Long::class.java,
|
||||
"char" to java.lang.Character::class.java,
|
||||
"float" to java.lang.Float::class.java,
|
||||
"double" to java.lang.Double::class.java,
|
||||
"boolean" to java.lang.Boolean::class.java
|
||||
)
|
||||
@@ -9,7 +9,7 @@
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
fx:id="rootPane"
|
||||
fx:controller="org.jackhuang.hmcl.ui.ModController">
|
||||
<JFXSpinner style="-fx-radius:16" styleClass="materialDesign-purple, first-spinner" />
|
||||
<JFXSpinner fx:id="spinner" style="-fx-radius:16" styleClass="materialDesign-purple, first-spinner" />
|
||||
<StackPane fx:id="contentPane">
|
||||
<ScrollPane fx:id="scrollPane" fitToWidth="true" fitToHeight="true">
|
||||
<VBox fx:id="modPane" spacing="10" style="-fx-padding: 20 20 70 20;">
|
||||
|
||||
@@ -43,7 +43,8 @@ public final class ModManager {
|
||||
Optional.ofNullable(modsDirectory.listFiles()).map(Arrays::stream).ifPresent(files -> files.forEach(modFile -> {
|
||||
if (modFile.isDirectory() && VersionNumber.parseVersion(modFile.getName()) != null)
|
||||
Optional.ofNullable(modFile.listFiles()).map(Arrays::stream).ifPresent(x -> x.forEach(puter));
|
||||
puter.accept(modFile);
|
||||
else
|
||||
puter.accept(modFile);
|
||||
}));
|
||||
return modCache.get(id);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import javafx.beans.property.ReadOnlyDoubleProperty;
|
||||
import javafx.beans.property.ReadOnlyDoubleWrapper;
|
||||
@@ -58,7 +57,7 @@ public abstract class Task {
|
||||
|
||||
/**
|
||||
* True if requires all {@link #getDependents} finishing successfully.
|
||||
*
|
||||
* <p>
|
||||
* **Note** if this field is set false, you are not supposed to invoke [run]
|
||||
*
|
||||
* @defaultValue true
|
||||
@@ -69,7 +68,7 @@ public abstract class Task {
|
||||
|
||||
/**
|
||||
* True if requires all {@link #getDependencies} finishing successfully.
|
||||
*
|
||||
* <p>
|
||||
* **Note** if this field is set false, you are not supposed to invoke [run]
|
||||
*
|
||||
* @defaultValue false
|
||||
@@ -99,8 +98,8 @@ public abstract class Task {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Thread#isInterrupted
|
||||
* @throws InterruptedException if current thread is interrupted
|
||||
* @see Thread#isInterrupted
|
||||
*/
|
||||
public abstract void execute() throws Exception;
|
||||
|
||||
@@ -128,10 +127,10 @@ public abstract class Task {
|
||||
|
||||
private long lastTime = Long.MIN_VALUE;
|
||||
private final AtomicReference<Double> progressUpdate = new AtomicReference<>();
|
||||
private final ReadOnlyDoubleWrapper progressProperty = new ReadOnlyDoubleWrapper(this, "progress", 0);
|
||||
private final ReadOnlyDoubleWrapper progress = new ReadOnlyDoubleWrapper(this, "progress", 0);
|
||||
|
||||
public ReadOnlyDoubleProperty getProgressProperty() {
|
||||
return progressProperty.getReadOnlyProperty();
|
||||
public ReadOnlyDoubleProperty progressProperty() {
|
||||
return progress.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
protected void updateProgress(int progress, int total) {
|
||||
@@ -149,18 +148,18 @@ public abstract class Task {
|
||||
}
|
||||
|
||||
protected void updateProgressImmediately(double progress) {
|
||||
Properties.updateAsync(progressProperty, progress, progressUpdate);
|
||||
Properties.updateAsync(this.progress, progress, progressUpdate);
|
||||
}
|
||||
|
||||
private final AtomicReference<String> messageUpdate = new AtomicReference<>();
|
||||
private final ReadOnlyStringWrapper messageProperty = new ReadOnlyStringWrapper(this, "message", null);
|
||||
private final ReadOnlyStringWrapper message = new ReadOnlyStringWrapper(this, "message", null);
|
||||
|
||||
public final ReadOnlyStringProperty getMessageProperty() {
|
||||
return messageProperty.getReadOnlyProperty();
|
||||
public final ReadOnlyStringProperty messageProperty() {
|
||||
return message.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
protected final void updateMessage(String newMessage) {
|
||||
Properties.updateAsync(messageProperty, newMessage, messageUpdate);
|
||||
Properties.updateAsync(message, newMessage, messageUpdate);
|
||||
}
|
||||
|
||||
public final void run() throws Exception {
|
||||
@@ -173,11 +172,11 @@ public abstract class Task {
|
||||
}
|
||||
|
||||
private void doSubTask(Task task) throws Exception {
|
||||
messageProperty.bind(task.messageProperty);
|
||||
progressProperty.bind(task.progressProperty);
|
||||
message.bind(task.message);
|
||||
progress.bind(task.progress);
|
||||
task.run();
|
||||
messageProperty.unbind();
|
||||
progressProperty.unbind();
|
||||
message.unbind();
|
||||
progress.unbind();
|
||||
}
|
||||
|
||||
public final TaskExecutor executor() {
|
||||
@@ -228,6 +227,11 @@ public abstract class Task {
|
||||
return new CoupleTask<>(this, b, false);
|
||||
}
|
||||
|
||||
public static Task empty() {
|
||||
return of(s -> {
|
||||
});
|
||||
}
|
||||
|
||||
public static Task of(ExceptionalRunnable<?> runnable) {
|
||||
return of(s -> runnable.run());
|
||||
}
|
||||
|
||||
@@ -256,6 +256,14 @@ public final class Lang {
|
||||
}
|
||||
}
|
||||
|
||||
public static Integer toIntOrNull(String string) {
|
||||
try {
|
||||
return Integer.parseInt(string);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T nonNull(T... t) {
|
||||
for (T a : t) if (a != null) return a;
|
||||
return null;
|
||||
|
||||
@@ -117,6 +117,10 @@ public final class ReflectionHelper {
|
||||
}
|
||||
}
|
||||
|
||||
public static Object construct(Class<?> clazz, Object... args) {
|
||||
return call(clazz, "new", null, args);
|
||||
}
|
||||
|
||||
public static Object call(Object obj, String name, Object... args) {
|
||||
return call(obj.getClass(), name, obj, args);
|
||||
}
|
||||
@@ -136,12 +140,7 @@ public final class ReflectionHelper {
|
||||
}
|
||||
|
||||
public static boolean isInstance(Class<?> superClass, Object obj) {
|
||||
if (superClass.isInstance(obj))
|
||||
return true;
|
||||
else if (PRIMITIVES.get(superClass.getName()) == obj.getClass())
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
return superClass.isInstance(obj) || PRIMITIVES.get(superClass.getName()) == obj.getClass();
|
||||
}
|
||||
|
||||
public static Optional<Method> forMethod(Class<?> cls, String name, Object... args) {
|
||||
|
||||
Reference in New Issue
Block a user