Merge branch 'javafx' into refactor-account

This commit is contained in:
huanghongxun
2018-07-21 19:50:16 +08:00
committed by GitHub
16 changed files with 219 additions and 134 deletions

View File

@@ -18,6 +18,7 @@
package org.jackhuang.hmcl.game; package org.jackhuang.hmcl.game;
import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck; import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
@@ -29,6 +30,7 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.logging.Level; import java.util.logging.Level;
import static org.jackhuang.hmcl.setting.ConfigHolder.CONFIG; import static org.jackhuang.hmcl.setting.ConfigHolder.CONFIG;
@@ -59,7 +61,11 @@ public class HMCLGameDownloadTask extends Task {
public void execute() { public void execute() {
File jar = profile.getRepository().getVersionJar(version); File jar = profile.getRepository().getVersionJar(version);
File cache = new File(CONFIG.getCommonDirectory(), "jars/" + gameVersion + ".jar"); // Force using common directory will not affect the behaviour that repository acts
// Since we always copy the downloaded jar to .minecraft/versions/<version>/
File cache = new File(Optional.ofNullable(Settings.INSTANCE.getCommonDirectory())
.orElse(Settings.getDefaultCommonDirectory()),
"jars/" + gameVersion + ".jar");
if (cache.exists()) if (cache.exists())
try { try {
FileUtils.copyFile(cache, jar); FileUtils.copyFile(cache, jar);

View File

@@ -24,6 +24,7 @@ import org.jackhuang.hmcl.event.RefreshedVersionsEvent;
import org.jackhuang.hmcl.event.RefreshingVersionsEvent; import org.jackhuang.hmcl.event.RefreshingVersionsEvent;
import org.jackhuang.hmcl.setting.EnumGameDirectory; import org.jackhuang.hmcl.setting.EnumGameDirectory;
import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.setting.VersionSetting; import org.jackhuang.hmcl.setting.VersionSetting;
import org.jackhuang.hmcl.util.FileUtils; import org.jackhuang.hmcl.util.FileUtils;
import org.jackhuang.hmcl.util.Logging; import org.jackhuang.hmcl.util.Logging;
@@ -51,17 +52,16 @@ public class HMCLGameRepository extends DefaultGameRepository {
return profile; return profile;
} }
private boolean useSelf(String version, String assetId) { private boolean useSelf(String assetId) {
VersionSetting vs = profile.getVersionSetting(version); return new File(getBaseDirectory(), "assets/indexes/" + assetId + ".json").exists();
return new File(getBaseDirectory(), "assets/indexes/" + assetId + ".json").exists() || vs.isNoCommon();
} }
@Override @Override
public File getAssetDirectory(String version, String assetId) { public File getAssetDirectory(String version, String assetId) {
if (useSelf(version, assetId)) if (Settings.INSTANCE.isCommonDirectoryDisabled() || useSelf(assetId))
return super.getAssetDirectory(version, assetId); return super.getAssetDirectory(version, assetId);
else else
return new File(CONFIG.getCommonDirectory(), "assets"); return new File(Settings.INSTANCE.getCommonDirectory(), "assets");
} }
@Override @Override
@@ -83,10 +83,10 @@ public class HMCLGameRepository extends DefaultGameRepository {
public File getLibraryFile(Version version, Library lib) { public File getLibraryFile(Version version, Library lib) {
VersionSetting vs = profile.getVersionSetting(version.getId()); VersionSetting vs = profile.getVersionSetting(version.getId());
File self = super.getLibraryFile(version, lib); File self = super.getLibraryFile(version, lib);
if (self.exists() || vs.isNoCommon()) if (Settings.INSTANCE.isCommonDirectoryDisabled() || self.exists())
return self; return self;
else else
return new File(CONFIG.getCommonDirectory(), "libraries/" + lib.getPath()); return new File(Settings.INSTANCE.getCommonDirectory(), "libraries/" + lib.getPath());
} }

View File

@@ -89,6 +89,9 @@ public final class Config implements Cloneable, Observable {
@SerializedName("bgpath") @SerializedName("bgpath")
private StringProperty backgroundImage = new SimpleStringProperty(); private StringProperty backgroundImage = new SimpleStringProperty();
@SerializedName("commonDirType")
private ObjectProperty<EnumCommonDirectory> commonDirType = new SimpleObjectProperty<>(EnumCommonDirectory.DEFAULT);
@SerializedName("commonpath") @SerializedName("commonpath")
private StringProperty commonDirectory = new SimpleStringProperty(Launcher.MINECRAFT_DIRECTORY.getAbsolutePath()); private StringProperty commonDirectory = new SimpleStringProperty(Launcher.MINECRAFT_DIRECTORY.getAbsolutePath());
@@ -146,6 +149,9 @@ public final class Config implements Cloneable, Observable {
@SerializedName("authlibInjectorServers") @SerializedName("authlibInjectorServers")
private ObservableList<AuthlibInjectorServer> authlibInjectorServers = FXCollections.observableArrayList(); private ObservableList<AuthlibInjectorServer> authlibInjectorServers = FXCollections.observableArrayList();
@SerializedName("_version")
private IntegerProperty configVersion = new SimpleIntegerProperty(0);
private transient ObservableHelper helper = new ObservableHelper(this); private transient ObservableHelper helper = new ObservableHelper(this);
public Config() { public Config() {
@@ -225,6 +231,18 @@ public final class Config implements Cloneable, Observable {
return backgroundImage; return backgroundImage;
} }
public EnumCommonDirectory getCommonDirType() {
return commonDirType.get();
}
public ObjectProperty<EnumCommonDirectory> commonDirTypeProperty() {
return commonDirType;
}
public void setCommonDirType(EnumCommonDirectory commonDirType) {
this.commonDirType.set(commonDirType);
}
public String getCommonDirectory() { public String getCommonDirectory() {
return commonDirectory.get(); return commonDirectory.get();
} }

View File

@@ -24,8 +24,10 @@ import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import com.google.gson.Gson;
import com.google.gson.JsonParseException; import com.google.gson.JsonParseException;
public final class ConfigHolder { public final class ConfigHolder {
@@ -36,15 +38,23 @@ public final class ConfigHolder {
public static final Path CONFIG_PATH = Paths.get(CONFIG_FILENAME).toAbsolutePath(); public static final Path CONFIG_PATH = Paths.get(CONFIG_FILENAME).toAbsolutePath();
public static final Config CONFIG = initSettings(); public static final Config CONFIG = initSettings();
private static Config upgradeSettings(Config deserialized, Map rawJson) {
if (!rawJson.containsKey("commonDirType"))
deserialized.setCommonDirType(deserialized.getCommonDirectory().equals(Settings.getDefaultCommonDirectory()) ? EnumCommonDirectory.DEFAULT : EnumCommonDirectory.CUSTOM);
return deserialized;
}
private static Config initSettings() { private static Config initSettings() {
Config config = new Config(); Config config = new Config();
if (Files.exists(CONFIG_PATH)) { if (Files.exists(CONFIG_PATH)) {
try { try {
Config deserialized = Config.fromJson(new String(Files.readAllBytes(CONFIG_PATH), UTF_8)); String json = new String(Files.readAllBytes(CONFIG_PATH), UTF_8);
Map raw = new Gson().fromJson(json, Map.class);
Config deserialized = Config.fromJson(json);
if (deserialized == null) { if (deserialized == null) {
LOG.finer("Settings file is empty, use the default settings."); LOG.finer("Settings file is empty, use the default settings.");
} else { } else {
config = deserialized; config = upgradeSettings(deserialized, raw);
} }
LOG.finest("Initialized settings."); LOG.finest("Initialized settings.");
} catch (IOException | JsonParseException e) { } catch (IOException | JsonParseException e) {

View File

@@ -0,0 +1,33 @@
/*
* 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;
public enum EnumCommonDirectory {
/**
* same to game directory
*/
DISABLED,
/**
* %appdata%/.minecraft or ~/.minecraft
*/
DEFAULT,
/**
* user customized common directory.
*/
CUSTOM
}

View File

@@ -27,6 +27,7 @@ import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.util.*; import org.jackhuang.hmcl.util.*;
import org.jackhuang.hmcl.util.i18n.Locales; import org.jackhuang.hmcl.util.i18n.Locales;
import java.io.File;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -94,6 +95,27 @@ public class Settings {
CONFIG.setLogLines(logLines); CONFIG.setLogLines(logLines);
} }
public boolean isCommonDirectoryDisabled() {
return CONFIG.getCommonDirType() == EnumCommonDirectory.DISABLED;
}
public static String getDefaultCommonDirectory() {
return Launcher.MINECRAFT_DIRECTORY.getAbsolutePath();
}
public String getCommonDirectory() {
switch (CONFIG.getCommonDirType()) {
case DISABLED:
return null;
case DEFAULT:
return getDefaultCommonDirectory();
case CUSTOM:
return CONFIG.getCommonDirectory();
default:
return null;
}
}
/**************************************** /****************************************
* DOWNLOAD PROVIDERS * * DOWNLOAD PROVIDERS *
****************************************/ ****************************************/

View File

@@ -81,7 +81,7 @@ public final class VersionSetting {
} }
/** /**
* Java version or null if user customizes java directory. * Java version or "Custom" if user customizes java directory, "Default" if the jvm that this app relies on.
*/ */
public String getJava() { public String getJava() {
return javaProperty.get(); return javaProperty.get();
@@ -91,6 +91,15 @@ public final class VersionSetting {
javaProperty.set(java); javaProperty.set(java);
} }
public boolean isUsesCustomJavaDir() {
return "Custom".equals(getJava());
}
public void setUsesCustomJavaDir() {
setJava("Custom");
setDefaultJavaPath(null);
}
private final ImmediateStringProperty defaultJavaPathProperty = new ImmediateStringProperty(this, "defaultJavaPath", ""); private final ImmediateStringProperty defaultJavaPathProperty = new ImmediateStringProperty(this, "defaultJavaPath", "");
/** /**
@@ -278,23 +287,6 @@ public final class VersionSetting {
notCheckGameProperty.set(notCheckGame); notCheckGameProperty.set(notCheckGame);
} }
private final ImmediateBooleanProperty noCommonProperty = new ImmediateBooleanProperty(this, "noCommon", false);
public ImmediateBooleanProperty noCommonProperty() {
return noCommonProperty;
}
/**
* True if HMCL does not find/download libraries in/to common path.
*/
public boolean isNoCommon() {
return noCommonProperty.get();
}
public void setNoCommon(boolean noCommon) {
noCommonProperty.set(noCommon);
}
private final ImmediateBooleanProperty showLogsProperty = new ImmediateBooleanProperty(this, "showLogs", false); private final ImmediateBooleanProperty showLogsProperty = new ImmediateBooleanProperty(this, "showLogs", false);
public ImmediateBooleanProperty showLogsProperty() { public ImmediateBooleanProperty showLogsProperty() {
@@ -455,7 +447,7 @@ public final class VersionSetting {
if (StringUtils.isBlank(getJava())) if (StringUtils.isBlank(getJava()))
setJava(StringUtils.isBlank(getJavaDir()) ? "Default" : "Custom"); setJava(StringUtils.isBlank(getJavaDir()) ? "Default" : "Custom");
if ("Default".equals(getJava())) return JavaVersion.fromCurrentEnvironment(); if ("Default".equals(getJava())) return JavaVersion.fromCurrentEnvironment();
else if ("Custom".equals(getJava())) { else if (isUsesCustomJavaDir()) {
try { try {
return JavaVersion.fromExecutable(new File(getJavaDir())); return JavaVersion.fromExecutable(new File(getJavaDir()));
} catch (IOException e) { } catch (IOException e) {
@@ -477,6 +469,11 @@ public final class VersionSetting {
} else throw new Error(); } else throw new Error();
} }
public void setJavaVersion(JavaVersion java) {
setJava(java.getVersion());
setDefaultJavaPath(java.getBinary().toString());
}
public void addPropertyChangedListener(InvalidationListener listener) { public void addPropertyChangedListener(InvalidationListener listener) {
usesGlobalProperty.addListener(listener); usesGlobalProperty.addListener(listener);
javaProperty.addListener(listener); javaProperty.addListener(listener);
@@ -490,7 +487,6 @@ public final class VersionSetting {
minecraftArgsProperty.addListener(listener); minecraftArgsProperty.addListener(listener);
noJVMArgsProperty.addListener(listener); noJVMArgsProperty.addListener(listener);
notCheckGameProperty.addListener(listener); notCheckGameProperty.addListener(listener);
noCommonProperty.addListener(listener);
showLogsProperty.addListener(listener); showLogsProperty.addListener(listener);
serverIpProperty.addListener(listener); serverIpProperty.addListener(listener);
fullscreenProperty.addListener(listener); fullscreenProperty.addListener(listener);
@@ -555,7 +551,6 @@ public final class VersionSetting {
obj.addProperty("fullscreen", src.isFullscreen()); obj.addProperty("fullscreen", src.isFullscreen());
obj.addProperty("noJVMArgs", src.isNoJVMArgs()); obj.addProperty("noJVMArgs", src.isNoJVMArgs());
obj.addProperty("notCheckGame", src.isNotCheckGame()); obj.addProperty("notCheckGame", src.isNotCheckGame());
obj.addProperty("noCommon", src.isNoCommon());
obj.addProperty("showLogs", src.isShowLogs()); obj.addProperty("showLogs", src.isShowLogs());
obj.addProperty("gameDir", src.getGameDir()); obj.addProperty("gameDir", src.getGameDir());
obj.addProperty("launcherVisibility", src.getLauncherVisibility().ordinal()); obj.addProperty("launcherVisibility", src.getLauncherVisibility().ordinal());
@@ -593,7 +588,6 @@ public final class VersionSetting {
vs.setFullscreen(Optional.ofNullable(obj.get("fullscreen")).map(JsonElement::getAsBoolean).orElse(false)); vs.setFullscreen(Optional.ofNullable(obj.get("fullscreen")).map(JsonElement::getAsBoolean).orElse(false));
vs.setNoJVMArgs(Optional.ofNullable(obj.get("noJVMArgs")).map(JsonElement::getAsBoolean).orElse(false)); vs.setNoJVMArgs(Optional.ofNullable(obj.get("noJVMArgs")).map(JsonElement::getAsBoolean).orElse(false));
vs.setNotCheckGame(Optional.ofNullable(obj.get("notCheckGame")).map(JsonElement::getAsBoolean).orElse(false)); vs.setNotCheckGame(Optional.ofNullable(obj.get("notCheckGame")).map(JsonElement::getAsBoolean).orElse(false));
vs.setNoCommon(Optional.ofNullable(obj.get("noCommon")).map(JsonElement::getAsBoolean).orElse(false));
vs.setShowLogs(Optional.ofNullable(obj.get("showLogs")).map(JsonElement::getAsBoolean).orElse(false)); vs.setShowLogs(Optional.ofNullable(obj.get("showLogs")).map(JsonElement::getAsBoolean).orElse(false));
vs.setLauncherVisibility(LauncherVisibility.values()[Optional.ofNullable(obj.get("launcherVisibility")).map(JsonElement::getAsInt).orElse(1)]); vs.setLauncherVisibility(LauncherVisibility.values()[Optional.ofNullable(obj.get("launcherVisibility")).map(JsonElement::getAsInt).orElse(1)]);
vs.setGameDirType(EnumGameDirectory.values()[Optional.ofNullable(obj.get("gameDirType")).map(JsonElement::getAsInt).orElse(0)]); vs.setGameDirType(EnumGameDirectory.values()[Optional.ofNullable(obj.get("gameDirType")).map(JsonElement::getAsInt).orElse(0)]);

View File

@@ -69,6 +69,7 @@ import org.jackhuang.hmcl.ui.wizard.*;
import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.Lang;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.List; import java.util.List;
@@ -227,7 +228,7 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
Bindings.createObjectBinding( Bindings.createObjectBinding(
() -> { () -> {
Image image = null; Image image = null;
if (CONFIG.getBackgroundImageType() == EnumBackgroundImage.CUSTOM) { if (CONFIG.getBackgroundImageType() == EnumBackgroundImage.CUSTOM && CONFIG.getBackgroundImage() != null) {
image = tryLoadImage(Paths.get(CONFIG.getBackgroundImage())) image = tryLoadImage(Paths.get(CONFIG.getBackgroundImage()))
.orElse(null); .orElse(null);
} }

View File

@@ -20,6 +20,7 @@ package org.jackhuang.hmcl.ui;
import com.jfoenix.controls.*; import com.jfoenix.controls.*;
import com.jfoenix.effects.JFXDepthManager; import com.jfoenix.effects.JFXDepthManager;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.When; import javafx.beans.binding.When;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
@@ -50,8 +51,10 @@ import static org.jackhuang.hmcl.setting.ConfigHolder.CONFIG;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
import java.net.Proxy; import java.net.Proxy;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Objects; import java.util.Objects;
import java.util.Optional;
public final class SettingsPage extends StackPane implements DecoratorPage { public final class SettingsPage extends StackPane implements DecoratorPage {
private final StringProperty title = new SimpleStringProperty(this, "title", i18n("settings.launcher")); private final StringProperty title = new SimpleStringProperty(this, "title", i18n("settings.launcher"));
@@ -73,7 +76,7 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
@FXML @FXML
private FontComboBox cboFont; private FontComboBox cboFont;
@FXML @FXML
private FileItem fileCommonLocation; private MultiFileItem<EnumCommonDirectory> fileCommonLocation;
@FXML @FXML
private Label lblDisplay; private Label lblDisplay;
@FXML @FXML
@@ -85,7 +88,7 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
@FXML @FXML
private ScrollPane scroll; private ScrollPane scroll;
@FXML @FXML
private MultiFileItem backgroundItem; private MultiFileItem<EnumBackgroundImage> backgroundItem;
@FXML @FXML
private StackPane themeColorPickerContainer; private StackPane themeColorPickerContainer;
@FXML @FXML
@@ -102,7 +105,6 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
private Pane proxyPane; private Pane proxyPane;
private ObjectProperty<Proxy.Type> selectedProxyType; private ObjectProperty<Proxy.Type> selectedProxyType;
private ObjectProperty<EnumBackgroundImage> backgroundType;
public SettingsPage() { public SettingsPage() {
FXUtils.loadFXML(this, "/assets/fxml/setting.fxml"); FXUtils.loadFXML(this, "/assets/fxml/setting.fxml");
@@ -182,7 +184,16 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
})); }));
// ==== // ====
fileCommonLocation.pathProperty().bindBidirectional(CONFIG.commonDirectoryProperty()); fileCommonLocation.loadChildren(Arrays.asList(
fileCommonLocation.createChildren(i18n("launcher.common_directory.disabled"), EnumCommonDirectory.DISABLED),
fileCommonLocation.createChildren(i18n("launcher.common_directory.default"), EnumCommonDirectory.DEFAULT)
), EnumCommonDirectory.CUSTOM);
fileCommonLocation.selectedDataProperty().bindBidirectional(CONFIG.commonDirTypeProperty());
fileCommonLocation.customTextProperty().bindBidirectional(CONFIG.commonDirectoryProperty());
fileCommonLocation.subtitleProperty().bind(
Bindings.createObjectBinding(() -> Optional.ofNullable(Settings.INSTANCE.getCommonDirectory())
.orElse(i18n("launcher.common_directory.disabled")),
CONFIG.commonDirectoryProperty(), CONFIG.commonDirTypeProperty()));
FXUtils.installTooltip(btnUpdate, i18n("update.tooltip")); FXUtils.installTooltip(btnUpdate, i18n("update.tooltip"));
checkUpdate(); checkUpdate();
@@ -190,28 +201,11 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
// ==== Background ==== // ==== Background ====
backgroundItem.loadChildren(Collections.singletonList( backgroundItem.loadChildren(Collections.singletonList(
backgroundItem.createChildren(i18n("launcher.background.default"), EnumBackgroundImage.DEFAULT) backgroundItem.createChildren(i18n("launcher.background.default"), EnumBackgroundImage.DEFAULT)
)); ), EnumBackgroundImage.CUSTOM);
backgroundItem.setCustomUserData(EnumBackgroundImage.CUSTOM); backgroundItem.customTextProperty().bindBidirectional(CONFIG.backgroundImageProperty());
backgroundItem.getTxtCustom().textProperty().bindBidirectional(CONFIG.backgroundImageProperty()); backgroundItem.selectedDataProperty().bindBidirectional(CONFIG.backgroundImageTypeProperty());
backgroundType = new SimpleObjectProperty<EnumBackgroundImage>(EnumBackgroundImage.DEFAULT) {
{
invalidated();
}
@Override
protected void invalidated() {
backgroundItem.getGroup().getToggles().stream()
.filter(it -> it.getUserData() == get())
.findFirst()
.ifPresent(it -> it.setSelected(true));
}
};
backgroundItem.getGroup().selectedToggleProperty().addListener((observable, oldValue, newValue) -> backgroundType.set((EnumBackgroundImage) newValue.getUserData()));
backgroundType.bindBidirectional(CONFIG.backgroundImageTypeProperty());
backgroundItem.subtitleProperty().bind( backgroundItem.subtitleProperty().bind(
new When(backgroundType.isEqualTo(EnumBackgroundImage.DEFAULT)) new When(backgroundItem.selectedDataProperty().isEqualTo(EnumBackgroundImage.DEFAULT))
.then(i18n("launcher.background.default")) .then(i18n("launcher.background.default"))
.otherwise(CONFIG.backgroundImageProperty())); .otherwise(CONFIG.backgroundImageProperty()));
// ==== // ====

View File

@@ -22,6 +22,7 @@ import com.jfoenix.controls.JFXComboBox;
import com.jfoenix.controls.JFXTextField; import com.jfoenix.controls.JFXTextField;
import com.jfoenix.controls.JFXToggleButton; import com.jfoenix.controls.JFXToggleButton;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane; import javafx.scene.control.ScrollPane;
@@ -43,6 +44,7 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Paths;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
@@ -70,11 +72,10 @@ public final class VersionSettingsController {
@FXML private JFXCheckBox chkFullscreen; @FXML private JFXCheckBox chkFullscreen;
@FXML private Label lblPhysicalMemory; @FXML private Label lblPhysicalMemory;
@FXML private JFXToggleButton chkNoJVMArgs; @FXML private JFXToggleButton chkNoJVMArgs;
@FXML private JFXToggleButton chkNoCommon;
@FXML private JFXToggleButton chkNoGameCheck; @FXML private JFXToggleButton chkNoGameCheck;
@FXML private MultiFileItem globalItem; @FXML private MultiFileItem<Boolean> globalItem;
@FXML private MultiFileItem javaItem; @FXML private MultiFileItem<JavaVersion> javaItem;
@FXML private MultiFileItem gameDirItem; @FXML private MultiFileItem<EnumGameDirectory> gameDirItem;
@FXML private JFXToggleButton chkShowLogs; @FXML private JFXToggleButton chkShowLogs;
@FXML private ImagePickerItem iconPickerItem; @FXML private ImagePickerItem iconPickerItem;
@@ -94,9 +95,12 @@ public final class VersionSettingsController {
initializeSelectedJava(); initializeSelectedJava();
}); });
javaItem.setSelectedData(null);
javaItem.setFallbackData(JavaVersion.fromCurrentEnvironment());
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS)
javaItem.getExtensionFilters().add(new FileChooser.ExtensionFilter("Java", "java.exe", "javaw.exe")); javaItem.getExtensionFilters().add(new FileChooser.ExtensionFilter("Java", "java.exe", "javaw.exe"));
gameDirItem.setCustomUserData(EnumGameDirectory.CUSTOM);
gameDirItem.loadChildren(Arrays.asList( gameDirItem.loadChildren(Arrays.asList(
gameDirItem.createChildren(i18n("settings.advanced.game_dir.default"), EnumGameDirectory.ROOT_FOLDER), gameDirItem.createChildren(i18n("settings.advanced.game_dir.default"), EnumGameDirectory.ROOT_FOLDER),
gameDirItem.createChildren(i18n("settings.advanced.game_dir.independent"), EnumGameDirectory.VERSION_FOLDER) gameDirItem.createChildren(i18n("settings.advanced.game_dir.independent"), EnumGameDirectory.VERSION_FOLDER)
@@ -132,16 +136,19 @@ public final class VersionSettingsController {
FXUtils.unbindString(txtServerIP, lastVersionSetting.serverIpProperty()); FXUtils.unbindString(txtServerIP, lastVersionSetting.serverIpProperty());
FXUtils.unbindBoolean(chkFullscreen, lastVersionSetting.fullscreenProperty()); FXUtils.unbindBoolean(chkFullscreen, lastVersionSetting.fullscreenProperty());
FXUtils.unbindBoolean(chkNoGameCheck, lastVersionSetting.notCheckGameProperty()); FXUtils.unbindBoolean(chkNoGameCheck, lastVersionSetting.notCheckGameProperty());
FXUtils.unbindBoolean(chkNoCommon, lastVersionSetting.noCommonProperty());
FXUtils.unbindBoolean(chkNoJVMArgs, lastVersionSetting.noJVMArgsProperty()); FXUtils.unbindBoolean(chkNoJVMArgs, lastVersionSetting.noJVMArgsProperty());
FXUtils.unbindBoolean(chkShowLogs, lastVersionSetting.showLogsProperty()); FXUtils.unbindBoolean(chkShowLogs, lastVersionSetting.showLogsProperty());
FXUtils.unbindEnum(cboLauncherVisibility); FXUtils.unbindEnum(cboLauncherVisibility);
globalItem.selectedDataProperty().unbindBidirectional(lastVersionSetting.usesGlobalProperty());
gameDirItem.selectedDataProperty().unbindBidirectional(lastVersionSetting.gameDirTypeProperty());
gameDirItem.subtitleProperty().unbind();
} }
// unbind data fields // unbind data fields
globalItem.setToggleSelectedListener(null); globalItem.setToggleSelectedListener(null);
javaItem.setToggleSelectedListener(null); javaItem.setToggleSelectedListener(null);
gameDirItem.setToggleSelectedListener(null);
// bind new data fields // bind new data fields
FXUtils.bindInt(txtWidth, versionSetting.widthProperty()); FXUtils.bindInt(txtWidth, versionSetting.widthProperty());
@@ -157,19 +164,15 @@ public final class VersionSettingsController {
FXUtils.bindString(txtServerIP, versionSetting.serverIpProperty()); FXUtils.bindString(txtServerIP, versionSetting.serverIpProperty());
FXUtils.bindBoolean(chkFullscreen, versionSetting.fullscreenProperty()); FXUtils.bindBoolean(chkFullscreen, versionSetting.fullscreenProperty());
FXUtils.bindBoolean(chkNoGameCheck, versionSetting.notCheckGameProperty()); FXUtils.bindBoolean(chkNoGameCheck, versionSetting.notCheckGameProperty());
FXUtils.bindBoolean(chkNoCommon, versionSetting.noCommonProperty());
FXUtils.bindBoolean(chkNoJVMArgs, versionSetting.noJVMArgsProperty()); FXUtils.bindBoolean(chkNoJVMArgs, versionSetting.noJVMArgsProperty());
FXUtils.bindBoolean(chkShowLogs, versionSetting.showLogsProperty()); FXUtils.bindBoolean(chkShowLogs, versionSetting.showLogsProperty());
FXUtils.bindEnum(cboLauncherVisibility, versionSetting.launcherVisibilityProperty()); FXUtils.bindEnum(cboLauncherVisibility, versionSetting.launcherVisibilityProperty());
javaItem.setToggleSelectedListener(newValue -> { javaItem.setToggleSelectedListener(newValue -> {
if (javaItem.isCustomToggle(newValue)) { if (javaItem.isCustomToggle(newValue)) {
versionSetting.setJava("Custom"); versionSetting.setUsesCustomJavaDir();
versionSetting.setDefaultJavaPath(null);
} else { } else {
JavaVersion java = (JavaVersion) newValue.getUserData(); versionSetting.setJavaVersion((JavaVersion) newValue.getUserData());
versionSetting.setJava(java.getVersion());
versionSetting.setDefaultJavaPath(java.getBinary().toString());
} }
}); });
@@ -177,13 +180,13 @@ public final class VersionSettingsController {
versionSetting.javaProperty().setChangedListener(it -> initJavaSubtitle(versionSetting)); versionSetting.javaProperty().setChangedListener(it -> initJavaSubtitle(versionSetting));
initJavaSubtitle(versionSetting); initJavaSubtitle(versionSetting);
if (versionSetting.isUsesGlobal()) globalItem.selectedDataProperty().bindBidirectional(versionSetting.usesGlobalProperty());
globalItem.getGroup().getToggles().stream().filter(it -> it.getUserData() == Boolean.TRUE).findFirst().ifPresent(it -> it.setSelected(true)); globalItem.subtitleProperty().bind(Bindings.createStringBinding(() -> i18n(versionSetting.isUsesGlobal() ? "settings.type.global" : "settings.type.special"),
else versionSetting.usesGlobalProperty()));
globalItem.getGroup().getToggles().stream().filter(it -> it.getUserData() == Boolean.FALSE).findFirst().ifPresent(it -> it.setSelected(true));
globalItem.setToggleSelectedListener(newValue -> { globalItem.setToggleSelectedListener(newValue -> {
// do not call versionSettings.setUsesGlobal(true/false) // do not call versionSettings.setUsesGlobal(true/false)
// because versionSettings can be the global one. // because versionSettings can be the global one.
// global versionSettings.usesGlobal is always true.
if ((Boolean) newValue.getUserData()) if ((Boolean) newValue.getUserData())
profile.globalizeVersionSetting(versionId); profile.globalizeVersionSetting(versionId);
else else
@@ -192,19 +195,9 @@ public final class VersionSettingsController {
Platform.runLater(() -> loadVersionSetting(profile, versionId)); Platform.runLater(() -> loadVersionSetting(profile, versionId));
}); });
versionSetting.usesGlobalProperty().setChangedListenerAndOperate(it -> gameDirItem.selectedDataProperty().bindBidirectional(versionSetting.gameDirTypeProperty());
globalItem.setSubtitle(i18n(versionSetting.isUsesGlobal() ? "settings.type.global" : "settings.type.special"))); gameDirItem.subtitleProperty().bind(Bindings.createStringBinding(() -> Paths.get(profile.getRepository().getRunDirectory(versionId).getAbsolutePath()).normalize().toString(),
versionSetting.gameDirProperty(), versionSetting.gameDirTypeProperty()));
gameDirItem.getGroup().getToggles().stream()
.filter(it -> it.getUserData() == versionSetting.getGameDirType())
.findFirst().ifPresent(toggle -> toggle.setSelected(true));
gameDirItem.setCustomUserData(EnumGameDirectory.CUSTOM);
gameDirItem.setToggleSelectedListener(newValue -> versionSetting.setGameDirType((EnumGameDirectory) newValue.getUserData()));
versionSetting.gameDirProperty().setChangedListener(it -> initGameDirSubtitle(versionSetting));
versionSetting.gameDirTypeProperty().setChangedListener(it -> initGameDirSubtitle(versionSetting));
initGameDirSubtitle(versionSetting);
lastVersionSetting = versionSetting; lastVersionSetting = versionSetting;
@@ -219,30 +212,14 @@ public final class VersionSettingsController {
return; return;
} }
List<Toggle> toggles = javaItem.getGroup().getToggles(); if (lastVersionSetting.isUsesCustomJavaDir()) {
if ("Custom".equals(lastVersionSetting.getJava())) { javaItem.setSelectedData(null);
toggles.stream()
.filter(javaItem::isCustomToggle)
.findFirst()
.get().setSelected(true);
} else { } else {
JavaVersion selectedJava;
try { try {
selectedJava = lastVersionSetting.getJavaVersion(); javaItem.setSelectedData(lastVersionSetting.getJavaVersion());
} catch (InterruptedException e) { } catch (InterruptedException e) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
return;
} }
toggles.stream()
.filter(it -> it.getUserData() == selectedJava)
.findFirst()
.orElseGet( // fallback to select current java
() -> toggles.stream()
.filter(it -> it.getUserData() == JavaVersion.fromCurrentEnvironment())
.findFirst()
.get())
.setSelected(true);
;
} }
} }
@@ -253,10 +230,6 @@ public final class VersionSettingsController {
.map(JavaVersion::getBinary).map(File::getAbsolutePath).orElse("Invalid Java Directory")))); .map(JavaVersion::getBinary).map(File::getAbsolutePath).orElse("Invalid Java Directory"))));
} }
private void initGameDirSubtitle(VersionSetting versionSetting) {
gameDirItem.setSubtitle(profile.getRepository().getRunDirectory(versionId).getAbsolutePath());
}
@FXML @FXML
private void onExploreIcon() { private void onExploreIcon() {
FileChooser chooser = new FileChooser(); FileChooser chooser = new FileChooser();

View File

@@ -21,10 +21,7 @@ import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXRadioButton; import com.jfoenix.controls.JFXRadioButton;
import com.jfoenix.controls.JFXTextField; import com.jfoenix.controls.JFXTextField;
import javafx.beans.NamedArg; import javafx.beans.NamedArg;
import javafx.beans.property.BooleanProperty; import javafx.beans.property.*;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.geometry.Pos; import javafx.geometry.Pos;
@@ -46,12 +43,15 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
import java.io.File; import java.io.File;
import java.util.Collection; import java.util.Collection;
import java.util.Optional;
import java.util.function.Consumer; import java.util.function.Consumer;
public class MultiFileItem extends ComponentList { public class MultiFileItem<T> extends ComponentList {
private final StringProperty customTitle = new SimpleStringProperty(this, "customTitle", i18n("selector.custom")); private final StringProperty customTitle = new SimpleStringProperty(this, "customTitle", i18n("selector.custom"));
private final StringProperty chooserTitle = new SimpleStringProperty(this, "chooserTitle", i18n("selector.choose_file")); private final StringProperty chooserTitle = new SimpleStringProperty(this, "chooserTitle", i18n("selector.choose_file"));
private final BooleanProperty directory = new SimpleBooleanProperty(this, "directory", false); private final BooleanProperty directory = new SimpleBooleanProperty(this, "directory", false);
private final ObjectProperty<T> selectedData = new SimpleObjectProperty<>(this, "selectedData");
private final ObjectProperty<T> fallbackData = new SimpleObjectProperty<>(this, "fallbackData");
private final ObservableList<FileChooser.ExtensionFilter> extensionFilters = FXCollections.observableArrayList(); private final ObservableList<FileChooser.ExtensionFilter> extensionFilters = FXCollections.observableArrayList();
private final ToggleGroup group = new ToggleGroup(); private final ToggleGroup group = new ToggleGroup();
@@ -64,6 +64,7 @@ public class MultiFileItem extends ComponentList {
private Consumer<Toggle> toggleSelectedListener; private Consumer<Toggle> toggleSelectedListener;
@SuppressWarnings("unchecked")
public MultiFileItem(@NamedArg(value = "hasCustom", defaultValue = "true") boolean hasCustom) { public MultiFileItem(@NamedArg(value = "hasCustom", defaultValue = "true") boolean hasCustom) {
this.hasCustom = hasCustom; this.hasCustom = hasCustom;
@@ -110,6 +111,20 @@ public class MultiFileItem extends ComponentList {
group.selectedToggleProperty().addListener((a, b, newValue) -> { group.selectedToggleProperty().addListener((a, b, newValue) -> {
if (toggleSelectedListener != null) if (toggleSelectedListener != null)
toggleSelectedListener.accept(newValue); toggleSelectedListener.accept(newValue);
selectedData.set((T) newValue.getUserData());
});
selectedData.addListener((a, b, newValue) -> {
Optional<Toggle> selecting = group.getToggles().stream()
.filter(it -> it.getUserData() == newValue)
.findFirst();
if (!selecting.isPresent()) {
selecting = group.getToggles().stream()
.filter(it -> it.getUserData() == getFallbackData())
.findFirst();
}
selecting.ifPresent(toggle -> toggle.setSelected(true));
}); });
} }
@@ -117,11 +132,11 @@ public class MultiFileItem extends ComponentList {
return createChildren(title, null); return createChildren(title, null);
} }
public Node createChildren(String title, Object userData) { public Node createChildren(String title, T userData) {
return createChildren(title, "", userData); return createChildren(title, "", userData);
} }
public Node createChildren(String title, String subtitle, Object userData) { public Node createChildren(String title, String subtitle, T userData) {
BorderPane pane = new BorderPane(); BorderPane pane = new BorderPane();
pane.setStyle("-fx-padding: 3;"); pane.setStyle("-fx-padding: 3;");
FXUtils.setLimitHeight(pane, 20); FXUtils.setLimitHeight(pane, 20);
@@ -146,12 +161,9 @@ public class MultiFileItem extends ComponentList {
pane.getChildren().add(custom); pane.getChildren().add(custom);
} }
public void onExploreJavaDir() { public void loadChildren(Collection<Node> list, T customUserData) {
DirectoryChooser chooser = new DirectoryChooser(); loadChildren(list);
chooser.setTitle(i18n(getChooserTitle())); setCustomUserData(customUserData);
File selectedDir = chooser.showDialog(Controllers.getStage());
if (selectedDir != null)
txtCustom.setText(selectedDir.getAbsolutePath());
} }
public ToggleGroup getGroup() { public ToggleGroup getGroup() {
@@ -182,7 +194,7 @@ public class MultiFileItem extends ComponentList {
this.chooserTitle.set(chooserTitle); this.chooserTitle.set(chooserTitle);
} }
public void setCustomUserData(Object userData) { public void setCustomUserData(T userData) {
radioCustom.setUserData(userData); radioCustom.setUserData(userData);
} }
@@ -225,4 +237,28 @@ public class MultiFileItem extends ComponentList {
public ObservableList<FileChooser.ExtensionFilter> getExtensionFilters() { public ObservableList<FileChooser.ExtensionFilter> getExtensionFilters() {
return extensionFilters; return extensionFilters;
} }
public T getSelectedData() {
return selectedData.get();
}
public ObjectProperty<T> selectedDataProperty() {
return selectedData;
}
public void setSelectedData(T selectedData) {
this.selectedData.set(selectedData);
}
public T getFallbackData() {
return fallbackData.get();
}
public ObjectProperty<T> fallbackDataProperty() {
return fallbackData;
}
public void setFallbackData(T fallbackData) {
this.fallbackData.set(fallbackData);
}
} }

View File

@@ -31,7 +31,7 @@
</right> </right>
</BorderPane> </BorderPane>
<FileItem fx:id="fileCommonLocation" name="%launcher.common_directory" title="%launcher.common_directory.choose" tooltip="%settings.launcher.common_path.tooltip" /> <MultiFileItem fx:id="fileCommonLocation" title="%launcher.common_directory" chooserTitle="%launcher.common_directory.choose" hasSubtitle="true" customText="%settings.custom" />
<MultiFileItem fx:id="backgroundItem" title="%launcher.background" chooserTitle="%launcher.background.choose" <MultiFileItem fx:id="backgroundItem" title="%launcher.background" chooserTitle="%launcher.background.choose"
hasSubtitle="true" customText="%settings.custom" /> hasSubtitle="true" customText="%settings.custom" />

View File

@@ -139,14 +139,6 @@
<JFXToggleButton fx:id="chkNoJVMArgs" size="7" FXUtils.limitHeight="10" /> <JFXToggleButton fx:id="chkNoJVMArgs" size="7" FXUtils.limitHeight="10" />
</right> </right>
</BorderPane> </BorderPane>
<BorderPane>
<left>
<Label text="%settings.advanced.no_common"/>
</left>
<right>
<JFXToggleButton fx:id="chkNoCommon" size="7" FXUtils.limitHeight="10" />
</right>
</BorderPane>
<BorderPane> <BorderPane>
<left> <left>
<Label text="%settings.advanced.dont_check_game_completeness"/> <Label text="%settings.advanced.dont_check_game_completeness"/>

View File

@@ -153,6 +153,8 @@ launcher.background.choose=Choose background path.
launcher.background.default=Default launcher.background.default=Default
launcher.common_directory=Common Directory launcher.common_directory=Common Directory
launcher.common_directory.choose=Choose common directory. launcher.common_directory.choose=Choose common directory.
launcher.common_directory.default=Default
launcher.common_directory.disabled=Disabled
launcher.contact=Contact Us launcher.contact=Contact Us
launcher.crash=Hello Minecraft! Launcher has crashed! launcher.crash=Hello Minecraft! Launcher has crashed!
launcher.crash_out_dated=Hello Minecraft! Launcher has crashed! Your launcher is outdated. Update it! launcher.crash_out_dated=Hello Minecraft! Launcher has crashed! Your launcher is outdated. Update it!

View File

@@ -153,6 +153,8 @@ launcher.background.choose=選擇背景路徑
launcher.background.default=默認(自動檢索啓動器同目錄下的 background.png/jpg 及 bg 文件夾內的圖片) launcher.background.default=默認(自動檢索啓動器同目錄下的 background.png/jpg 及 bg 文件夾內的圖片)
launcher.common_directory=公共文件夾 launcher.common_directory=公共文件夾
launcher.common_directory.choose=選擇公共文件夾 launcher.common_directory.choose=選擇公共文件夾
launcher.common_directory.default=默認
launcher.common_directory.disabled=禁用
launcher.contact=聯繫我們 launcher.contact=聯繫我們
launcher.crash=Hello Minecraft! Launcher 遇到了無法處理的錯誤,請複製下列內容並通過 MCBBS、貼吧、GitHub 或 Minecraft Forum 反饋 bug。 launcher.crash=Hello Minecraft! Launcher 遇到了無法處理的錯誤,請複製下列內容並通過 MCBBS、貼吧、GitHub 或 Minecraft Forum 反饋 bug。
launcher.crash_out_dated=Hello Minecraft! Launcher 遇到了無法處理的錯誤,已檢測到您的啓動器不是最新版本,請更新後再試! launcher.crash_out_dated=Hello Minecraft! Launcher 遇到了無法處理的錯誤,已檢測到您的啓動器不是最新版本,請更新後再試!

View File

@@ -151,8 +151,10 @@ launcher=启动器
launcher.background=背景地址 launcher.background=背景地址
launcher.background.choose=选择背景路径 launcher.background.choose=选择背景路径
launcher.background.default=默认(自动检索启动器同目录下的 background.png/jpg 及 bg 文件夹内的图片) launcher.background.default=默认(自动检索启动器同目录下的 background.png/jpg 及 bg 文件夹内的图片)
launcher.common_directory=公共文件夹 launcher.common_directory=公共文件夹(统一存放游戏资源文件,减少游戏体积)
launcher.common_directory.choose=选择公共文件夹 launcher.common_directory.choose=选择公共文件夹
launcher.common_directory.default=默认(%AppData%/.minecraft 或者 ~/.minecraft
launcher.common_directory.disabled=禁用(总是使用游戏路径)
launcher.contact=联系我们 launcher.contact=联系我们
launcher.crash=Hello Minecraft! Launcher 遇到了无法处理的错误,请复制下列内容并通过 MCBBS、贴吧、GitHub 或 Minecraft Forum 反馈 bug。 launcher.crash=Hello Minecraft! Launcher 遇到了无法处理的错误,请复制下列内容并通过 MCBBS、贴吧、GitHub 或 Minecraft Forum 反馈 bug。
launcher.crash_out_dated=Hello Minecraft! Launcher 遇到了无法处理的错误,已检测到您的启动器不是最新版本,请更新后再试! launcher.crash_out_dated=Hello Minecraft! Launcher 遇到了无法处理的错误,已检测到您的启动器不是最新版本,请更新后再试!