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
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* 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 org.jackhuang.hmcl.setting.Profile
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This event gets fired when the selected profile changed.
|
* This event gets fired when the selected profile changed.
|
||||||
* <br></br>
|
* <br>
|
||||||
* This event is fired on the [org.jackhuang.hmcl.event.EVENT_BUS]
|
* This event is fired on the {@link org.jackhuang.hmcl.event.EventBus#EVENT_BUS}
|
||||||
* @param source [org.jackhuang.hmcl.setting.Settings]
|
|
||||||
* *
|
|
||||||
* @param Profile the new profile.
|
|
||||||
* *
|
|
||||||
* @author huangyuhui
|
* @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.
|
* Constructor.
|
||||||
* <br></br>
|
*
|
||||||
* This event is fired on the [org.jackhuang.hmcl.event.EVENT_BUS]
|
* @param source {@link org.jackhuang.hmcl.setting.Settings}
|
||||||
* @param source [org.jackhuang.hmcl.setting.Settings]
|
* @param profile the new profile.
|
||||||
* *
|
*/
|
||||||
* @author huangyuhui
|
public ProfileChangedEvent(Object source, Profile profile) {
|
||||||
*/
|
super(source);
|
||||||
class ProfileLoadingEvent(source: Any) : Event(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
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* 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
|
* This event gets fired when loading profiles.
|
||||||
import javafx.scene.web.WebView
|
* <br>
|
||||||
import javafx.stage.Stage
|
* 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()
|
* Constructor.
|
||||||
init {
|
*
|
||||||
scene = Scene(webView, 800.0, 480.0)
|
* @param source {@link org.jackhuang.hmcl.setting.Settings}
|
||||||
scene.stylesheets.addAll(*stylesheets)
|
*/
|
||||||
icons += Image("/assets/img/icon.png")
|
public ProfileLoadingEvent(Object source) {
|
||||||
|
super(source);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ public final class LauncherHelper {
|
|||||||
public void onFinished(Task task) {
|
public void onFinished(Task task) {
|
||||||
finished.incrementAndGet();
|
finished.incrementAndGet();
|
||||||
Platform.runLater(() -> {
|
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)
|
if (state == LoadingState.DONE)
|
||||||
Controllers.INSTANCE.closeDialog();
|
Controllers.INSTANCE.closeDialog();
|
||||||
|
|
||||||
launchingStepsPane.getLblCurrentState().setText(state.toString());
|
launchingStepsPane.setCurrentState(state.toString());
|
||||||
launchingStepsPane.getLblSteps().setText((state.ordinal() + 1) + " / " + LoadingState.values().length);
|
launchingStepsPane.setSteps((state.ordinal() + 1) + " / " + LoadingState.values().length);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkExit(LauncherVisibility v) {
|
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 {
|
public final class LogWindow extends Stage {
|
||||||
|
|
||||||
private final ReadOnlyIntegerWrapper fatalProperty = new ReadOnlyIntegerWrapper(0);
|
private final ReadOnlyIntegerWrapper fatal = new ReadOnlyIntegerWrapper(0);
|
||||||
private final ReadOnlyIntegerWrapper errorProperty = new ReadOnlyIntegerWrapper(0);
|
private final ReadOnlyIntegerWrapper error = new ReadOnlyIntegerWrapper(0);
|
||||||
private final ReadOnlyIntegerWrapper warnProperty = new ReadOnlyIntegerWrapper(0);
|
private final ReadOnlyIntegerWrapper warn = new ReadOnlyIntegerWrapper(0);
|
||||||
private final ReadOnlyIntegerWrapper infoProperty = new ReadOnlyIntegerWrapper(0);
|
private final ReadOnlyIntegerWrapper info = new ReadOnlyIntegerWrapper(0);
|
||||||
private final ReadOnlyIntegerWrapper debugProperty = new ReadOnlyIntegerWrapper(0);
|
private final ReadOnlyIntegerWrapper debug = new ReadOnlyIntegerWrapper(0);
|
||||||
private final LogWindowImpl impl = new LogWindowImpl();
|
private final LogWindowImpl impl = new LogWindowImpl();
|
||||||
private final CountDownLatch latch = new CountDownLatch(1);
|
private final CountDownLatch latch = new CountDownLatch(1);
|
||||||
public final EventManager<Event> onDone = new EventManager<>();
|
public final EventManager<Event> onDone = new EventManager<>();
|
||||||
@@ -75,43 +75,43 @@ public final class LogWindow extends Stage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlyIntegerProperty fatalProperty() {
|
public ReadOnlyIntegerProperty fatalProperty() {
|
||||||
return fatalProperty.getReadOnlyProperty();
|
return fatal.getReadOnlyProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getFatal() {
|
public int getFatal() {
|
||||||
return fatalProperty.get();
|
return fatal.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlyIntegerProperty errorProperty() {
|
public ReadOnlyIntegerProperty errorProperty() {
|
||||||
return errorProperty.getReadOnlyProperty();
|
return error.getReadOnlyProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getError() {
|
public int getError() {
|
||||||
return errorProperty.get();
|
return error.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlyIntegerProperty warnProperty() {
|
public ReadOnlyIntegerProperty warnProperty() {
|
||||||
return warnProperty.getReadOnlyProperty();
|
return warn.getReadOnlyProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getWarn() {
|
public int getWarn() {
|
||||||
return warnProperty.get();
|
return warn.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlyIntegerProperty infoProperty() {
|
public ReadOnlyIntegerProperty infoProperty() {
|
||||||
return infoProperty.getReadOnlyProperty();
|
return info.getReadOnlyProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getInfo() {
|
public int getInfo() {
|
||||||
return infoProperty.get();
|
return info.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlyIntegerProperty debugProperty() {
|
public ReadOnlyIntegerProperty debugProperty() {
|
||||||
return debugProperty.getReadOnlyProperty();
|
return debug.getReadOnlyProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getDebug() {
|
public int getDebug() {
|
||||||
return debugProperty.get();
|
return debug.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void logLine(String line, Log4jLevel level) {
|
public void logLine(String line, Log4jLevel level) {
|
||||||
@@ -125,45 +125,45 @@ public final class LogWindow extends Stage {
|
|||||||
|
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case FATAL:
|
case FATAL:
|
||||||
fatalProperty.set(fatalProperty.get() + 1);
|
fatal.set(fatal.get() + 1);
|
||||||
break;
|
break;
|
||||||
case ERROR:
|
case ERROR:
|
||||||
errorProperty.set(errorProperty.get() + 1);
|
error.set(error.get() + 1);
|
||||||
break;
|
break;
|
||||||
case WARN:
|
case WARN:
|
||||||
warnProperty.set(warnProperty.get() + 1);
|
warn.set(warn.get() + 1);
|
||||||
break;
|
break;
|
||||||
case INFO:
|
case INFO:
|
||||||
infoProperty.set(infoProperty.get() + 1);
|
info.set(info.get() + 1);
|
||||||
break;
|
break;
|
||||||
case DEBUG:
|
case DEBUG:
|
||||||
debugProperty.set(debugProperty.get() + 1);
|
debug.set(debug.get() + 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class LogWindowImpl extends StackPane {
|
private class LogWindowImpl extends StackPane {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public WebView webView;
|
private WebView webView;
|
||||||
@FXML
|
@FXML
|
||||||
public ToggleButton btnFatals;
|
private ToggleButton btnFatals;
|
||||||
@FXML
|
@FXML
|
||||||
public ToggleButton btnErrors;
|
private ToggleButton btnErrors;
|
||||||
@FXML
|
@FXML
|
||||||
public ToggleButton btnWarns;
|
private ToggleButton btnWarns;
|
||||||
@FXML
|
@FXML
|
||||||
public ToggleButton btnInfos;
|
private ToggleButton btnInfos;
|
||||||
@FXML
|
@FXML
|
||||||
public ToggleButton btnDebugs;
|
private ToggleButton btnDebugs;
|
||||||
@FXML
|
@FXML
|
||||||
public ComboBox<String> cboLines;
|
private ComboBox<String> cboLines;
|
||||||
|
|
||||||
WebEngine engine;
|
WebEngine engine;
|
||||||
Node body;
|
Node body;
|
||||||
Document document;
|
Document document;
|
||||||
|
|
||||||
public LogWindowImpl() {
|
LogWindowImpl() {
|
||||||
FXUtilsKt.loadFXML(this, "/assets/fxml/log.fxml");
|
FXUtilsKt.loadFXML(this, "/assets/fxml/log.fxml");
|
||||||
|
|
||||||
engine = webView.getEngine();
|
engine = webView.getEngine();
|
||||||
@@ -194,11 +194,11 @@ public final class LogWindow extends Stage {
|
|||||||
if (!flag)
|
if (!flag)
|
||||||
cboLines.getSelectionModel().select(0);
|
cboLines.getSelectionModel().select(0);
|
||||||
|
|
||||||
btnFatals.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(fatalProperty.get()) + " fatals", fatalProperty));
|
btnFatals.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(fatal.get()) + " fatals", fatal));
|
||||||
btnErrors.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(errorProperty.get()) + " errors", errorProperty));
|
btnErrors.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(error.get()) + " errors", error));
|
||||||
btnWarns.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(warnProperty.get()) + " warns", warnProperty));
|
btnWarns.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(warn.get()) + " warns", warn));
|
||||||
btnInfos.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(infoProperty.get()) + " infos", infoProperty));
|
btnInfos.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(info.get()) + " infos", info));
|
||||||
btnDebugs.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(debugProperty.get()) + " debugs", debugProperty));
|
btnDebugs.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(debug.get()) + " debugs", debug));
|
||||||
|
|
||||||
btnFatals.selectedProperty().addListener(o -> specificChanged());
|
btnFatals.selectedProperty().addListener(o -> specificChanged());
|
||||||
btnErrors.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
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* 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 com.jfoenix.controls.JFXButton;
|
||||||
import javafx.scene.control.Label
|
import com.jfoenix.controls.JFXDialog;
|
||||||
import javafx.scene.layout.StackPane
|
import javafx.fxml.FXML;
|
||||||
import org.jackhuang.hmcl.download.RemoteVersion
|
import javafx.scene.control.Label;
|
||||||
import org.jackhuang.hmcl.ui.loadFXML
|
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
|
||||||
@FXML lateinit var lblGameVersion: Label
|
private JFXButton acceptButton;
|
||||||
|
@FXML
|
||||||
|
private Label content;
|
||||||
|
|
||||||
private var handler: () -> Unit = {}
|
public MessageDialogPane(String text, JFXDialog dialog) {
|
||||||
|
this.text = text;
|
||||||
|
this.dialog = dialog;
|
||||||
|
|
||||||
init {
|
FXUtilsKt.loadFXML(this, "/assets/fxml/message-dialog.fxml");
|
||||||
loadFXML("/assets/fxml/download/versions-list-item.fxml")
|
content.setText(text);
|
||||||
lblSelfVersion.text = remoteVersion.selfVersion
|
acceptButton.setOnMouseClicked(e -> dialog.close());
|
||||||
lblGameVersion.text = remoteVersion.gameVersion
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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 {
|
public interface AnimationHandler {
|
||||||
Node getSnapshot();
|
Node getSnapshot();
|
||||||
Duration getDuration();
|
Duration getDuration();
|
||||||
Pane getView();
|
Pane getCurrentRoot();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,14 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* 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 {
|
import javafx.animation.KeyFrame;
|
||||||
fun onNavigate(settings: MutableMap<String, Any>) {}
|
|
||||||
fun cleanup(settings: MutableMap<String, Any>)
|
import java.util.List;
|
||||||
val title: String
|
|
||||||
}
|
@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
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* 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 {
|
public interface DecoratorPage {
|
||||||
val titleProperty: StringProperty
|
StringProperty titleProperty();
|
||||||
|
|
||||||
fun onClose() {}
|
default void onClose() {
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@@ -15,22 +15,26 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* 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 java.util.Map;
|
||||||
import javafx.fxml.FXML
|
|
||||||
import javafx.scene.control.Label
|
|
||||||
import javafx.scene.layout.StackPane
|
|
||||||
|
|
||||||
class LaunchingStepsPane(): StackPane() {
|
/**
|
||||||
@FXML lateinit var pgsTasks: JFXProgressBar
|
* @author huangyuhui
|
||||||
@FXML lateinit var lblCurrentState: Label
|
*/
|
||||||
@FXML lateinit var lblSteps: Label
|
public abstract class DeferredWizardResult {
|
||||||
init {
|
private final boolean canAbort;
|
||||||
loadFXML("/assets/fxml/launching-steps.fxml")
|
|
||||||
|
|
||||||
limitHeight(200.0)
|
public DeferredWizardResult() {
|
||||||
limitWidth(400.0)
|
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
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* 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
|
* 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.
|
* will take a while and needs to happen on a background thread.
|
||||||
* @author Tim Boudreau
|
* @author Tim Boudreau
|
||||||
*/
|
*/
|
||||||
interface ResultProgressHandle {
|
public interface ResultProgressHandle {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the current position and total number of steps. Note it is
|
* Set the current position and total number of steps. Note it is
|
||||||
* inadvisable to be holding any locks when calling this method, as it
|
* inadvisable to be holding any locks when calling this method, as it
|
||||||
* may immediately update the GUI using
|
* may immediately update the GUI using
|
||||||
* `EventQueue.invokeAndWait()`.
|
* `EventQueue.invokeAndWait()`.
|
||||||
|
*
|
||||||
* @param currentStep the current step in the progress of computing the
|
* @param currentStep the current step in the progress of computing the
|
||||||
* * result.
|
* * result.
|
||||||
* *
|
* *
|
||||||
* @param totalSteps the total number of steps. Must be greater than
|
* @param totalSteps the total number of steps. Must be greater than
|
||||||
* * or equal to currentStep.
|
* * 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
|
* 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
|
* @param totalSteps the total number of steps. Must be greater than
|
||||||
* * or equal to currentStep.
|
* * 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
|
* 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
|
* @param description Text to describe what is being done, which can
|
||||||
* * be displayed in the UI.
|
* * 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
|
* 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.
|
* called, a runtime exception may be thrown.
|
||||||
* @param result the Object which was computed, if any.
|
* @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
|
* 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
|
* @param canNavigateBack whether or not the Prev button should be
|
||||||
* * enabled.
|
* * 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.
|
* 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.
|
* @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
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* 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 javafx.scene.Node;
|
||||||
import org.jackhuang.hmcl.task.Task
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
|
||||||
interface WizardDisplayer {
|
import java.util.Map;
|
||||||
fun onStart()
|
|
||||||
fun onEnd()
|
|
||||||
fun onCancel()
|
|
||||||
|
|
||||||
fun navigateTo(page: Node, nav: Navigation.NavigationDirection)
|
public interface WizardDisplayer {
|
||||||
|
void onStart();
|
||||||
fun handleDeferredWizardResult(settings: Map<String, Any>, deferredResult: DeferredWizardResult)
|
void onEnd();
|
||||||
|
void onCancel();
|
||||||
fun handleTask(settings: Map<String, Any>, task: Task)
|
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
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* 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 {
|
PROCEED {
|
||||||
override val deferredComputation = false
|
@Override
|
||||||
|
public boolean getDeferredComputation() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
DENY {
|
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
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* 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)
|
public interface WizardPage {
|
||||||
open fun abort() { }
|
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
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
* 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>() {
|
import java.util.Map;
|
||||||
/** {@inheritDoc} */
|
|
||||||
override fun fromString(value: String?) = value?.toIntOrNull()
|
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
public interface WizardProvider {
|
||||||
override fun toString(value: Int?) = value?.toString() ?: ""
|
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()
|
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()))
|
icon.translateYProperty().bind(Bindings.createDoubleBinding(Callable { header.boundsInParent.height - icon.height / 2 - 32.0 }, header.boundsInParentProperty(), icon.heightProperty()))
|
||||||
|
|
||||||
chkSelected.properties["account"] = account
|
chkSelected.properties["account"] = account
|
||||||
chkSelected.isSelected = Settings.selectedAccount == account
|
chkSelected.isSelected = Settings.INSTANCE.selectedAccount == account
|
||||||
lblUser.text = account.username
|
lblUser.text = account.username
|
||||||
lblType.text = accountType(account)
|
lblType.text = accountType(account)
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ import org.jackhuang.hmcl.util.onChangeAndOperate
|
|||||||
import org.jackhuang.hmcl.util.taskResult
|
import org.jackhuang.hmcl.util.taskResult
|
||||||
|
|
||||||
class AccountsPage() : StackPane(), DecoratorPage {
|
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 scrollPane: ScrollPane
|
||||||
@FXML lateinit var masonryPane: JFXMasonryPane
|
@FXML lateinit var masonryPane: JFXMasonryPane
|
||||||
@@ -74,7 +75,7 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
|||||||
txtPassword.setOnAction { onCreationAccept() }
|
txtPassword.setOnAction { onCreationAccept() }
|
||||||
txtUsername.setOnAction { onCreationAccept() }
|
txtUsername.setOnAction { onCreationAccept() }
|
||||||
|
|
||||||
Settings.selectedAccountProperty.onChangeAndOperate { account ->
|
Settings.INSTANCE.selectedAccountProperty().onChangeAndOperate { account ->
|
||||||
masonryPane.children.forEach { node ->
|
masonryPane.children.forEach { node ->
|
||||||
if (node is AccountItem) {
|
if (node is AccountItem) {
|
||||||
node.chkSelected.isSelected = account?.username == node.lblUser.text
|
node.chkSelected.isSelected = account?.username == node.lblUser.text
|
||||||
@@ -84,7 +85,7 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
|||||||
|
|
||||||
loadAccounts()
|
loadAccounts()
|
||||||
|
|
||||||
if (Settings.getAccounts().isEmpty())
|
if (Settings.INSTANCE.getAccounts().isEmpty())
|
||||||
addNewAccount()
|
addNewAccount()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,12 +93,12 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
|||||||
val children = mutableListOf<Node>()
|
val children = mutableListOf<Node>()
|
||||||
var i = 0
|
var i = 0
|
||||||
val group = ToggleGroup()
|
val group = ToggleGroup()
|
||||||
for ((_, account) in Settings.getAccounts()) {
|
for ((_, account) in Settings.INSTANCE.getAccounts()) {
|
||||||
children += buildNode(++i, account, group)
|
children += buildNode(++i, account, group)
|
||||||
}
|
}
|
||||||
group.selectedToggleProperty().onChange {
|
group.selectedToggleProperty().onChange {
|
||||||
if (it != null)
|
if (it != null)
|
||||||
Settings.selectedAccount = it.properties["account"] as Account
|
Settings.INSTANCE.selectedAccount = it.properties["account"] as Account
|
||||||
}
|
}
|
||||||
masonryPane.resetChildren(children)
|
masonryPane.resetChildren(children)
|
||||||
Platform.runLater {
|
Platform.runLater {
|
||||||
@@ -109,7 +110,7 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
|||||||
private fun buildNode(i: Int, account: Account, group: ToggleGroup): Node {
|
private fun buildNode(i: Int, account: Account, group: ToggleGroup): Node {
|
||||||
return AccountItem(i, account, group).apply {
|
return AccountItem(i, account, group).apply {
|
||||||
btnDelete.setOnMouseClicked {
|
btnDelete.setOnMouseClicked {
|
||||||
Settings.deleteAccount(account.username)
|
Settings.INSTANCE.deleteAccount(account.username)
|
||||||
Platform.runLater(this@AccountsPage::loadAccounts)
|
Platform.runLater(this@AccountsPage::loadAccounts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,7 +136,7 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
|||||||
else -> throw UnsupportedOperationException()
|
else -> throw UnsupportedOperationException()
|
||||||
}
|
}
|
||||||
|
|
||||||
account.logIn(HMCLMultiCharacterSelector.INSTANCE, Settings.proxy)
|
account.logIn(HMCLMultiCharacterSelector.INSTANCE, Settings.INSTANCE.proxy)
|
||||||
account
|
account
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e
|
e
|
||||||
@@ -143,7 +144,7 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
|||||||
}.subscribe(Schedulers.javafx()) {
|
}.subscribe(Schedulers.javafx()) {
|
||||||
val account: Any = it["create_account"]
|
val account: Any = it["create_account"]
|
||||||
if (account is Account) {
|
if (account is Account) {
|
||||||
Settings.addAccount(account)
|
Settings.INSTANCE.addAccount(account)
|
||||||
dialog.close()
|
dialog.close()
|
||||||
loadAccounts()
|
loadAccounts()
|
||||||
} else if (account is InvalidCredentialsException) {
|
} 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)
|
decorator.showPage(null)
|
||||||
leftPaneController = LeftPaneController(decorator.leftPane)
|
leftPaneController = LeftPaneController(decorator.leftPane)
|
||||||
|
|
||||||
Settings.onProfileLoading()
|
Settings.INSTANCE.onProfileLoading()
|
||||||
task { JavaVersion.initialize() }.start()
|
task { JavaVersion.initialize() }.start()
|
||||||
|
|
||||||
decorator.isCustomMaximize = false
|
decorator.isCustomMaximize = false
|
||||||
|
|||||||
@@ -388,7 +388,7 @@ class Decorator @JvmOverloads constructor(private val primaryStage: Stage, priva
|
|||||||
titleLabel.text = prefix + content.title
|
titleLabel.text = prefix + content.title
|
||||||
|
|
||||||
if (content is DecoratorPage)
|
if (content is DecoratorPage)
|
||||||
titleLabel.textProperty().bind(content.titleProperty)
|
titleLabel.textProperty().bind(content.titleProperty())
|
||||||
}
|
}
|
||||||
|
|
||||||
var category: String? = null
|
var category: String? = null
|
||||||
@@ -418,6 +418,7 @@ class Decorator @JvmOverloads constructor(private val primaryStage: Stage, priva
|
|||||||
return dialog
|
return dialog
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmOverloads
|
||||||
fun startWizard(wizardProvider: WizardProvider, category: String? = null) {
|
fun startWizard(wizardProvider: WizardProvider, category: String? = null) {
|
||||||
this.category = category
|
this.category = category
|
||||||
wizardController.provider = wizardProvider
|
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.Main
|
||||||
import org.jackhuang.hmcl.util.*
|
import org.jackhuang.hmcl.util.*
|
||||||
import org.jackhuang.hmcl.util.Logging.LOG
|
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.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.logging.Level
|
import java.util.logging.Level
|
||||||
@@ -178,7 +180,7 @@ fun unbindEnum(comboBox: JFXComboBox<*>) {
|
|||||||
* return value of `interpolate()` is `endValue` only when the
|
* return value of `interpolate()` is `endValue` only when the
|
||||||
* input `fraction` is 1.0, and `startValue` otherwise.
|
* 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 {
|
override fun curve(t: Double): Double {
|
||||||
return Math.sin(t * Math.PI / 2)
|
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) {
|
fun Node.installTooltip(openDelay: Double = 1000.0, visibleDelay: Double = 5000.0, closeDelay: Double = 200.0, tooltip: Tooltip) {
|
||||||
try {
|
try {
|
||||||
Class.forName("javafx.scene.control.Tooltip\$TooltipBehavior")
|
call(construct(Class.forName("javafx.scene.control.Tooltip\$TooltipBehavior"), Duration(openDelay), Duration(visibleDelay), Duration(closeDelay), false),
|
||||||
.construct(Duration(openDelay), Duration(visibleDelay), Duration(closeDelay), false)!!
|
"install", this, tooltip);
|
||||||
.call("install", this, tooltip)
|
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
LOG.log(Level.SEVERE, "Cannot install tooltip by reflection", e)
|
LOG.log(Level.SEVERE, "Cannot install tooltip by reflection", e)
|
||||||
Tooltip.install(this, tooltip)
|
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.layout.VBox
|
||||||
import javafx.scene.paint.Paint
|
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.auth.yggdrasil.YggdrasilAccount
|
||||||
import org.jackhuang.hmcl.event.EventBus
|
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.game.AccountHelper
|
||||||
import org.jackhuang.hmcl.i18n
|
import org.jackhuang.hmcl.i18n
|
||||||
import org.jackhuang.hmcl.setting.Settings
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
@@ -65,29 +65,27 @@ class LeftPaneController(private val leftPane: AdvancedListBox) {
|
|||||||
Controllers.decorator.showPage(ProfilePage(null))
|
Controllers.decorator.showPage(ProfilePage(null))
|
||||||
}
|
}
|
||||||
|
|
||||||
Settings.selectedAccountProperty.onChangeAndOperate {
|
Settings.INSTANCE.selectedAccountProperty().onChangeAndOperate {
|
||||||
if (it == null) {
|
if (it == null) {
|
||||||
accountItem.lblVersionName.text = "mojang@mojang.com"
|
accountItem.setVersionName("mojang@mojang.com")
|
||||||
accountItem.lblGameVersion.text = "Yggdrasil"
|
accountItem.setGameVersion("Yggdrasil")
|
||||||
} else {
|
} else {
|
||||||
accountItem.lblVersionName.text = it.username
|
accountItem.setVersionName(it.username)
|
||||||
accountItem.lblGameVersion.text = accountType(it)
|
accountItem.setGameVersion(accountType(it))
|
||||||
}
|
}
|
||||||
if (it is YggdrasilAccount) {
|
if (it is YggdrasilAccount) {
|
||||||
accountItem.imageView.image = AccountHelper.getSkin(it, 4.0)
|
accountItem.setImage(AccountHelper.getSkin(it, 4.0), AccountHelper.getViewport(4.0))
|
||||||
accountItem.imageView.viewport = AccountHelper.getViewport(4.0)
|
|
||||||
} else {
|
} else {
|
||||||
accountItem.imageView.image = DEFAULT_ICON
|
accountItem.setImage(DEFAULT_ICON, null)
|
||||||
accountItem.imageView.viewport = null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings.getAccounts().isEmpty())
|
if (Settings.INSTANCE.getAccounts().isEmpty())
|
||||||
Controllers.navigate(AccountsPage())
|
Controllers.navigate(AccountsPage())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onProfileChanged(event: ProfileChangedEvent) {
|
fun onProfileChanged(event: ProfileChangedEvent) {
|
||||||
val profile = event.value
|
val profile = event.profile
|
||||||
|
|
||||||
profilePane.children
|
profilePane.children
|
||||||
.filter { it is RipplerContainer && it.properties["profile"] is Pair<*, *> }
|
.filter { it is RipplerContainer && it.properties["profile"] is Pair<*, *> }
|
||||||
@@ -96,7 +94,7 @@ class LeftPaneController(private val leftPane: AdvancedListBox) {
|
|||||||
|
|
||||||
fun onProfilesLoading() {
|
fun onProfilesLoading() {
|
||||||
val list = LinkedList<RipplerContainer>()
|
val list = LinkedList<RipplerContainer>()
|
||||||
Settings.getProfiles().forEach { profile ->
|
Settings.INSTANCE.profiles.forEach { profile ->
|
||||||
val item = VersionListItem(profile.name)
|
val item = VersionListItem(profile.name)
|
||||||
val ripplerContainer = RipplerContainer(item)
|
val ripplerContainer = RipplerContainer(item)
|
||||||
item.onSettingsButtonClicked {
|
item.onSettingsButtonClicked {
|
||||||
@@ -107,7 +105,7 @@ class LeftPaneController(private val leftPane: AdvancedListBox) {
|
|||||||
// clean selected property
|
// clean selected property
|
||||||
profilePane.children.forEach { if (it is RipplerContainer) it.selected = false }
|
profilePane.children.forEach { if (it is RipplerContainer) it.selected = false }
|
||||||
ripplerContainer.selected = true
|
ripplerContainer.selected = true
|
||||||
Settings.selectedProfile = profile
|
Settings.INSTANCE.selectedProfile = profile
|
||||||
}
|
}
|
||||||
ripplerContainer.properties["profile"] = profile.name to item
|
ripplerContainer.properties["profile"] = profile.name to item
|
||||||
ripplerContainer.maxWidthProperty().bind(leftPane.widthProperty())
|
ripplerContainer.maxWidthProperty().bind(leftPane.widthProperty())
|
||||||
|
|||||||
@@ -21,13 +21,14 @@ import com.jfoenix.controls.JFXButton
|
|||||||
import com.jfoenix.controls.JFXMasonryPane
|
import com.jfoenix.controls.JFXMasonryPane
|
||||||
import javafx.application.Platform
|
import javafx.application.Platform
|
||||||
import javafx.beans.property.SimpleStringProperty
|
import javafx.beans.property.SimpleStringProperty
|
||||||
|
import javafx.beans.property.StringProperty
|
||||||
import javafx.fxml.FXML
|
import javafx.fxml.FXML
|
||||||
import javafx.scene.Node
|
import javafx.scene.Node
|
||||||
import javafx.scene.image.Image
|
import javafx.scene.image.Image
|
||||||
import javafx.scene.layout.StackPane
|
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.EventBus
|
||||||
|
import org.jackhuang.hmcl.event.ProfileChangedEvent
|
||||||
|
import org.jackhuang.hmcl.event.ProfileLoadingEvent
|
||||||
import org.jackhuang.hmcl.event.RefreshedVersionsEvent
|
import org.jackhuang.hmcl.event.RefreshedVersionsEvent
|
||||||
import org.jackhuang.hmcl.game.GameVersion.minecraftVersion
|
import org.jackhuang.hmcl.game.GameVersion.minecraftVersion
|
||||||
import org.jackhuang.hmcl.game.LauncherHelper
|
import org.jackhuang.hmcl.game.LauncherHelper
|
||||||
@@ -43,7 +44,8 @@ import org.jackhuang.hmcl.util.plusAssign
|
|||||||
* @see /assets/fxml/main.fxml
|
* @see /assets/fxml/main.fxml
|
||||||
*/
|
*/
|
||||||
class MainPage : StackPane(), DecoratorPage {
|
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 btnRefresh: JFXButton
|
||||||
@FXML lateinit var btnAdd: JFXButton
|
@FXML lateinit var btnAdd: JFXButton
|
||||||
@@ -57,30 +59,31 @@ class MainPage : StackPane(), DecoratorPage {
|
|||||||
EventBus.EVENT_BUS.channel<ProfileChangedEvent>() += this::onProfileChanged
|
EventBus.EVENT_BUS.channel<ProfileChangedEvent>() += this::onProfileChanged
|
||||||
|
|
||||||
btnAdd.setOnMouseClicked { Controllers.decorator.startWizard(DownloadWizardProvider(), "Install New Game") }
|
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 {
|
private fun buildNode(i: Int, profile: Profile, version: String, game: String): Node {
|
||||||
return VersionItem().apply {
|
return VersionItem().apply {
|
||||||
lblGameVersion.text = game
|
setGameVersion(game)
|
||||||
lblVersionName.text = version
|
setVersionName(version)
|
||||||
btnLaunch.setOnMouseClicked {
|
|
||||||
if (Settings.selectedAccount == null) {
|
setOnLaunchButtonClicked {
|
||||||
|
if (Settings.INSTANCE.selectedAccount == null) {
|
||||||
Controllers.dialog(i18n("login.no_Player007"))
|
Controllers.dialog(i18n("login.no_Player007"))
|
||||||
} else
|
} else
|
||||||
LauncherHelper.INSTANCE.launch(version)
|
LauncherHelper.INSTANCE.launch(version)
|
||||||
}
|
}
|
||||||
btnDelete.setOnMouseClicked {
|
setOnDeleteButtonClicked {
|
||||||
profile.repository.removeVersionFromDisk(version)
|
profile.repository.removeVersionFromDisk(version)
|
||||||
Platform.runLater { loadVersions() }
|
Platform.runLater { loadVersions() }
|
||||||
}
|
}
|
||||||
btnSettings.setOnMouseClicked {
|
setOnSettingsButtonClicked {
|
||||||
Controllers.decorator.showPage(Controllers.versionPane)
|
Controllers.decorator.showPage(Controllers.versionPane)
|
||||||
Controllers.versionPane.load(version, profile)
|
Controllers.versionPane.load(version, profile)
|
||||||
}
|
}
|
||||||
val iconFile = profile.repository.getVersionIcon(version)
|
val iconFile = profile.repository.getVersionIcon(version)
|
||||||
if (iconFile.exists())
|
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 {
|
fun onProfileChanged(event: ProfileChangedEvent) = runOnUiThread {
|
||||||
val profile = event.value
|
val profile = event.profile
|
||||||
loadVersions(profile)
|
loadVersions(profile)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadVersions(profile: Profile = Settings.selectedProfile) {
|
private fun loadVersions(profile: Profile = Settings.INSTANCE.selectedProfile) {
|
||||||
val children = mutableListOf<Node>()
|
val children = mutableListOf<Node>()
|
||||||
var i = 0
|
var i = 0
|
||||||
profile.repository.versions.forEach { version ->
|
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
|
package org.jackhuang.hmcl.ui
|
||||||
|
|
||||||
|
import com.jfoenix.controls.JFXSpinner
|
||||||
import com.jfoenix.controls.JFXTabPane
|
import com.jfoenix.controls.JFXTabPane
|
||||||
import javafx.fxml.FXML
|
import javafx.fxml.FXML
|
||||||
import javafx.scene.control.ScrollPane
|
import javafx.scene.control.ScrollPane
|
||||||
@@ -38,6 +39,7 @@ class ModController {
|
|||||||
@FXML lateinit var rootPane: StackPane
|
@FXML lateinit var rootPane: StackPane
|
||||||
@FXML lateinit var modPane: VBox
|
@FXML lateinit var modPane: VBox
|
||||||
@FXML lateinit var contentPane: StackPane
|
@FXML lateinit var contentPane: StackPane
|
||||||
|
@FXML lateinit var spinner: JFXSpinner
|
||||||
lateinit var parentTab: JFXTabPane
|
lateinit var parentTab: JFXTabPane
|
||||||
private lateinit var modManager: ModManager
|
private lateinit var modManager: ModManager
|
||||||
private lateinit var versionId: String
|
private lateinit var versionId: String
|
||||||
@@ -67,7 +69,7 @@ class ModController {
|
|||||||
this.versionId = versionId
|
this.versionId = versionId
|
||||||
task {
|
task {
|
||||||
synchronized(contentPane) {
|
synchronized(contentPane) {
|
||||||
runOnUiThread { rootPane.children -= contentPane }
|
runOnUiThread { rootPane.children -= contentPane; spinner.isVisible = true }
|
||||||
modManager.refreshMods(versionId)
|
modManager.refreshMods(versionId)
|
||||||
|
|
||||||
// Surprisingly, if there are a great number of mods, this processing will cause a UI pause.
|
// Surprisingly, if there are a great number of mods, this processing will cause a UI pause.
|
||||||
@@ -89,7 +91,7 @@ class ModController {
|
|||||||
styleClass += "disabled"
|
styleClass += "disabled"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
runOnUiThread { rootPane.children += contentPane }
|
runOnUiThread { rootPane.children += contentPane; spinner.isVisible = false }
|
||||||
it["list"] = list
|
it["list"] = list
|
||||||
}
|
}
|
||||||
}.subscribe(Schedulers.javafx()) { variables ->
|
}.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.JFXButton
|
||||||
import com.jfoenix.controls.JFXTextField
|
import com.jfoenix.controls.JFXTextField
|
||||||
import javafx.beans.property.SimpleStringProperty
|
import javafx.beans.property.SimpleStringProperty
|
||||||
|
import javafx.beans.property.StringProperty
|
||||||
import javafx.fxml.FXML
|
import javafx.fxml.FXML
|
||||||
import javafx.scene.layout.StackPane
|
import javafx.scene.layout.StackPane
|
||||||
import org.jackhuang.hmcl.i18n
|
import org.jackhuang.hmcl.i18n
|
||||||
@@ -34,8 +35,11 @@ import java.io.File
|
|||||||
* @param profile null if creating a new profile.
|
* @param profile null if creating a new profile.
|
||||||
*/
|
*/
|
||||||
class ProfilePage(private val profile: Profile?): StackPane(), DecoratorPage {
|
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)
|
if (profile == null) i18n("ui.newProfileWindow.title") else i18n("ui.label.profile") + " - " + profile.name)
|
||||||
|
|
||||||
|
override fun titleProperty() = titleProperty
|
||||||
|
|
||||||
private val locationProperty = SimpleStringProperty(this, "location",
|
private val locationProperty = SimpleStringProperty(this, "location",
|
||||||
profile?.gameDir?.absolutePath ?: "")
|
profile?.gameDir?.absolutePath ?: "")
|
||||||
@FXML lateinit var txtProfileName: JFXTextField
|
@FXML lateinit var txtProfileName: JFXTextField
|
||||||
@@ -61,7 +65,7 @@ class ProfilePage(private val profile: Profile?): StackPane(), DecoratorPage {
|
|||||||
|
|
||||||
fun onDelete() {
|
fun onDelete() {
|
||||||
if (profile != null) {
|
if (profile != null) {
|
||||||
Settings.deleteProfile(profile)
|
Settings.INSTANCE.deleteProfile(profile)
|
||||||
Controllers.navigate(null)
|
Controllers.navigate(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,10 +79,10 @@ class ProfilePage(private val profile: Profile?): StackPane(), DecoratorPage {
|
|||||||
if (locationProperty.get().isNullOrBlank()) {
|
if (locationProperty.get().isNullOrBlank()) {
|
||||||
gameDir.onExplore()
|
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)
|
Controllers.navigate(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,17 +49,17 @@ object SVG {
|
|||||||
return 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)
|
@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)
|
||||||
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 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)
|
@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)
|
||||||
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 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)
|
@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)
|
||||||
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 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)
|
@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)
|
||||||
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 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)
|
@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)
|
||||||
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 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)
|
@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)
|
||||||
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 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 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
|
import org.jackhuang.hmcl.util.onChange
|
||||||
|
|
||||||
class SettingsPage : StackPane(), DecoratorPage {
|
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 txtProxyHost: JFXTextField
|
||||||
@FXML lateinit var txtProxyPort: JFXTextField
|
@FXML lateinit var txtProxyPort: JFXTextField
|
||||||
@@ -59,56 +61,56 @@ class SettingsPage : StackPane(), DecoratorPage {
|
|||||||
cboLanguage.limitWidth(400.0)
|
cboLanguage.limitWidth(400.0)
|
||||||
cboDownloadSource.limitWidth(400.0)
|
cboDownloadSource.limitWidth(400.0)
|
||||||
|
|
||||||
txtProxyHost.text = Settings.proxyHost
|
txtProxyHost.text = Settings.INSTANCE.proxyHost
|
||||||
txtProxyHost.textProperty().onChange { Settings.proxyHost = it }
|
txtProxyHost.textProperty().onChange { Settings.INSTANCE.proxyHost = it }
|
||||||
|
|
||||||
txtProxyPort.text = Settings.proxyPort
|
txtProxyPort.text = Settings.INSTANCE.proxyPort
|
||||||
txtProxyPort.textProperty().onChange { Settings.proxyPort = it }
|
txtProxyPort.textProperty().onChange { Settings.INSTANCE.proxyPort = it }
|
||||||
|
|
||||||
txtProxyUsername.text = Settings.proxyUser
|
txtProxyUsername.text = Settings.INSTANCE.proxyUser
|
||||||
txtProxyUsername.textProperty().onChange { Settings.proxyUser = it }
|
txtProxyUsername.textProperty().onChange { Settings.INSTANCE.proxyUser = it }
|
||||||
|
|
||||||
txtProxyPassword.text = Settings.proxyPass
|
txtProxyPassword.text = Settings.INSTANCE.proxyPass
|
||||||
txtProxyPassword.textProperty().onChange { Settings.proxyPass = it }
|
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 {
|
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 {
|
cboFont.valueProperty().onChange {
|
||||||
val font = Font.font(it, Settings.font.size)
|
val font = Font.font(it, Settings.INSTANCE.font.size)
|
||||||
Settings.font = font
|
Settings.INSTANCE.font = font
|
||||||
lblDisplay.style = "-fx-font: ${Settings.font.size} \"${font.family}\";"
|
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.validators += Validator { it.toDoubleOrNull() != null }
|
||||||
txtFontSize.textProperty().onChange {
|
txtFontSize.textProperty().onChange {
|
||||||
if (txtFontSize.validate()) {
|
if (txtFontSize.validate()) {
|
||||||
val font = Font.font(Settings.font.family, it!!.toDouble())
|
val font = Font.font(Settings.INSTANCE.font.family, it!!.toDouble())
|
||||||
Settings.font = font
|
Settings.INSTANCE.font = font
|
||||||
lblDisplay.style = "-fx-font: ${font.size} \"${Settings.font.family}\";"
|
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>()
|
val list = FXCollections.observableArrayList<Label>()
|
||||||
for (locale in Locales.LOCALES) {
|
for (locale in Locales.LOCALES) {
|
||||||
list += Label(locale.getName(Settings.locale.resourceBundle))
|
list += Label(locale.getName(Settings.INSTANCE.locale.resourceBundle))
|
||||||
}
|
}
|
||||||
cboLanguage.items = list
|
cboLanguage.items = list
|
||||||
cboLanguage.selectionModel.select(Locales.LOCALES.indexOf(Settings.locale))
|
cboLanguage.selectionModel.select(Locales.LOCALES.indexOf(Settings.INSTANCE.locale))
|
||||||
cboLanguage.selectionModel.selectedIndexProperty().onChange {
|
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 {
|
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
|
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
||||||
|
|
||||||
class VersionPage : StackPane(), 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 versionSettingsController: VersionSettingsController
|
||||||
@FXML lateinit var modTab: Tab
|
@FXML lateinit var modTab: Tab
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class YggdrasilAccountLoginPane(private val oldAccount: YggdrasilAccount, privat
|
|||||||
taskResult("login") {
|
taskResult("login") {
|
||||||
try {
|
try {
|
||||||
val account = YggdrasilAccountFactory.INSTANCE.fromUsername(username, password)
|
val account = YggdrasilAccountFactory.INSTANCE.fromUsername(username, password)
|
||||||
account.logIn(HMCLMultiCharacterSelector.INSTANCE, Settings.proxy)
|
account.logIn(HMCLMultiCharacterSelector.INSTANCE, Settings.INSTANCE.proxy)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e
|
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 org.jackhuang.hmcl.util.task
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class DownloadWizardProvider(): WizardProvider() {
|
class DownloadWizardProvider(): WizardProvider {
|
||||||
lateinit var profile: Profile
|
lateinit var profile: Profile
|
||||||
|
|
||||||
override fun start(settings: MutableMap<String, Any>) {
|
override fun start(settings: MutableMap<String, Any>) {
|
||||||
profile = Settings.selectedProfile
|
profile = Settings.INSTANCE.selectedProfile
|
||||||
settings[PROFILE] = profile
|
settings[PROFILE] = profile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,8 +41,7 @@ class InstallTypePage(private val controller: WizardController): StackPane(), Wi
|
|||||||
settings.remove(INSTALL_TYPE)
|
settings.remove(INSTALL_TYPE)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val title: String
|
override fun getTitle() = "Select an operation"
|
||||||
get() = "Select an operation"
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val INSTALL_TYPE: String = "INSTALL_TYPE"
|
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
|
override fun getTitle() = "Choose a game version"
|
||||||
get() = "Choose a game version"
|
|
||||||
|
|
||||||
override fun onNavigate(settings: MutableMap<String, Any>) {
|
override fun onNavigate(settings: MutableMap<String, Any>) {
|
||||||
lblGameVersion.text = "Current Game Version: ${controller.settings["game"]}"
|
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
|
import org.jackhuang.hmcl.util.onInvalidated
|
||||||
|
|
||||||
class ModpackPage(private val controller: WizardController): StackPane(), WizardPage {
|
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 borderPane: Region
|
||||||
@FXML lateinit var lblName: Label
|
@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
|
running = false
|
||||||
}
|
}
|
||||||
|
|
||||||
override val isRunning: Boolean
|
override fun isRunning() = running
|
||||||
get() = 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"
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
fx:id="rootPane"
|
fx:id="rootPane"
|
||||||
fx:controller="org.jackhuang.hmcl.ui.ModController">
|
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">
|
<StackPane fx:id="contentPane">
|
||||||
<ScrollPane fx:id="scrollPane" fitToWidth="true" fitToHeight="true">
|
<ScrollPane fx:id="scrollPane" fitToWidth="true" fitToHeight="true">
|
||||||
<VBox fx:id="modPane" spacing="10" style="-fx-padding: 20 20 70 20;">
|
<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 -> {
|
Optional.ofNullable(modsDirectory.listFiles()).map(Arrays::stream).ifPresent(files -> files.forEach(modFile -> {
|
||||||
if (modFile.isDirectory() && VersionNumber.parseVersion(modFile.getName()) != null)
|
if (modFile.isDirectory() && VersionNumber.parseVersion(modFile.getName()) != null)
|
||||||
Optional.ofNullable(modFile.listFiles()).map(Arrays::stream).ifPresent(x -> x.forEach(puter));
|
Optional.ofNullable(modFile.listFiles()).map(Arrays::stream).ifPresent(x -> x.forEach(puter));
|
||||||
puter.accept(modFile);
|
else
|
||||||
|
puter.accept(modFile);
|
||||||
}));
|
}));
|
||||||
return modCache.get(id);
|
return modCache.get(id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import javafx.beans.property.ReadOnlyDoubleProperty;
|
import javafx.beans.property.ReadOnlyDoubleProperty;
|
||||||
import javafx.beans.property.ReadOnlyDoubleWrapper;
|
import javafx.beans.property.ReadOnlyDoubleWrapper;
|
||||||
@@ -58,7 +57,7 @@ public abstract class Task {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* True if requires all {@link #getDependents} finishing successfully.
|
* True if requires all {@link #getDependents} finishing successfully.
|
||||||
*
|
* <p>
|
||||||
* **Note** if this field is set false, you are not supposed to invoke [run]
|
* **Note** if this field is set false, you are not supposed to invoke [run]
|
||||||
*
|
*
|
||||||
* @defaultValue true
|
* @defaultValue true
|
||||||
@@ -69,7 +68,7 @@ public abstract class Task {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* True if requires all {@link #getDependencies} finishing successfully.
|
* True if requires all {@link #getDependencies} finishing successfully.
|
||||||
*
|
* <p>
|
||||||
* **Note** if this field is set false, you are not supposed to invoke [run]
|
* **Note** if this field is set false, you are not supposed to invoke [run]
|
||||||
*
|
*
|
||||||
* @defaultValue false
|
* @defaultValue false
|
||||||
@@ -77,7 +76,7 @@ public abstract class Task {
|
|||||||
public boolean isRelyingOnDependencies() {
|
public boolean isRelyingOnDependencies() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String name = getClass().toString();
|
private String name = getClass().toString();
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@@ -99,8 +98,8 @@ public abstract class Task {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see Thread#isInterrupted
|
|
||||||
* @throws InterruptedException if current thread is interrupted
|
* @throws InterruptedException if current thread is interrupted
|
||||||
|
* @see Thread#isInterrupted
|
||||||
*/
|
*/
|
||||||
public abstract void execute() throws Exception;
|
public abstract void execute() throws Exception;
|
||||||
|
|
||||||
@@ -128,10 +127,10 @@ public abstract class Task {
|
|||||||
|
|
||||||
private long lastTime = Long.MIN_VALUE;
|
private long lastTime = Long.MIN_VALUE;
|
||||||
private final AtomicReference<Double> progressUpdate = new AtomicReference<>();
|
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() {
|
public ReadOnlyDoubleProperty progressProperty() {
|
||||||
return progressProperty.getReadOnlyProperty();
|
return progress.getReadOnlyProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void updateProgress(int progress, int total) {
|
protected void updateProgress(int progress, int total) {
|
||||||
@@ -149,18 +148,18 @@ public abstract class Task {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void updateProgressImmediately(double progress) {
|
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 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() {
|
public final ReadOnlyStringProperty messageProperty() {
|
||||||
return messageProperty.getReadOnlyProperty();
|
return message.getReadOnlyProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final void updateMessage(String newMessage) {
|
protected final void updateMessage(String newMessage) {
|
||||||
Properties.updateAsync(messageProperty, newMessage, messageUpdate);
|
Properties.updateAsync(message, newMessage, messageUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void run() throws Exception {
|
public final void run() throws Exception {
|
||||||
@@ -173,11 +172,11 @@ public abstract class Task {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void doSubTask(Task task) throws Exception {
|
private void doSubTask(Task task) throws Exception {
|
||||||
messageProperty.bind(task.messageProperty);
|
message.bind(task.message);
|
||||||
progressProperty.bind(task.progressProperty);
|
progress.bind(task.progress);
|
||||||
task.run();
|
task.run();
|
||||||
messageProperty.unbind();
|
message.unbind();
|
||||||
progressProperty.unbind();
|
progress.unbind();
|
||||||
}
|
}
|
||||||
|
|
||||||
public final TaskExecutor executor() {
|
public final TaskExecutor executor() {
|
||||||
@@ -228,6 +227,11 @@ public abstract class Task {
|
|||||||
return new CoupleTask<>(this, b, false);
|
return new CoupleTask<>(this, b, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Task empty() {
|
||||||
|
return of(s -> {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static Task of(ExceptionalRunnable<?> runnable) {
|
public static Task of(ExceptionalRunnable<?> runnable) {
|
||||||
return of(s -> runnable.run());
|
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) {
|
public static <T> T nonNull(T... t) {
|
||||||
for (T a : t) if (a != null) return a;
|
for (T a : t) if (a != null) return a;
|
||||||
return null;
|
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) {
|
public static Object call(Object obj, String name, Object... args) {
|
||||||
return call(obj.getClass(), name, obj, args);
|
return call(obj.getClass(), name, obj, args);
|
||||||
}
|
}
|
||||||
@@ -136,12 +140,7 @@ public final class ReflectionHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isInstance(Class<?> superClass, Object obj) {
|
public static boolean isInstance(Class<?> superClass, Object obj) {
|
||||||
if (superClass.isInstance(obj))
|
return superClass.isInstance(obj) || PRIMITIVES.get(superClass.getName()) == obj.getClass();
|
||||||
return true;
|
|
||||||
else if (PRIMITIVES.get(superClass.getName()) == obj.getClass())
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Optional<Method> forMethod(Class<?> cls, String name, Object... args) {
|
public static Optional<Method> forMethod(Class<?> cls, String name, Object... args) {
|
||||||
|
|||||||
Reference in New Issue
Block a user