diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Profile.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Profile.java index 1230a9f25..78a6ca28a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Profile.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Profile.java @@ -75,22 +75,16 @@ public final class Profile implements Observable { this.gameDir.set(gameDir); } - private final ObjectProperty global = new ImmediateObjectProperty<>(this, "global", new VersionSetting()); + private final ReadOnlyObjectWrapper global = new ReadOnlyObjectWrapper<>(this, "global"); - public ObjectProperty globalProperty() { - return global; + public ReadOnlyObjectProperty globalProperty() { + return global.getReadOnlyProperty(); } public VersionSetting getGlobal() { return global.get(); } - private void setGlobal(VersionSetting global) { - if (global == null) - global = new VersionSetting(); - this.global.set(global); - } - private final ImmediateStringProperty name; public ImmediateStringProperty nameProperty() { @@ -124,10 +118,15 @@ public final class Profile implements Observable { } public Profile(String name, File initialGameDir) { + this(name, initialGameDir, new VersionSetting()); + } + + public Profile(String name, File initialGameDir, VersionSetting global) { this.name = new ImmediateStringProperty(this, "name", name); gameDir = new ImmediateObjectProperty<>(this, "gameDir", initialGameDir); repository = new HMCLGameRepository(this, initialGameDir); modManager = new ModManager(repository); + this.global.set(global == null ? new VersionSetting() : global); gameDir.addListener((a, b, newValue) -> repository.changeDirectory(newValue)); selectedVersion.addListener(o -> checkSelectedVersion()); @@ -227,7 +226,7 @@ public final class Profile implements Observable { } protected void invalidate() { - JFXUtilities.runInFX(observableHelper::invalidate); + observableHelper.invalidate(); } public static final class Serializer implements JsonSerializer, JsonDeserializer { @@ -256,8 +255,7 @@ public final class Profile implements Observable { JsonObject obj = (JsonObject) json; String gameDir = Optional.ofNullable(obj.get("gameDir")).map(JsonElement::getAsString).orElse(""); - Profile profile = new Profile("Default", new File(gameDir)); - profile.setGlobal(context.deserialize(obj.get("global"), VersionSetting.class)); + Profile profile = new Profile("Default", new File(gameDir), context.deserialize(obj.get("global"), VersionSetting.class)); profile.setSelectedVersion(Optional.ofNullable(obj.get("selectedMinecraftVersion")).map(JsonElement::getAsString).orElse("")); profile.setUseRelativePath(Optional.ofNullable(obj.get("useRelativePath")).map(JsonElement::getAsBoolean).orElse(false)); return profile; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionPage.java index 35e924311..16134dd9d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionPage.java @@ -40,7 +40,7 @@ public final class VersionPage extends StackPane implements DecoratorPage { private final StringProperty title = new SimpleStringProperty(this, "title", null); @FXML - private VersionSettingsController versionSettingsController; + private VersionSettingsPage versionSettings; @FXML private Tab modTab; @FXML @@ -92,7 +92,7 @@ public final class VersionPage extends StackPane implements DecoratorPage { title.set(i18n("settings.game") + " - " + id); - versionSettingsController.loadVersionSetting(profile, id); + versionSettings.loadVersionSetting(profile, id); modController.setParentTab(tabPane); modTab.setUserData(modController); modController.loadMods(profile.getModManager(), id); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionSettingsController.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionSettingsPage.java similarity index 82% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionSettingsController.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionSettingsPage.java index d48a671f2..9bd1745b2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionSettingsController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/VersionSettingsPage.java @@ -17,16 +17,18 @@ */ package org.jackhuang.hmcl.ui; -import com.jfoenix.controls.JFXCheckBox; -import com.jfoenix.controls.JFXComboBox; -import com.jfoenix.controls.JFXTextField; -import com.jfoenix.controls.JFXToggleButton; +import com.jfoenix.controls.*; import javafx.application.Platform; +import javafx.beans.InvalidationListener; import javafx.beans.binding.Bindings; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; import javafx.fxml.FXML; import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; import javafx.scene.image.Image; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.stage.FileChooser; @@ -38,6 +40,8 @@ import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.construct.ComponentList; import org.jackhuang.hmcl.ui.construct.ImagePickerItem; import org.jackhuang.hmcl.ui.construct.MultiFileItem; +import org.jackhuang.hmcl.ui.decorator.DecoratorPage; +import org.jackhuang.hmcl.ui.versions.Versions; import org.jackhuang.hmcl.util.*; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; @@ -49,11 +53,14 @@ import java.util.List; import java.util.logging.Level; import java.util.stream.Collectors; -public final class VersionSettingsController { +public final class VersionSettingsPage extends StackPane implements DecoratorPage { + private final StringProperty title = new SimpleStringProperty(); + private VersionSetting lastVersionSetting = null; private Profile profile; private String versionId; private boolean javaItemsLoaded; + private InvalidationListener specificSettingsListener; @FXML private VBox rootPane; @FXML private ScrollPane scroll; @@ -67,16 +74,22 @@ public final class VersionSettingsController { @FXML private JFXTextField txtPrecallingCommand; @FXML private JFXTextField txtServerIP; @FXML private ComponentList advancedSettingsPane; + @FXML private ComponentList componentList; @FXML private JFXComboBox cboLauncherVisibility; @FXML private JFXCheckBox chkFullscreen; @FXML private Label lblPhysicalMemory; @FXML private JFXToggleButton chkNoJVMArgs; @FXML private JFXToggleButton chkNoGameCheck; - @FXML private MultiFileItem globalItem; @FXML private MultiFileItem javaItem; @FXML private MultiFileItem gameDirItem; @FXML private JFXToggleButton chkShowLogs; @FXML private ImagePickerItem iconPickerItem; + @FXML private JFXCheckBox chkEnableSpecificSettings; + @FXML private BorderPane settingsTypePane; + + public VersionSettingsPage() { + FXUtils.loadFXML(this, "/assets/fxml/version/version-settings.fxml"); + } @FXML private void initialize() { @@ -105,20 +118,42 @@ public final class VersionSettingsController { gameDirItem.createChildren(i18n("settings.advanced.game_dir.independent"), EnumGameDirectory.VERSION_FOLDER) )); - globalItem.loadChildren(Arrays.asList( - globalItem.createChildren(i18n("settings.type.global"), true), - globalItem.createChildren(i18n("settings.type.special"), false) - )); + chkEnableSpecificSettings.selectedProperty().addListener((a, b, newValue) -> { + if (versionId == null) return; + + // do not call versionSettings.setUsesGlobal(true/false) + // because versionSettings can be the global one. + // global versionSettings.usesGlobal is always true. + if (newValue) + profile.specializeVersionSetting(versionId); + else + profile.globalizeVersionSetting(versionId); + + Platform.runLater(() -> loadVersionSetting(profile, versionId)); + }); + + specificSettingsListener = o -> { + chkEnableSpecificSettings.setSelected(!lastVersionSetting.isUsesGlobal()); + }; + + componentList.disableProperty().bind(chkEnableSpecificSettings.selectedProperty().not()); + advancedSettingsPane.disableProperty().bind(chkEnableSpecificSettings.selectedProperty().not()); } public void loadVersionSetting(Profile profile, String versionId) { this.profile = profile; this.versionId = versionId; + if (versionId == null) { + componentList.removeChild(iconPickerItem); + rootPane.getChildren().remove(settingsTypePane); + chkEnableSpecificSettings.setSelected(true); + } + VersionSetting versionSetting = profile.getVersionSetting(versionId); - gameDirItem.setDisable(profile.getRepository().isModpack(versionId)); - globalItem.setDisable(profile.getRepository().isModpack(versionId)); + gameDirItem.setDisable(versionId != null && profile.getRepository().isModpack(versionId)); + settingsTypePane.setDisable(versionId != null && profile.getRepository().isModpack(versionId)); // unbind data fields if (lastVersionSetting != null) { @@ -139,14 +174,13 @@ public final class VersionSettingsController { FXUtils.unbindBoolean(chkShowLogs, lastVersionSetting.showLogsProperty()); FXUtils.unbindEnum(cboLauncherVisibility); - globalItem.selectedDataProperty().unbindBidirectional(lastVersionSetting.usesGlobalProperty()); + lastVersionSetting.usesGlobalProperty().removeListener(specificSettingsListener); gameDirItem.selectedDataProperty().unbindBidirectional(lastVersionSetting.gameDirTypeProperty()); gameDirItem.subtitleProperty().unbind(); } // unbind data fields - globalItem.setToggleSelectedListener(null); javaItem.setToggleSelectedListener(null); // bind new data fields @@ -167,6 +201,10 @@ public final class VersionSettingsController { FXUtils.bindBoolean(chkShowLogs, versionSetting.showLogsProperty()); FXUtils.bindEnum(cboLauncherVisibility, versionSetting.launcherVisibilityProperty()); + versionSetting.usesGlobalProperty().addListener(specificSettingsListener); + if (versionId != null) + chkEnableSpecificSettings.setSelected(!versionSetting.isUsesGlobal()); + javaItem.setToggleSelectedListener(newValue -> { if (javaItem.isCustomToggle(newValue)) { versionSetting.setUsesCustomJavaDir(); @@ -179,21 +217,6 @@ public final class VersionSettingsController { versionSetting.javaProperty().setChangedListener(it -> initJavaSubtitle(versionSetting)); initJavaSubtitle(versionSetting); - globalItem.selectedDataProperty().bindBidirectional(versionSetting.usesGlobalProperty()); - globalItem.subtitleProperty().bind(Bindings.createStringBinding(() -> i18n(versionSetting.isUsesGlobal() ? "settings.type.global" : "settings.type.special"), - versionSetting.usesGlobalProperty())); - globalItem.setToggleSelectedListener(newValue -> { - // do not call versionSettings.setUsesGlobal(true/false) - // because versionSettings can be the global one. - // global versionSettings.usesGlobal is always true. - if ((Boolean) newValue.getUserData()) - profile.globalizeVersionSetting(versionId); - else - profile.specializeVersionSetting(versionId); - - Platform.runLater(() -> loadVersionSetting(profile, versionId)); - }); - gameDirItem.selectedDataProperty().bindBidirectional(versionSetting.gameDirTypeProperty()); gameDirItem.subtitleProperty().bind(Bindings.createStringBinding(() -> Paths.get(profile.getRepository().getRunDirectory(versionId).getAbsolutePath()).normalize().toString(), versionSetting.gameDirProperty(), versionSetting.gameDirTypeProperty())); @@ -229,8 +252,16 @@ public final class VersionSettingsController { .map(JavaVersion::getBinary).map(File::getAbsolutePath).orElse("Invalid Java Directory")))); } + @FXML + private void editGlobalSettings() { + Versions.modifyGlobalSettings(profile); + } + @FXML private void onExploreIcon() { + if (versionId == null) + return; + FileChooser chooser = new FileChooser(); chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("extension.png"), "*.png")); File selectedFile = chooser.showOpenDialog(Controllers.getStage()); @@ -246,6 +277,11 @@ public final class VersionSettingsController { } private void loadIcon() { + if (versionId == null) { + iconPickerItem.setImage(new Image("/assets/img/grass.png")); + return; + } + File iconFile = profile.getRepository().getVersionIcon(versionId); if (iconFile.exists()) iconPickerItem.setImage(new Image("file:" + iconFile.getAbsolutePath())); @@ -253,4 +289,9 @@ public final class VersionSettingsController { iconPickerItem.setImage(new Image("/assets/img/grass.png")); FXUtils.limitSize(iconPickerItem.getImageView(), 32, 32); } + + @Override + public StringProperty titleProperty() { + return title; + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentList.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentList.java index 67d93a3f4..ffe131359 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentList.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/ComponentList.java @@ -60,7 +60,7 @@ public class ComponentList extends StackPane { vbox.getChildren().add(child); } - public void removeChildren(Node node) { + public void removeChild(Node node) { vbox.getChildren().removeIf(node1 -> node1.getProperties().get("node") == node); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameList.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameList.java index ec7bfc4d3..f07750fa9 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameList.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameList.java @@ -30,6 +30,7 @@ import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profiles; import org.jackhuang.hmcl.ui.Controllers; +import org.jackhuang.hmcl.ui.VersionSettingsPage; import org.jackhuang.hmcl.ui.download.DownloadWizardProvider; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; import org.jackhuang.hmcl.util.VersionNumber; @@ -111,7 +112,7 @@ public class GameList extends Control implements DecoratorPage { } public void modifyGlobalGameSettings() { - // Controllers.navigate(); + Versions.modifyGlobalSettings(profile); } @Override diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListSkin.java index da6fbdb73..b7cf3bb13 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListSkin.java @@ -79,13 +79,13 @@ public class GameListSkin extends SkinBase { btnRefresh.setOnMouseClicked(e -> skinnable.refresh()); toolbar.getChildren().add(btnRefresh); -// JFXButton btnModify = new JFXButton(); -// btnModify.getStyleClass().add("jfx-tool-bar-button"); -// btnModify.textFillProperty().bind(Theme.foregroundFillBinding()); -// btnModify.setGraphic(wrap(SVG.gear(Theme.foregroundFillBinding(), -1, -1))); -// btnModify.setText(I18n.i18n("settings.type.global.manage")); -// btnModify.setOnMouseClicked(e -> skinnable.modifyGlobalGameSettings()); -// toolbar.getChildren().add(btnModify); + JFXButton btnModify = new JFXButton(); + btnModify.getStyleClass().add("jfx-tool-bar-button"); + btnModify.textFillProperty().bind(Theme.foregroundFillBinding()); + btnModify.setGraphic(wrap(SVG.gear(Theme.foregroundFillBinding(), -1, -1))); + btnModify.setText(I18n.i18n("settings.type.global.manage")); + btnModify.setOnMouseClicked(e -> skinnable.modifyGlobalGameSettings()); + toolbar.getChildren().add(btnModify); root.setTop(toolbar); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java index 391ab00a0..949d3b0c1 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -27,11 +27,13 @@ import org.jackhuang.hmcl.mod.UnsupportedModpackException; import org.jackhuang.hmcl.setting.Accounts; import org.jackhuang.hmcl.setting.EnumGameDirectory; import org.jackhuang.hmcl.setting.Profile; +import org.jackhuang.hmcl.setting.Profiles; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.VersionSettingsPage; import org.jackhuang.hmcl.ui.construct.DialogCloseEvent; import org.jackhuang.hmcl.ui.construct.MessageBox; import org.jackhuang.hmcl.ui.export.ExportWizardProvider; @@ -130,4 +132,11 @@ public class Versions { else LauncherHelper.INSTANCE.launch(profile, Accounts.getSelectedAccount(), id, null); } + + public static void modifyGlobalSettings(Profile profile) { + VersionSettingsPage page = new VersionSettingsPage(); + page.loadVersionSetting(profile, null); + page.titleProperty().set(Profiles.getProfileDisplayName(profile) + " - " + i18n("settings.type.global.manage")); + Controllers.navigate(page); + } } diff --git a/HMCL/src/main/resources/assets/fxml/version/version-settings.fxml b/HMCL/src/main/resources/assets/fxml/version/version-settings.fxml index 0e9742998..d5524de68 100644 --- a/HMCL/src/main/resources/assets/fxml/version/version-settings.fxml +++ b/HMCL/src/main/resources/assets/fxml/version/version-settings.fxml @@ -8,15 +8,24 @@ - + - + - + + + + + + + + - + @@ -104,7 +113,7 @@ - + @@ -150,4 +159,4 @@ - + diff --git a/HMCL/src/main/resources/assets/fxml/version/version.fxml b/HMCL/src/main/resources/assets/fxml/version/version.fxml index e7923dae9..54f3eea7f 100644 --- a/HMCL/src/main/resources/assets/fxml/version/version.fxml +++ b/HMCL/src/main/resources/assets/fxml/version/version.fxml @@ -4,6 +4,7 @@ + - + diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index eaf4b37a7..bb2ec3aa5 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -313,7 +313,8 @@ settings.tabs.installers=Installers settings.type=Version setting type settings.type.global=Global global settings(all shared) settings.type.global.manage=Global Game Settings -settings.type.special=Specialized version settings(will not affect other versions) +settings.type.global.edit=Configure global game settings +settings.type.special.enable=Enable specialized settings for this game update=Update update.channel.dev=Update to development version diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index a2145257b..d4f1f479f 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -313,7 +313,8 @@ settings.tabs.installers=自動安裝 settings.type=版本設定類型 settings.type.global=全域版本設定(使用該設定的版本共用一套設定) settings.type.global.manage=全域遊戲設定 -settings.type.special=單獨版本設定(不會影響到其他版本的設定) +settings.type.global.edit=編輯全域遊戲設定 +settings.type.special.enable=啟用遊戲特別設定(不影響其他遊戲版本) update=啟動器更新 update.channel.dev=更新到開發版 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index 4397f57b3..efe44fdca 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -313,7 +313,8 @@ settings.tabs.installers=自动安装 settings.type=版本设置类型 settings.type.global=全局版本设置(使用该设置的版本共用一套设定) settings.type.global.manage=全局游戏设置 -settings.type.special=版本特定设置(不会影响到其他版本的设定) +settings.type.global.edit=编辑全局版本设置 +settings.type.special.enable=启用游戏特定设置(不影响其他游戏版本) update=启动器更新 update.channel.dev=更新到开发版