diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/game/HMCLGameRepository.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/game/HMCLGameRepository.kt index 8ccd8c080..4d8f3b552 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/game/HMCLGameRepository.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/game/HMCLGameRepository.kt @@ -56,6 +56,7 @@ class HMCLGameRepository(val profile: Profile, baseDirectory: File) return when (vs.gameDirType) { EnumGameDirectory.VERSION_FOLDER -> getVersionRoot(id) EnumGameDirectory.ROOT_FOLDER -> super.getRunDirectory(id) + EnumGameDirectory.CUSTOM -> File(vs.gameDir) } } } diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/setting/EnumGameDirectory.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/setting/EnumGameDirectory.kt index c3fff72c1..fc2447500 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/setting/EnumGameDirectory.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/setting/EnumGameDirectory.kt @@ -19,5 +19,6 @@ package org.jackhuang.hmcl.setting enum class EnumGameDirectory { ROOT_FOLDER, - VERSION_FOLDER + VERSION_FOLDER, + CUSTOM } \ No newline at end of file diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/setting/VersionSetting.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/setting/VersionSetting.kt index 182026c02..b86d9f43b 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/setting/VersionSetting.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/setting/VersionSetting.kt @@ -166,9 +166,15 @@ class VersionSetting() { * 0 - .minecraft
* 1 - .minecraft/versions/<version>/
*/ - val gameDirTypeProperty = ImmediateObjectProperty(this, "gameDirTypeProperty", EnumGameDirectory.ROOT_FOLDER) + val gameDirTypeProperty = ImmediateObjectProperty(this, "gameDirType", EnumGameDirectory.ROOT_FOLDER) var gameDirType: EnumGameDirectory by gameDirTypeProperty + /** + * Your custom gameDir + */ + val gameDirProperty = ImmediateStringProperty(this, "gameDir", "") + var gameDir: String by gameDirProperty + // launcher settings /** @@ -220,6 +226,7 @@ class VersionSetting() { widthProperty.addListener(listener) heightProperty.addListener(listener) gameDirTypeProperty.addListener(listener) + gameDirProperty.addListener(listener) launcherVisibilityProperty.addListener(listener) } @@ -272,6 +279,7 @@ class VersionSetting() { addProperty("notCheckGame", src.notCheckGame) addProperty("noCommon", src.noCommon) addProperty("showLogs", src.showLogs) + addProperty("gameDir", src.gameDir) addProperty("launcherVisibility", src.launcherVisibility.ordinal) addProperty("gameDirType", src.gameDirType.ordinal) } @@ -299,6 +307,7 @@ class VersionSetting() { serverIp = json["serverIp"]?.asString ?: "" java = json["java"]?.asString ?: "" wrapper = json["wrapper"]?.asString ?: "" + gameDir = json["gameDir"]?.asString ?: "" fullscreen = json["fullscreen"]?.asBoolean ?: false noJVMArgs = json["noJVMArgs"]?.asBoolean ?: false notCheckGame = json["notCheckGame"]?.asBoolean ?: false diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/SVG.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/SVG.kt index 55d0e045c..183c08b7e 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/SVG.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/SVG.kt @@ -61,4 +61,5 @@ object SVG { fun launch(fill: String = "white", width: Double = 20.0, height: Double = 20.0) = createSVGPath("M1008 6.286q18.857 13.714 15.429 36.571l-146.286 877.714q-2.857 16.571-18.286 25.714-8 4.571-17.714 4.571-6.286 0-13.714-2.857l-258.857-105.714-138.286 168.571q-10.286 13.143-28 13.143-7.429 0-12.571-2.286-10.857-4-17.429-13.429t-6.571-20.857v-199.429l493.714-605.143-610.857 528.571-225.714-92.571q-21.143-8-22.857-31.429-1.143-22.857 18.286-33.714l950.857-548.571q8.571-5.143 18.286-5.14311.429 0 20.571 6.286z", fill, width, height) fun pencil(fill: String = "white", width: Double = 20.0, height: Double = 20.0) = createSVGPath("M20.71,4.04C21.1,3.65 21.1,3 20.71,2.63L18.37,0.29C18,-0.1 17.35,-0.1 16.96,0.29L15,2.25L18.75,6M17.75,7L14,3.25L4,13.25V17H7.75L17.75,7Z", fill, width, height) fun refresh(fill: String = "white", width: Double = 20.0, height: Double = 20.0) = createSVGPath("M17.65,6.35C16.2,4.9 14.21,4 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20C15.73,20 18.84,17.45 19.73,14H17.65C16.83,16.33 14.61,18 12,18A6,6 0 0,1 6,12A6,6 0 0,1 12,6C13.66,6 15.14,6.69 16.22,7.78L13,11H20V4L17.65,6.35Z", fill, width, height) + fun folderOpen(fill: String = "white", width: Double = 20.0, height: Double = 20.0) = createSVGPath("M19,20H4C2.89,20 2,19.1 2,18V6C2,4.89 2.89,4 4,4H10L12,6H19A2,2 0 0,1 21,8H21L4,8V18L6.14,10H23.21L20.93,18.5C20.7,19.37 19.92,20 19,20Z", fill, width, height) } \ No newline at end of file diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/VersionSettingsController.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/VersionSettingsController.kt index 1de521e5a..b1831579f 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/VersionSettingsController.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/VersionSettingsController.kt @@ -33,11 +33,13 @@ import javafx.scene.layout.VBox import javafx.stage.DirectoryChooser import javafx.stage.FileChooser import org.jackhuang.hmcl.i18n +import org.jackhuang.hmcl.setting.EnumGameDirectory import org.jackhuang.hmcl.setting.Profile import org.jackhuang.hmcl.setting.VersionSetting import org.jackhuang.hmcl.task.Scheduler import org.jackhuang.hmcl.task.task import org.jackhuang.hmcl.ui.construct.ComponentList +import org.jackhuang.hmcl.ui.construct.MultiFileItem import org.jackhuang.hmcl.ui.construct.NumberValidator import org.jackhuang.hmcl.util.JavaVersion import org.jackhuang.hmcl.util.OS @@ -55,20 +57,15 @@ class VersionSettingsController { @FXML lateinit var txtWrapper: JFXTextField @FXML lateinit var txtPrecallingCommand: JFXTextField @FXML lateinit var txtServerIP: JFXTextField - @FXML lateinit var txtJavaDir: JFXTextField @FXML lateinit var advancedSettingsPane: ComponentList @FXML lateinit var cboLauncherVisibility: JFXComboBox<*> - @FXML lateinit var cboRunDirectory: JFXComboBox<*> @FXML lateinit var chkFullscreen: JFXCheckBox @FXML lateinit var lblPhysicalMemory: Label @FXML lateinit var chkNoJVMArgs: JFXToggleButton @FXML lateinit var chkNoCommon: JFXToggleButton @FXML lateinit var chkNoGameCheck: JFXToggleButton - @FXML lateinit var componentJava: ComponentList - @FXML lateinit var javaPane: VBox - @FXML lateinit var javaPaneCustom: BorderPane - @FXML lateinit var radioCustom: JFXRadioButton - @FXML lateinit var btnJavaSelect: JFXButton + @FXML lateinit var javaItem: MultiFileItem + @FXML lateinit var gameDirItem: MultiFileItem @FXML lateinit var chkShowLogs: JFXToggleButton @FXML lateinit var btnIconSelection: JFXButton @FXML lateinit var iconView: ImageView @@ -76,9 +73,6 @@ class VersionSettingsController { lateinit var profile: Profile lateinit var versionId: String - val javaGroup = ToggleGroup() - - fun initialize() { lblPhysicalMemory.text = i18n("settings.physical_memory") + ": ${OS.TOTAL_MEMORY}MB" @@ -88,7 +82,6 @@ class VersionSettingsController { //txtJavaDir.limitWidth(limit) txtMaxMemory.limitWidth(limit) cboLauncherVisibility.limitWidth(limit) - cboRunDirectory.limitWidth(limit) val limitHeight = 10.0 chkNoJVMArgs.limitHeight(limitHeight) @@ -107,37 +100,18 @@ class VersionSettingsController { txtMetaspace.setValidators(validator(true)) txtMetaspace.setValidateWhileTextChanged() - javaPaneCustom.limitHeight(20.0) - radioCustom.toggleGroup = javaGroup - txtJavaDir.disableProperty().bind(radioCustom.selectedProperty().not()) - btnJavaSelect.disableProperty().bind(radioCustom.selectedProperty().not()) - task { - val list = mutableListOf() - list += createJavaPane(JavaVersion.fromCurrentEnvironment(), javaGroup) - JavaVersion.getJREs().values.forEach { javaVersion -> - list += createJavaPane(javaVersion, javaGroup) + it["list"] = JavaVersion.getJREs().values.map { javaVersion -> + javaItem.createChildren(javaVersion.longVersion, javaVersion.binary.absolutePath, javaVersion) } - list += javaPaneCustom - it["list"] = list }.subscribe(Scheduler.JAVAFX) { - javaPane.children.setAll(it.get>("list")) + javaItem.loadChildren(it.get>("list")) } - } - private fun createJavaPane(java: JavaVersion, group: ToggleGroup): Pane { - return BorderPane().apply { - style = "-fx-padding: 3;" - limitHeight(20.0) - left = JFXRadioButton(java.longVersion).apply { - toggleGroup = group - userData = java - } - right = Label(java.binary.absolutePath).apply { - styleClass += "subtitle-label" - style += "-fx-font-size: 10;" - } - } + gameDirItem.loadChildren(listOf( + gameDirItem.createChildren(i18n("advancedsettings.game_dir.default"), userData = EnumGameDirectory.ROOT_FOLDER), + gameDirItem.createChildren(i18n("advancedsettings.game_dir.independent"), userData = EnumGameDirectory.VERSION_FOLDER) + )) } fun loadVersionSetting(profile: Profile, versionId: String, version: VersionSetting) { @@ -162,13 +136,13 @@ class VersionSettingsController { javaDirProperty.unbind() showLogsProperty.unbind() unbindEnum(cboLauncherVisibility) - unbindEnum(cboRunDirectory) } bindInt(txtWidth, version.widthProperty) bindInt(txtHeight, version.heightProperty) bindInt(txtMaxMemory, version.maxMemoryProperty) - bindString(txtJavaDir, version.javaDirProperty) + bindString(javaItem.txtCustom, version.javaDirProperty) + bindString(gameDirItem.txtCustom, version.gameDirProperty) bindString(txtJVMArgs, version.javaArgsProperty) bindString(txtGameArgs, version.minecraftArgsProperty) bindString(txtMetaspace, version.permSizeProperty) @@ -176,7 +150,6 @@ class VersionSettingsController { bindString(txtPrecallingCommand, version.precalledCommandProperty) bindString(txtServerIP, version.serverIpProperty) bindEnum(cboLauncherVisibility, version.launcherVisibilityProperty) - bindEnum(cboRunDirectory, version.gameDirTypeProperty) bindBoolean(chkFullscreen, version.fullscreenProperty) bindBoolean(chkNoGameCheck, version.notCheckGameProperty) bindBoolean(chkNoCommon, version.noCommonProperty) @@ -184,29 +157,30 @@ class VersionSettingsController { val javaGroupKey = "java_group.listener" @Suppress("UNCHECKED_CAST") - if (javaGroup.properties.containsKey(javaGroupKey)) - javaGroup.selectedToggleProperty().removeListener(javaGroup.properties[javaGroupKey] as ChangeListener) + (javaItem.group.properties[javaGroupKey] as? ChangeListener?) + ?.run(javaItem.group.selectedToggleProperty()::removeListener) var flag = false var defaultToggle: JFXRadioButton? = null - for (toggle in javaGroup.toggles) - if (toggle is JFXRadioButton) - if (toggle.userData == version.javaVersion) { - toggle.isSelected = true - flag = true - } else if (toggle.userData == JavaVersion.fromCurrentEnvironment()) { - defaultToggle = toggle - } + var customToggle = javaItem.radioCustom + javaItem.group.toggles.filter { it is JFXRadioButton }.forEach { toggle -> + if (toggle.userData == version.javaVersion) { + toggle.isSelected = true + flag = true + } else if (toggle.userData == JavaVersion.fromCurrentEnvironment()) { + defaultToggle = toggle as JFXRadioButton + } + } val listener = ChangeListener { _, _, newValue -> - if (newValue == radioCustom) { // Custom + if (newValue == javaItem.radioCustom) { // Custom version.java = "Custom" } else { version.java = ((newValue as JFXRadioButton).userData as JavaVersion).longVersion } } - javaGroup.properties[javaGroupKey] = listener - javaGroup.selectedToggleProperty().addListener(listener) + javaItem.group.properties[javaGroupKey] = listener + javaItem.group.selectedToggleProperty().addListener(listener) if (!flag) { defaultToggle?.isSelected = true @@ -216,6 +190,30 @@ class VersionSettingsController { version.javaProperty.setChangedListener { initJavaSubtitle(version) } initJavaSubtitle(version) + val gameDirKey = "game_dir.listener" + @Suppress("UNCHECKED_CAST") + (gameDirItem.group.properties[gameDirKey] as? ChangeListener?) + ?.run(gameDirItem.group.selectedToggleProperty()::removeListener) + + gameDirItem.group.toggles.filter { it is JFXRadioButton }.forEach { toggle -> + if (toggle.userData == version.gameDirType) { + toggle.isSelected = true + flag = true + } + } + + gameDirItem.radioCustom.userData = EnumGameDirectory.CUSTOM + + val gameDirListener = ChangeListener { _, _, newValue -> + version.gameDirType = (newValue as JFXRadioButton).userData as EnumGameDirectory + } + gameDirItem.group.properties[gameDirKey] = gameDirListener + gameDirItem.group.selectedToggleProperty().addListener(gameDirListener) + + version.gameDirProperty.setChangedListener { initGameDirSubtitle(version) } + version.gameDirTypeProperty.setChangedListener { initGameDirSubtitle(version) } + initGameDirSubtitle(version) + lastVersionSetting = version loadIcon() @@ -223,7 +221,11 @@ class VersionSettingsController { private fun initJavaSubtitle(version: VersionSetting) { task { it["java"] = version.javaVersion } - .subscribe(task(Scheduler.JAVAFX) { componentJava.subtitle = it.get("java")?.binary?.absolutePath ?: "Invalid Java Directory" }) + .subscribe(task(Scheduler.JAVAFX) { javaItem.subtitle = it.get("java")?.binary?.absolutePath ?: "Invalid Java Directory" }) + } + + private fun initGameDirSubtitle(version: VersionSetting) { + gameDirItem.subtitle = profile.repository.getRunDirectory(versionId).absolutePath } fun onShowAdvanced() { @@ -233,14 +235,6 @@ class VersionSettingsController { rootPane.children.remove(advancedSettingsPane) } - fun onExploreJavaDir() { - val chooser = DirectoryChooser() - chooser.title = i18n("settings.choose_javapath") - val selectedDir = chooser.showDialog(Controllers.stage) - if (selectedDir != null) - txtJavaDir.text = selectedDir.absolutePath - } - fun onExploreIcon() { val chooser = FileChooser() chooser.extensionFilters += FileChooser.ExtensionFilter("Image", "*.png") diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/construct/ComponentList.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/construct/ComponentList.kt index 5459821f0..caa06e98b 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/construct/ComponentList.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/construct/ComponentList.kt @@ -32,9 +32,9 @@ import kotlin.collections.plusAssign import kotlin.collections.set @DefaultProperty("content") -class ComponentList: StackPane() { +open class ComponentList: StackPane() { - val vbox: VBox + val vbox = VBox() val content: ObservableList = FXCollections.observableArrayList().apply { addListener { change: ListChangeListener.Change -> @@ -47,8 +47,6 @@ class ComponentList: StackPane() { } init { - vbox = VBox().apply { - } children.setAll(vbox) styleClass += "options-list" diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/construct/MultiFileItem.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/construct/MultiFileItem.kt new file mode 100644 index 000000000..d316acde8 --- /dev/null +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/construct/MultiFileItem.kt @@ -0,0 +1,111 @@ +/* + * Hello Minecraft! Launcher. + * Copyright (C) 2017 huangyuhui + * + * 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 com.jfoenix.controls.JFXRadioButton +import com.jfoenix.controls.JFXTextField +import javafx.beans.property.SimpleStringProperty +import javafx.geometry.Pos +import javafx.scene.Node +import javafx.scene.control.Label +import javafx.scene.control.ToggleGroup +import javafx.scene.layout.BorderPane +import javafx.scene.layout.HBox +import javafx.scene.layout.VBox +import javafx.stage.DirectoryChooser +import org.jackhuang.hmcl.i18n +import org.jackhuang.hmcl.ui.Controllers +import org.jackhuang.hmcl.ui.SVG +import org.jackhuang.hmcl.ui.limitHeight +import org.jackhuang.hmcl.util.* + +class MultiFileItem : ComponentList() { + val customTextProperty = SimpleStringProperty(this, "customText", "Custom") + var customText by customTextProperty + + val chooserTitleProperty = SimpleStringProperty(this, "chooserTitle", "Select a file") + var chooserTitle by chooserTitleProperty + + val group = ToggleGroup() + val txtCustom = JFXTextField().apply { + BorderPane.setAlignment(this, Pos.CENTER_RIGHT) + } + val btnSelect = JFXButton().apply { + graphic = SVG.folderOpen("black", 15.0, 15.0) + setOnMouseClicked { + // TODO + } + } + val radioCustom = JFXRadioButton().apply { + textProperty().bind(customTextProperty) + toggleGroup = group + } + val custom = BorderPane().apply { + left = radioCustom + style = "-fx-padding: 3;" + right = HBox().apply { + spacing = 3.0 + children += txtCustom + children += btnSelect + } + limitHeight(20.0) + } + + val pane = VBox().apply { + style = "-fx-padding: 0 0 10 0;" + spacing = 8.0 + children += custom + } + + init { + addChildren(pane) + + txtCustom.disableProperty().bind(radioCustom.selectedProperty().not()) + btnSelect.disableProperty().bind(radioCustom.selectedProperty().not()) + } + + @JvmOverloads + fun createChildren(title: String, subtitle: String = "", userData: Any? = null): Node { + return BorderPane().apply { + style = "-fx-padding: 3;" + limitHeight(20.0) + left = JFXRadioButton(title).apply { + toggleGroup = group + this.userData = userData + } + right = Label(subtitle).apply { + styleClass += "subtitle-label" + style += "-fx-font-size: 10;" + } + } + } + + fun loadChildren(list: Collection) { + pane.children.setAll(list) + pane.children += custom + } + + fun onExploreJavaDir() { + val chooser = DirectoryChooser() + chooser.title = i18n(chooserTitle) + val selectedDir = chooser.showDialog(Controllers.stage) + if (selectedDir != null) + txtCustom.text = selectedDir.absolutePath + } +} \ No newline at end of file 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 1c8b27ac5..e100b8ed5 100644 --- a/HMCL/src/main/resources/assets/fxml/version/version-settings.fxml +++ b/HMCL/src/main/resources/assets/fxml/version/version-settings.fxml @@ -8,6 +8,7 @@ + @@ -37,26 +38,11 @@ - - - - - - - - - - - - - - - - - - - + + + @@ -88,22 +74,6 @@ - - - - - - - - - - - - -