Add game dir option: use relative path

This commit is contained in:
yushijinhun 2018-07-14 14:32:48 +08:00
parent ff74417352
commit f789343b6e
No known key found for this signature in database
GPG Key ID: 5BC167F73EA558E4
8 changed files with 92 additions and 14 deletions

View File

@ -19,6 +19,9 @@ package org.jackhuang.hmcl.setting;
import com.google.gson.*; import com.google.gson.*;
import javafx.beans.InvalidationListener; import javafx.beans.InvalidationListener;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import org.jackhuang.hmcl.game.HMCLDependencyManager; import org.jackhuang.hmcl.game.HMCLDependencyManager;
import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.game.HMCLGameRepository;
import org.jackhuang.hmcl.mod.ModManager; import org.jackhuang.hmcl.mod.ModManager;
@ -83,8 +86,18 @@ public final class Profile {
nameProperty.set(name); nameProperty.set(name);
} }
public Profile() { private BooleanProperty useRelativePathProperty = new SimpleBooleanProperty(this, "useRelativePath", false);
this("Default");
public BooleanProperty useRelativePathProperty() {
return useRelativePathProperty();
}
public boolean isUseRelativePath() {
return useRelativePathProperty.get();
}
public void setUseRelativePath(boolean useRelativePath) {
useRelativePathProperty.set(useRelativePath);
} }
public Profile(String name) { public Profile(String name) {
@ -153,6 +166,7 @@ public final class Profile {
return new ToStringBuilder(this) return new ToStringBuilder(this)
.append("gameDir", getGameDir()) .append("gameDir", getGameDir())
.append("name", getName()) .append("name", getName())
.append("useRelativePath", isUseRelativePath())
.toString(); .toString();
} }
@ -160,6 +174,7 @@ public final class Profile {
nameProperty.addListener(listener); nameProperty.addListener(listener);
globalProperty.addListener(listener); globalProperty.addListener(listener);
gameDirProperty.addListener(listener); gameDirProperty.addListener(listener);
useRelativePathProperty.addListener(listener);
} }
public static final class Serializer implements JsonSerializer<Profile>, JsonDeserializer<Profile> { public static final class Serializer implements JsonSerializer<Profile>, JsonDeserializer<Profile> {
@ -176,6 +191,7 @@ public final class Profile {
JsonObject jsonObject = new JsonObject(); JsonObject jsonObject = new JsonObject();
jsonObject.add("global", context.serialize(src.getGlobal())); jsonObject.add("global", context.serialize(src.getGlobal()));
jsonObject.addProperty("gameDir", src.getGameDir().getPath()); jsonObject.addProperty("gameDir", src.getGameDir().getPath());
jsonObject.addProperty("useRelativePath", src.isUseRelativePath());
return jsonObject; return jsonObject;
} }
@ -188,6 +204,8 @@ public final class Profile {
Profile profile = new Profile("Default", new File(gameDir)); Profile profile = new Profile("Default", new File(gameDir));
profile.setGlobal(context.deserialize(obj.get("global"), VersionSetting.class)); profile.setGlobal(context.deserialize(obj.get("global"), VersionSetting.class));
profile.setUseRelativePath(Optional.ofNullable(obj.get("useRelativePath")).map(JsonElement::getAsBoolean).orElse(false));
return profile; return profile;
} }

View File

@ -512,8 +512,12 @@ public class Settings {
private void checkProfileMap() { private void checkProfileMap() {
if (getProfileMap().isEmpty()) { if (getProfileMap().isEmpty()) {
getProfileMap().put(Profiles.DEFAULT_PROFILE, new Profile(Profiles.DEFAULT_PROFILE)); Profile current = new Profile(Profiles.DEFAULT_PROFILE);
getProfileMap().put(Profiles.HOME_PROFILE, new Profile(Profiles.HOME_PROFILE, Launcher.MINECRAFT_DIRECTORY)); current.setUseRelativePath(true);
getProfileMap().put(Profiles.DEFAULT_PROFILE, current);
Profile home = new Profile(Profiles.HOME_PROFILE, Launcher.MINECRAFT_DIRECTORY);
getProfileMap().put(Profiles.HOME_PROFILE, home);
} }
} }

View File

@ -18,6 +18,7 @@
package org.jackhuang.hmcl.ui; package org.jackhuang.hmcl.ui;
import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXCheckBox;
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.beans.property.StringProperty;
@ -40,12 +41,11 @@ public final class ProfilePage extends StackPane implements DecoratorPage {
private final StringProperty location; private final StringProperty location;
private final Profile profile; private final Profile profile;
@FXML @FXML private JFXTextField txtProfileName;
private JFXTextField txtProfileName; @FXML private FileItem gameDir;
@FXML
private FileItem gameDir;
@FXML private JFXButton btnSave; @FXML private JFXButton btnSave;
@FXML private JFXButton btnDelete; @FXML private JFXButton btnDelete;
@FXML private JFXCheckBox toggleUseRelativePath;
/** /**
* @param profile null if creating a new profile. * @param profile null if creating a new profile.
@ -69,9 +69,12 @@ public final class ProfilePage extends StackPane implements DecoratorPage {
FXUtils.onChangeAndOperate(location, it -> { FXUtils.onChangeAndOperate(location, it -> {
btnSave.setDisable(!txtProfileName.validate() || StringUtils.isBlank(getLocation())); btnSave.setDisable(!txtProfileName.validate() || StringUtils.isBlank(getLocation()));
}); });
gameDir.convertToRelativePathProperty().bind(toggleUseRelativePath.selectedProperty());
if (profile == null) if (profile == null) {
btnDelete.setVisible(false); btnDelete.setVisible(false);
} else {
toggleUseRelativePath.setSelected(profile.isUseRelativePath());
}
} }
@FXML @FXML
@ -86,13 +89,17 @@ public final class ProfilePage extends StackPane implements DecoratorPage {
private void onSave() { private void onSave() {
if (profile != null) { if (profile != null) {
profile.setName(txtProfileName.getText()); profile.setName(txtProfileName.getText());
if (StringUtils.isNotBlank(getLocation())) profile.setUseRelativePath(toggleUseRelativePath.isSelected());
if (StringUtils.isNotBlank(getLocation())) {
profile.setGameDir(new File(getLocation())); profile.setGameDir(new File(getLocation()));
}
} else { } else {
if (StringUtils.isBlank(getLocation())) { if (StringUtils.isBlank(getLocation())) {
gameDir.onExplore(); gameDir.onExplore();
} }
Settings.INSTANCE.putProfile(new Profile(txtProfileName.getText(), new File(getLocation()))); Profile newProfile = new Profile(txtProfileName.getText(), new File(getLocation()));
newProfile.setUseRelativePath(toggleUseRelativePath.isSelected());
Settings.INSTANCE.putProfile(newProfile);
} }
Settings.INSTANCE.onProfileLoading(); Settings.INSTANCE.onProfileLoading();

View File

@ -18,6 +18,9 @@
package org.jackhuang.hmcl.ui.construct; package org.jackhuang.hmcl.ui.construct;
import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXButton;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty; import javafx.beans.property.StringProperty;
import javafx.scene.control.Label; import javafx.scene.control.Label;
@ -30,9 +33,13 @@ import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG; import org.jackhuang.hmcl.ui.SVG;
import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
import java.io.File; import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
public class FileItem extends BorderPane { public class FileItem extends BorderPane {
private final Label lblPath = new Label(); private final Label lblPath = new Label();
@ -41,6 +48,7 @@ public class FileItem extends BorderPane {
private final SimpleStringProperty title = new SimpleStringProperty(this, "title"); private final SimpleStringProperty title = new SimpleStringProperty(this, "title");
private final SimpleStringProperty tooltip = new SimpleStringProperty(this, "tooltip"); private final SimpleStringProperty tooltip = new SimpleStringProperty(this, "tooltip");
private final SimpleStringProperty path = new SimpleStringProperty(this, "path"); private final SimpleStringProperty path = new SimpleStringProperty(this, "path");
private final SimpleBooleanProperty convertToRelativePath = new SimpleBooleanProperty(this, "convertToRelativePath");
public FileItem() { public FileItem() {
VBox left = new VBox(); VBox left = new VBox();
@ -61,6 +69,23 @@ public class FileItem extends BorderPane {
Tooltip tip = new Tooltip(); Tooltip tip = new Tooltip();
tip.textProperty().bind(tooltipProperty()); tip.textProperty().bind(tooltipProperty());
Tooltip.install(this, tip); Tooltip.install(this, tip);
convertToRelativePath.addListener(onInvalidating(() -> path.set(processPath(path.get()))));
}
/**
* Converts the given path to absolute/relative(if possible) path according to {@link #convertToRelativePathProperty()}.
*/
private String processPath(String path) {
Path given = Paths.get(path).toAbsolutePath();
if (isConvertToRelativePath()) {
try {
return Paths.get(".").toAbsolutePath().relativize(given).normalize().toString();
} catch (IllegalArgumentException e) {
// the given path can't be relativized against current path
}
}
return given.normalize().toString();
} }
public void onExplore() { public void onExplore() {
@ -77,8 +102,9 @@ public class FileItem extends BorderPane {
} }
chooser.titleProperty().bind(titleProperty()); chooser.titleProperty().bind(titleProperty());
File selectedDir = chooser.showDialog(Controllers.getStage()); File selectedDir = chooser.showDialog(Controllers.getStage());
if (selectedDir != null) if (selectedDir != null) {
path.set(selectedDir.getAbsolutePath()); path.set(processPath(selectedDir.toString()));
}
chooser.titleProperty().unbind(); chooser.titleProperty().unbind();
} }
@ -129,4 +155,16 @@ public class FileItem extends BorderPane {
public void setPath(String path) { public void setPath(String path) {
this.path.set(path); this.path.set(path);
} }
public boolean isConvertToRelativePath() {
return convertToRelativePath.get();
}
public BooleanProperty convertToRelativePathProperty() {
return convertToRelativePath;
}
public void setConvertToRelativePath(boolean convertToRelativePath) {
this.convertToRelativePath.set(convertToRelativePath);
}
} }

View File

@ -1,11 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import com.jfoenix.controls.JFXButton?> <?import com.jfoenix.controls.JFXButton?>
<?import com.jfoenix.controls.JFXCheckBox?>
<?import com.jfoenix.controls.JFXTextField?> <?import com.jfoenix.controls.JFXTextField?>
<?import com.jfoenix.validation.RequiredFieldValidator?> <?import com.jfoenix.validation.RequiredFieldValidator?>
<?import javafx.scene.control.Label?> <?import javafx.scene.control.Label?>
<?import javafx.scene.control.ScrollPane?> <?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.layout.*?> <?import javafx.scene.layout.*?>
<?import javafx.geometry.*?>
<?import org.jackhuang.hmcl.ui.construct.ComponentList?> <?import org.jackhuang.hmcl.ui.construct.ComponentList?>
<?import org.jackhuang.hmcl.ui.construct.FileItem?> <?import org.jackhuang.hmcl.ui.construct.FileItem?>
<fx:root xmlns="http://javafx.com/javafx" <fx:root xmlns="http://javafx.com/javafx"
@ -32,6 +34,12 @@
</BorderPane> </BorderPane>
<FileItem fx:id="gameDir" name="%profile.instance_directory" title="%profile.instance_directory.choose"/> <FileItem fx:id="gameDir" name="%profile.instance_directory" title="%profile.instance_directory.choose"/>
<JFXCheckBox fx:id="toggleUseRelativePath" text="%profile.use_relative_path" StackPane.alignment="CENTER_LEFT">
<StackPane.margin>
<Insets left="-10"/>
</StackPane.margin>
</JFXCheckBox>
</ComponentList> </ComponentList>
</VBox> </VBox>

View File

@ -240,6 +240,7 @@ profile.instance_directory.choose=Choose Game Directory
profile.new=New Config profile.new=New Config
profile.title=Game Directories profile.title=Game Directories
profile.selected=Selected profile.selected=Selected
profile.use_relative_path=Use relative path for game directory if possible
selector.choose=Choose selector.choose=Choose
selector.choose_file=Select a file selector.choose_file=Select a file

View File

@ -240,6 +240,7 @@ profile.instance_directory.choose=選擇遊戲路徑
profile.new=新建配置 profile.new=新建配置
profile.title=遊戲目錄 profile.title=遊戲目錄
profile.selected=已選中 profile.selected=已選中
profile.use_relative_path=若可能,遊戲目錄使用相對路徑
selector.choose=選擇 selector.choose=選擇
selector.choose_file=選擇文件 selector.choose_file=選擇文件

View File

@ -240,6 +240,7 @@ profile.instance_directory.choose=选择游戏路径
profile.new=新建配置 profile.new=新建配置
profile.title=游戏目录 profile.title=游戏目录
profile.selected=已选中 profile.selected=已选中
profile.use_relative_path=若可能,游戏目录使用相对路径
selector.choose=选择 selector.choose=选择
selector.choose_file=选择文件 selector.choose_file=选择文件