mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2024-12-27 07:10:41 +08:00
Java Selection
This commit is contained in:
parent
99f60ea6e5
commit
dc468f1a76
@ -47,8 +47,8 @@ class VersionSetting() {
|
|||||||
/**
|
/**
|
||||||
* Java version or null if user customizes java directory.
|
* Java version or null if user customizes java directory.
|
||||||
*/
|
*/
|
||||||
val javaProperty = ImmediateNullableStringProperty(this, "java", null)
|
val javaProperty = ImmediateStringProperty(this, "java", "")
|
||||||
var java: String? by javaProperty
|
var java: String by javaProperty
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User customized java directory or null if user uses system Java.
|
* User customized java directory or null if user uses system Java.
|
||||||
@ -169,6 +169,27 @@ class VersionSetting() {
|
|||||||
val launcherVisibilityProperty = ImmediateObjectProperty<LauncherVisibility>(this, "launcherVisibility", LauncherVisibility.HIDE)
|
val launcherVisibilityProperty = ImmediateObjectProperty<LauncherVisibility>(this, "launcherVisibility", LauncherVisibility.HIDE)
|
||||||
var launcherVisibility: LauncherVisibility by launcherVisibilityProperty
|
var launcherVisibility: LauncherVisibility by launcherVisibilityProperty
|
||||||
|
|
||||||
|
val javaVersion: JavaVersion? get() {
|
||||||
|
// TODO: lazy initialization may result in UI suspension.
|
||||||
|
if (java.isBlank())
|
||||||
|
java = if (javaDir.isBlank()) "Default" else "Custom"
|
||||||
|
if (java == "Default") return JavaVersion.fromCurrentEnvironment()
|
||||||
|
else if (java == "Custom") {
|
||||||
|
try {
|
||||||
|
return JavaVersion.fromExecutable(File(javaDir))
|
||||||
|
} catch (e: IOException) {
|
||||||
|
return null // Custom Java Directory not found,
|
||||||
|
}
|
||||||
|
} else if (java.isNotBlank()) {
|
||||||
|
val c = JavaVersion.JAVAS[java]
|
||||||
|
if (c == null) {
|
||||||
|
java = "Default"
|
||||||
|
return JavaVersion.fromCurrentEnvironment()
|
||||||
|
} else
|
||||||
|
return c
|
||||||
|
} else throw Error()
|
||||||
|
}
|
||||||
|
|
||||||
fun addPropertyChangedListener(listener: InvalidationListener) {
|
fun addPropertyChangedListener(listener: InvalidationListener) {
|
||||||
usesGlobalProperty.addListener(listener)
|
usesGlobalProperty.addListener(listener)
|
||||||
javaProperty.addListener(listener)
|
javaProperty.addListener(listener)
|
||||||
@ -194,8 +215,7 @@ class VersionSetting() {
|
|||||||
fun toLaunchOptions(gameDir: File): LaunchOptions {
|
fun toLaunchOptions(gameDir: File): LaunchOptions {
|
||||||
return LaunchOptions(
|
return LaunchOptions(
|
||||||
gameDir = gameDir,
|
gameDir = gameDir,
|
||||||
java = if (java == null) JavaVersion.fromCurrentEnvironment()
|
java = javaVersion ?: JavaVersion.fromCurrentEnvironment(),
|
||||||
else JavaVersion.fromExecutable(File(java)),
|
|
||||||
versionName = Main.TITLE,
|
versionName = Main.TITLE,
|
||||||
profileName = Main.TITLE,
|
profileName = Main.TITLE,
|
||||||
minecraftArgs = minecraftArgs,
|
minecraftArgs = minecraftArgs,
|
||||||
@ -261,7 +281,7 @@ class VersionSetting() {
|
|||||||
javaDir = json["javaDir"]?.asString ?: ""
|
javaDir = json["javaDir"]?.asString ?: ""
|
||||||
precalledCommand = json["precalledCommand"]?.asString ?: ""
|
precalledCommand = json["precalledCommand"]?.asString ?: ""
|
||||||
serverIp = json["serverIp"]?.asString ?: ""
|
serverIp = json["serverIp"]?.asString ?: ""
|
||||||
java = json["java"]?.asString
|
java = json["java"]?.asString ?: ""
|
||||||
wrapper = json["wrapper"]?.asString ?: ""
|
wrapper = json["wrapper"]?.asString ?: ""
|
||||||
fullscreen = json["fullscreen"]?.asBoolean ?: false
|
fullscreen = json["fullscreen"]?.asBoolean ?: false
|
||||||
noJVMArgs = json["noJVMArgs"]?.asBoolean ?: false
|
noJVMArgs = json["noJVMArgs"]?.asBoolean ?: false
|
||||||
|
@ -34,10 +34,8 @@ import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
|||||||
import org.jackhuang.hmcl.i18n
|
import org.jackhuang.hmcl.i18n
|
||||||
import org.jackhuang.hmcl.setting.Settings
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
import org.jackhuang.hmcl.task.Scheduler
|
import org.jackhuang.hmcl.task.Scheduler
|
||||||
import org.jackhuang.hmcl.task.Task
|
import org.jackhuang.hmcl.task.taskResult
|
||||||
import org.jackhuang.hmcl.task.task
|
|
||||||
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
||||||
import java.util.concurrent.Callable
|
|
||||||
|
|
||||||
class AccountsPage() : StackPane(), DecoratorPage {
|
class AccountsPage() : StackPane(), DecoratorPage {
|
||||||
override val titleProperty: StringProperty = SimpleStringProperty(this, "title", "Accounts")
|
override val titleProperty: StringProperty = SimpleStringProperty(this, "title", "Accounts")
|
||||||
@ -136,7 +134,7 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
|||||||
val username = txtUsername.text
|
val username = txtUsername.text
|
||||||
val password = txtPassword.text
|
val password = txtPassword.text
|
||||||
progressBar.isVisible = true
|
progressBar.isVisible = true
|
||||||
val task = task(Callable {
|
taskResult("create_account") {
|
||||||
try {
|
try {
|
||||||
val account = when (type) {
|
val account = when (type) {
|
||||||
0 -> OfflineAccount.fromUsername(username)
|
0 -> OfflineAccount.fromUsername(username)
|
||||||
@ -149,9 +147,8 @@ class AccountsPage() : StackPane(), DecoratorPage {
|
|||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e
|
e
|
||||||
}
|
}
|
||||||
})
|
}.subscribe(Scheduler.JAVAFX) {
|
||||||
task.subscribe(Scheduler.JAVAFX) {
|
val account: Any = it["create_account"]
|
||||||
val account = task.result
|
|
||||||
if (account is Account) {
|
if (account is Account) {
|
||||||
Settings.addAccount(account)
|
Settings.addAccount(account)
|
||||||
dialog.close()
|
dialog.close()
|
||||||
|
@ -19,14 +19,11 @@ package org.jackhuang.hmcl.ui
|
|||||||
|
|
||||||
import com.jfoenix.effects.JFXDepthManager
|
import com.jfoenix.effects.JFXDepthManager
|
||||||
import javafx.fxml.FXML
|
import javafx.fxml.FXML
|
||||||
import javafx.scene.Node
|
|
||||||
import javafx.scene.control.ScrollPane
|
import javafx.scene.control.ScrollPane
|
||||||
import javafx.scene.layout.VBox
|
import javafx.scene.layout.VBox
|
||||||
import org.jackhuang.hmcl.mod.ModManager
|
import org.jackhuang.hmcl.mod.ModManager
|
||||||
import org.jackhuang.hmcl.task.Scheduler
|
import org.jackhuang.hmcl.task.Scheduler
|
||||||
import org.jackhuang.hmcl.task.Task
|
|
||||||
import org.jackhuang.hmcl.task.task
|
import org.jackhuang.hmcl.task.task
|
||||||
import java.util.concurrent.Callable
|
|
||||||
|
|
||||||
class ModController {
|
class ModController {
|
||||||
@FXML lateinit var scrollPane: ScrollPane
|
@FXML lateinit var scrollPane: ScrollPane
|
||||||
|
@ -19,23 +19,27 @@ package org.jackhuang.hmcl.ui
|
|||||||
|
|
||||||
import com.jfoenix.controls.*
|
import com.jfoenix.controls.*
|
||||||
import javafx.beans.InvalidationListener
|
import javafx.beans.InvalidationListener
|
||||||
|
import javafx.beans.binding.Bindings
|
||||||
|
import javafx.beans.value.ChangeListener
|
||||||
import javafx.fxml.FXML
|
import javafx.fxml.FXML
|
||||||
|
import javafx.geometry.Pos
|
||||||
import javafx.scene.control.Label
|
import javafx.scene.control.Label
|
||||||
import javafx.scene.control.ScrollPane
|
import javafx.scene.control.ScrollPane
|
||||||
import javafx.scene.layout.GridPane
|
import javafx.scene.control.Toggle
|
||||||
import javafx.scene.layout.VBox
|
import javafx.scene.control.ToggleGroup
|
||||||
|
import javafx.scene.layout.*
|
||||||
import javafx.stage.DirectoryChooser
|
import javafx.stage.DirectoryChooser
|
||||||
import org.jackhuang.hmcl.i18n
|
import org.jackhuang.hmcl.i18n
|
||||||
import org.jackhuang.hmcl.setting.VersionSetting
|
import org.jackhuang.hmcl.setting.VersionSetting
|
||||||
import org.jackhuang.hmcl.ui.construct.ComponentList
|
import org.jackhuang.hmcl.ui.construct.ComponentList
|
||||||
import org.jackhuang.hmcl.ui.construct.NumberValidator
|
import org.jackhuang.hmcl.ui.construct.NumberValidator
|
||||||
|
import org.jackhuang.hmcl.util.JavaVersion
|
||||||
import org.jackhuang.hmcl.util.OS
|
import org.jackhuang.hmcl.util.OS
|
||||||
|
|
||||||
class VersionSettingsController {
|
class VersionSettingsController {
|
||||||
var lastVersionSetting: VersionSetting? = null
|
var lastVersionSetting: VersionSetting? = null
|
||||||
@FXML lateinit var rootPane: VBox
|
@FXML lateinit var rootPane: VBox
|
||||||
@FXML lateinit var scroll: ScrollPane
|
@FXML lateinit var scroll: ScrollPane
|
||||||
@FXML lateinit var settingsPane: GridPane
|
|
||||||
@FXML lateinit var txtWidth: JFXTextField
|
@FXML lateinit var txtWidth: JFXTextField
|
||||||
@FXML lateinit var txtHeight: JFXTextField
|
@FXML lateinit var txtHeight: JFXTextField
|
||||||
@FXML lateinit var txtMaxMemory: JFXTextField
|
@FXML lateinit var txtMaxMemory: JFXTextField
|
||||||
@ -45,7 +49,7 @@ class VersionSettingsController {
|
|||||||
@FXML lateinit var txtWrapper: JFXTextField
|
@FXML lateinit var txtWrapper: JFXTextField
|
||||||
@FXML lateinit var txtPrecallingCommand: JFXTextField
|
@FXML lateinit var txtPrecallingCommand: JFXTextField
|
||||||
@FXML lateinit var txtServerIP: JFXTextField
|
@FXML lateinit var txtServerIP: JFXTextField
|
||||||
@FXML lateinit var txtGameDir: JFXTextField
|
@FXML lateinit var txtJavaDir: JFXTextField
|
||||||
@FXML lateinit var advancedSettingsPane: ComponentList
|
@FXML lateinit var advancedSettingsPane: ComponentList
|
||||||
@FXML lateinit var cboLauncherVisibility: JFXComboBox<*>
|
@FXML lateinit var cboLauncherVisibility: JFXComboBox<*>
|
||||||
@FXML lateinit var cboRunDirectory: JFXComboBox<*>
|
@FXML lateinit var cboRunDirectory: JFXComboBox<*>
|
||||||
@ -54,6 +58,13 @@ class VersionSettingsController {
|
|||||||
@FXML lateinit var chkNoJVMArgs: JFXToggleButton
|
@FXML lateinit var chkNoJVMArgs: JFXToggleButton
|
||||||
@FXML lateinit var chkNoCommon: JFXToggleButton
|
@FXML lateinit var chkNoCommon: JFXToggleButton
|
||||||
@FXML lateinit var chkNoGameCheck: JFXToggleButton
|
@FXML lateinit var chkNoGameCheck: JFXToggleButton
|
||||||
|
@FXML lateinit var componentJava: ComponentList
|
||||||
|
@FXML lateinit var javaPane: VBox
|
||||||
|
@FXML lateinit var javaPaneCustom: HBox
|
||||||
|
@FXML lateinit var radioCustom: JFXRadioButton
|
||||||
|
@FXML lateinit var btnJavaSelect: JFXButton
|
||||||
|
|
||||||
|
val javaGroup = ToggleGroup()
|
||||||
|
|
||||||
fun initialize() {
|
fun initialize() {
|
||||||
lblPhysicalMemory.text = i18n("settings.physical_memory") + ": ${OS.TOTAL_MEMORY}MB"
|
lblPhysicalMemory.text = i18n("settings.physical_memory") + ": ${OS.TOTAL_MEMORY}MB"
|
||||||
@ -61,7 +72,7 @@ class VersionSettingsController {
|
|||||||
scroll.smoothScrolling()
|
scroll.smoothScrolling()
|
||||||
|
|
||||||
val limit = 300.0
|
val limit = 300.0
|
||||||
txtGameDir.limitWidth(limit)
|
//txtJavaDir.limitWidth(limit)
|
||||||
txtMaxMemory.limitWidth(limit)
|
txtMaxMemory.limitWidth(limit)
|
||||||
cboLauncherVisibility.limitWidth(limit)
|
cboLauncherVisibility.limitWidth(limit)
|
||||||
cboRunDirectory.limitWidth(limit)
|
cboRunDirectory.limitWidth(limit)
|
||||||
@ -82,6 +93,32 @@ class VersionSettingsController {
|
|||||||
txtMaxMemory.textProperty().addListener(validation(txtMaxMemory))
|
txtMaxMemory.textProperty().addListener(validation(txtMaxMemory))
|
||||||
txtMetaspace.setValidators(validator(true))
|
txtMetaspace.setValidators(validator(true))
|
||||||
txtMetaspace.textProperty().addListener(validation(txtMetaspace))
|
txtMetaspace.textProperty().addListener(validation(txtMetaspace))
|
||||||
|
|
||||||
|
javaPane.children.clear()
|
||||||
|
javaPane.children += createJavaPane(JavaVersion.fromCurrentEnvironment(), javaGroup)
|
||||||
|
JavaVersion.JAVAS.values.forEach { javaVersion ->
|
||||||
|
javaPane.children += createJavaPane(javaVersion, javaGroup)
|
||||||
|
}
|
||||||
|
javaPane.children += javaPaneCustom
|
||||||
|
radioCustom.toggleGroup = javaGroup
|
||||||
|
txtJavaDir.disableProperty().bind(radioCustom.selectedProperty().not())
|
||||||
|
btnJavaSelect.disableProperty().bind(radioCustom.selectedProperty().not())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createJavaPane(java: JavaVersion, group: ToggleGroup): Pane {
|
||||||
|
return HBox().apply {
|
||||||
|
style = "-fx-padding: 3;"
|
||||||
|
spacing = 8.0
|
||||||
|
alignment = Pos.CENTER_LEFT
|
||||||
|
children += JFXRadioButton(java.longVersion).apply {
|
||||||
|
toggleGroup = group
|
||||||
|
userData = java
|
||||||
|
}
|
||||||
|
children += Label(java.binary.absolutePath).apply {
|
||||||
|
styleClass += "subtitle-label"
|
||||||
|
style += "-fx-font-size: 10;"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadVersionSetting(version: VersionSetting) {
|
fun loadVersionSetting(version: VersionSetting) {
|
||||||
@ -100,6 +137,7 @@ class VersionSettingsController {
|
|||||||
fullscreenProperty.unbind()
|
fullscreenProperty.unbind()
|
||||||
notCheckGameProperty.unbind()
|
notCheckGameProperty.unbind()
|
||||||
noCommonProperty.unbind()
|
noCommonProperty.unbind()
|
||||||
|
javaDirProperty.unbind()
|
||||||
unbindEnum(cboLauncherVisibility)
|
unbindEnum(cboLauncherVisibility)
|
||||||
unbindEnum(cboRunDirectory)
|
unbindEnum(cboRunDirectory)
|
||||||
}
|
}
|
||||||
@ -107,6 +145,7 @@ class VersionSettingsController {
|
|||||||
bindInt(txtWidth, version.widthProperty)
|
bindInt(txtWidth, version.widthProperty)
|
||||||
bindInt(txtHeight, version.heightProperty)
|
bindInt(txtHeight, version.heightProperty)
|
||||||
bindInt(txtMaxMemory, version.maxMemoryProperty)
|
bindInt(txtMaxMemory, version.maxMemoryProperty)
|
||||||
|
bindString(txtJavaDir, version.javaDirProperty)
|
||||||
bindString(txtJVMArgs, version.javaArgsProperty)
|
bindString(txtJVMArgs, version.javaArgsProperty)
|
||||||
bindString(txtGameArgs, version.minecraftArgsProperty)
|
bindString(txtGameArgs, version.minecraftArgsProperty)
|
||||||
bindString(txtMetaspace, version.permSizeProperty)
|
bindString(txtMetaspace, version.permSizeProperty)
|
||||||
@ -119,9 +158,47 @@ class VersionSettingsController {
|
|||||||
bindBoolean(chkNoGameCheck, version.notCheckGameProperty)
|
bindBoolean(chkNoGameCheck, version.notCheckGameProperty)
|
||||||
bindBoolean(chkNoCommon, version.noCommonProperty)
|
bindBoolean(chkNoCommon, version.noCommonProperty)
|
||||||
|
|
||||||
|
val javaGroupKey = "java_group.listener"
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
if (javaGroup.properties.containsKey(javaGroupKey))
|
||||||
|
javaGroup.selectedToggleProperty().removeListener(javaGroup.properties[javaGroupKey] as ChangeListener<in Toggle>)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
val listener = ChangeListener<Toggle> { _, _, newValue ->
|
||||||
|
if (newValue == radioCustom) { // Custom
|
||||||
|
version.java = "Custom"
|
||||||
|
} else {
|
||||||
|
version.java = ((newValue as JFXRadioButton).userData as JavaVersion).longVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
javaGroup.properties[javaGroupKey] = listener
|
||||||
|
javaGroup.selectedToggleProperty().addListener(listener)
|
||||||
|
|
||||||
|
if (!flag) {
|
||||||
|
defaultToggle?.isSelected = true
|
||||||
|
}
|
||||||
|
|
||||||
|
version.javaDirProperty.setChangedListener { initJavaSubtitle(version) }
|
||||||
|
version.javaProperty.setChangedListener { initJavaSubtitle(version)}
|
||||||
|
initJavaSubtitle(version)
|
||||||
|
|
||||||
lastVersionSetting = version
|
lastVersionSetting = version
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun initJavaSubtitle(version: VersionSetting) {
|
||||||
|
componentJava.subtitle = version.javaVersion?.binary?.absolutePath ?: "Invalid Java Directory"
|
||||||
|
}
|
||||||
|
|
||||||
fun onShowAdvanced() {
|
fun onShowAdvanced() {
|
||||||
if (!rootPane.children.contains(advancedSettingsPane))
|
if (!rootPane.children.contains(advancedSettingsPane))
|
||||||
rootPane.children += advancedSettingsPane
|
rootPane.children += advancedSettingsPane
|
||||||
@ -134,6 +211,6 @@ class VersionSettingsController {
|
|||||||
chooser.title = i18n("settings.choose_javapath")
|
chooser.title = i18n("settings.choose_javapath")
|
||||||
val selectedDir = chooser.showDialog(Controllers.stage)
|
val selectedDir = chooser.showDialog(Controllers.stage)
|
||||||
if (selectedDir != null)
|
if (selectedDir != null)
|
||||||
txtGameDir.text = selectedDir.absolutePath
|
txtJavaDir.text = selectedDir.absolutePath
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -20,6 +20,7 @@ package org.jackhuang.hmcl.ui.construct
|
|||||||
import javafx.beans.DefaultProperty
|
import javafx.beans.DefaultProperty
|
||||||
import javafx.beans.property.SimpleIntegerProperty
|
import javafx.beans.property.SimpleIntegerProperty
|
||||||
import javafx.beans.property.SimpleObjectProperty
|
import javafx.beans.property.SimpleObjectProperty
|
||||||
|
import javafx.beans.property.SimpleStringProperty
|
||||||
import javafx.collections.FXCollections
|
import javafx.collections.FXCollections
|
||||||
import javafx.collections.ListChangeListener
|
import javafx.collections.ListChangeListener
|
||||||
import javafx.collections.ObservableList
|
import javafx.collections.ObservableList
|
||||||
@ -52,10 +53,12 @@ class ComponentList: StackPane() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun addChildren(node: Node) {
|
fun addChildren(node: Node) {
|
||||||
if (node is ComponentList)
|
if (node is ComponentList) {
|
||||||
node.properties["title"] = node.title
|
node.properties["title"] = node.title
|
||||||
|
node.properties["subtitle"] = node.subtitle
|
||||||
|
}
|
||||||
vbox.children += StackPane().apply {
|
vbox.children += StackPane().apply {
|
||||||
children += ComponentListCell(this@ComponentList, node)
|
children += ComponentListCell(node)
|
||||||
if (vbox.children.isEmpty())
|
if (vbox.children.isEmpty())
|
||||||
styleClass += "options-list-item-ahead"
|
styleClass += "options-list-item-ahead"
|
||||||
else {
|
else {
|
||||||
@ -64,9 +67,14 @@ class ComponentList: StackPane() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val titleProperty = SimpleObjectProperty(this, "title", "Group")
|
val titleProperty = SimpleStringProperty(this, "title", "Group")
|
||||||
var title: String by titleProperty
|
var title: String by titleProperty
|
||||||
|
|
||||||
|
val subtitleProperty = SimpleStringProperty(this, "subtitle", "")
|
||||||
|
var subtitle: String by subtitleProperty
|
||||||
|
|
||||||
|
var hasSubtitle: Boolean = false
|
||||||
|
|
||||||
val depthProperty = SimpleIntegerProperty(this, "depth", 0)
|
val depthProperty = SimpleIntegerProperty(this, "depth", 0)
|
||||||
var depth: Int by depthProperty
|
var depth: Int by depthProperty
|
||||||
}
|
}
|
@ -35,7 +35,7 @@ import org.jackhuang.hmcl.ui.SVG
|
|||||||
import org.jackhuang.hmcl.ui.limitHeight
|
import org.jackhuang.hmcl.ui.limitHeight
|
||||||
import org.jackhuang.hmcl.util.*
|
import org.jackhuang.hmcl.util.*
|
||||||
|
|
||||||
class ComponentListCell(private val superList: ComponentList, private val content: Node) : StackPane() {
|
class ComponentListCell(private val content: Node) : StackPane() {
|
||||||
|
|
||||||
var expandAnimation: Animation? = null
|
var expandAnimation: Animation? = null
|
||||||
private var clipRect: Rectangle? = null
|
private var clipRect: Rectangle? = null
|
||||||
@ -67,9 +67,7 @@ class ComponentListCell(private val superList: ComponentList, private val conten
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateLayout() {
|
private fun updateLayout() {
|
||||||
val isSubList = content is ComponentList
|
if (content is ComponentList) {
|
||||||
|
|
||||||
if (isSubList) {
|
|
||||||
content.styleClass -= "options-list"
|
content.styleClass -= "options-list"
|
||||||
content.styleClass += "options-sublist"
|
content.styleClass += "options-sublist"
|
||||||
|
|
||||||
@ -82,12 +80,33 @@ class ComponentListCell(private val superList: ComponentList, private val conten
|
|||||||
expandButton.styleClass += "options-list-item-expand-button"
|
expandButton.styleClass += "options-list-item-expand-button"
|
||||||
StackPane.setAlignment(expandButton, Pos.CENTER_RIGHT)
|
StackPane.setAlignment(expandButton, Pos.CENTER_RIGHT)
|
||||||
|
|
||||||
|
val labelVBox = VBox()
|
||||||
|
Label().apply {
|
||||||
|
textProperty().bind(content.titleProperty)
|
||||||
|
isMouseTransparent = true
|
||||||
|
labelVBox.children += this
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content.hasSubtitle)
|
||||||
|
Label().apply {
|
||||||
|
textProperty().bind(content.subtitleProperty)
|
||||||
|
isMouseTransparent = true
|
||||||
|
styleClass += "subtitle-label"
|
||||||
|
labelVBox.children += this
|
||||||
|
}
|
||||||
|
|
||||||
|
StackPane.setAlignment(labelVBox, Pos.CENTER_LEFT)
|
||||||
groupNode.children.setAll(
|
groupNode.children.setAll(
|
||||||
Label(content.properties["title"]?.toString() ?: "Group").apply { isMouseTransparent = true; StackPane.setAlignment(this, Pos.CENTER_LEFT) },
|
labelVBox,
|
||||||
expandButton)
|
expandButton)
|
||||||
|
|
||||||
val container = VBox().apply {
|
val container = VBox().apply {
|
||||||
|
style += "-fx-padding: 8 0 0 0;"
|
||||||
limitHeight(0.0)
|
limitHeight(0.0)
|
||||||
|
val clipRect = Rectangle()
|
||||||
|
clipRect.widthProperty().bind(widthProperty())
|
||||||
|
clipRect.heightProperty().bind(heightProperty())
|
||||||
|
clip = clipRect
|
||||||
children.setAll(content)
|
children.setAll(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ class FileItem : BorderPane() {
|
|||||||
init {
|
init {
|
||||||
left = VBox().apply {
|
left = VBox().apply {
|
||||||
children += Label().apply { textProperty().bind(nameProperty) }
|
children += Label().apply { textProperty().bind(nameProperty) }
|
||||||
children += x.apply { style += "-fx-text-fill: gray;" }
|
children += x.apply { styleClass += "subtitle-label" }
|
||||||
}
|
}
|
||||||
|
|
||||||
right = JFXButton().apply {
|
right = JFXButton().apply {
|
||||||
|
@ -18,9 +18,7 @@
|
|||||||
package org.jackhuang.hmcl.ui.download
|
package org.jackhuang.hmcl.ui.download
|
||||||
|
|
||||||
import javafx.scene.Node
|
import javafx.scene.Node
|
||||||
import javafx.scene.layout.Pane
|
|
||||||
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider
|
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider
|
||||||
import org.jackhuang.hmcl.mod.CurseForgeModpackCompletionTask
|
|
||||||
import org.jackhuang.hmcl.mod.CurseForgeModpackInstallTask
|
import org.jackhuang.hmcl.mod.CurseForgeModpackInstallTask
|
||||||
import org.jackhuang.hmcl.mod.CurseForgeModpackManifest
|
import org.jackhuang.hmcl.mod.CurseForgeModpackManifest
|
||||||
import org.jackhuang.hmcl.setting.EnumGameDirectory
|
import org.jackhuang.hmcl.setting.EnumGameDirectory
|
||||||
|
@ -23,6 +23,10 @@
|
|||||||
-fx-text-fill: rgba(0.0, 0.0, 0.0, 0.87);
|
-fx-text-fill: rgba(0.0, 0.0, 0.0, 0.87);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.subtitle-label {
|
||||||
|
-fx-text-fill: gray;
|
||||||
|
}
|
||||||
|
|
||||||
.radio-button-title-label {
|
.radio-button-title-label {
|
||||||
-fx-font-size: 16.0px;
|
-fx-font-size: 16.0px;
|
||||||
-fx-padding: 14.0 0.0 -20.0 0.0;
|
-fx-padding: 14.0 0.0 -20.0 0.0;
|
||||||
@ -579,7 +583,11 @@
|
|||||||
|
|
||||||
.options-list {
|
.options-list {
|
||||||
-fx-background-color: transparent;
|
-fx-background-color: transparent;
|
||||||
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.26), 10, 0.12, -1, 2);
|
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.26), 5, 0.06, -0.5, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.26), 5, 0.06, -0.5, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.options-sublist {
|
.options-sublist {
|
||||||
@ -602,7 +610,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.options-list-item-expand-button {
|
.options-list-item-expand-button {
|
||||||
-fx-toggle-icon4-size: 15px;
|
-fx-toggle-icon4-size: 20px;
|
||||||
-fx-pref-width: -fx-toggle-icon4-size;
|
-fx-pref-width: -fx-toggle-icon4-size;
|
||||||
-fx-max-width: -fx-toggle-icon4-size;
|
-fx-max-width: -fx-toggle-icon4-size;
|
||||||
-fx-min-width: -fx-toggle-icon4-size;
|
-fx-min-width: -fx-toggle-icon4-size;
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<?import java.lang.*?>
|
|
||||||
<?import java.util.*?>
|
|
||||||
<?import javafx.scene.*?>
|
|
||||||
<?import javafx.scene.control.*?>
|
<?import javafx.scene.control.*?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
@ -12,9 +9,9 @@
|
|||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import com.jfoenix.controls.JFXComboBox?>
|
<?import com.jfoenix.controls.JFXComboBox?>
|
||||||
<?import javafx.collections.FXCollections?>
|
<?import javafx.collections.FXCollections?>
|
||||||
<?import com.jfoenix.controls.JFXListView?>
|
|
||||||
<?import org.jackhuang.hmcl.ui.construct.ComponentList?>
|
<?import org.jackhuang.hmcl.ui.construct.ComponentList?>
|
||||||
<?import com.jfoenix.controls.JFXToggleButton?>
|
<?import com.jfoenix.controls.JFXToggleButton?>
|
||||||
|
<?import com.jfoenix.controls.JFXRadioButton?>
|
||||||
<StackPane xmlns="http://javafx.com/javafx"
|
<StackPane xmlns="http://javafx.com/javafx"
|
||||||
xmlns:fx="http://javafx.com/fxml"
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
fx:controller="org.jackhuang.hmcl.ui.VersionSettingsController">
|
fx:controller="org.jackhuang.hmcl.ui.VersionSettingsController">
|
||||||
@ -24,19 +21,22 @@
|
|||||||
<VBox fx:id="rootPane" style="-fx-padding: 20;">
|
<VBox fx:id="rootPane" style="-fx-padding: 20;">
|
||||||
<ComponentList depth="1">
|
<ComponentList depth="1">
|
||||||
|
|
||||||
<ComponentList title="%settings.java_dir"> <!-- Java Directory -->
|
<ComponentList fx:id="componentJava" title="%settings.java_dir" hasSubtitle="true"> <!-- Java Directory -->
|
||||||
<VBox>
|
<VBox fx:id="javaPane" spacing="8">
|
||||||
<JFXTextField fx:id="txtGameDir" BorderPane.alignment="CENTER_RIGHT" />
|
<HBox fx:id="javaPaneCustom" style="-fx-padding: 3;" spacing="3" alignment="CENTER_LEFT">
|
||||||
<JFXButton GridPane.rowIndex="0" GridPane.columnIndex="2" onMouseClicked="#onExploreJavaDir">
|
<JFXRadioButton fx:id="radioCustom" text="%settings.custom" />
|
||||||
<graphic>
|
<JFXTextField fx:id="txtJavaDir" BorderPane.alignment="CENTER_RIGHT" />
|
||||||
<fx:include source="/assets/svg/folder-open.fxml" />
|
<JFXButton fx:id="btnJavaSelect" onMouseClicked="#onExploreJavaDir">
|
||||||
</graphic>
|
<graphic>
|
||||||
</JFXButton>
|
<fx:include source="/assets/svg/folder-open.fxml" />
|
||||||
|
</graphic>
|
||||||
|
</JFXButton>
|
||||||
|
</HBox>
|
||||||
</VBox>
|
</VBox>
|
||||||
</ComponentList>
|
</ComponentList>
|
||||||
|
|
||||||
<BorderPane> <!-- Max Memory -->
|
<BorderPane> <!-- Max Memory -->
|
||||||
<left><VBox><Label text="%settings.max_memory" BorderPane.alignment="CENTER_LEFT" /><Label fx:id="lblPhysicalMemory" style="-fx-text-fill: gray;" /></VBox></left>
|
<left><VBox><Label text="%settings.max_memory" BorderPane.alignment="CENTER_LEFT" /><Label fx:id="lblPhysicalMemory" styleClass="subtitle-label" /></VBox></left>
|
||||||
<right><JFXTextField fx:id="txtMaxMemory" BorderPane.alignment="CENTER_RIGHT" /></right>
|
<right><JFXTextField fx:id="txtMaxMemory" BorderPane.alignment="CENTER_RIGHT" /></right>
|
||||||
</BorderPane>
|
</BorderPane>
|
||||||
|
|
||||||
@ -73,16 +73,16 @@
|
|||||||
<BorderPane> <!-- Dimension -->
|
<BorderPane> <!-- Dimension -->
|
||||||
<left><Label text="%settings.dimension" BorderPane.alignment="CENTER_LEFT" /></left>
|
<left><Label text="%settings.dimension" BorderPane.alignment="CENTER_LEFT" /></left>
|
||||||
<right>
|
<right>
|
||||||
<BorderPane BorderPane.alignment="CENTER_RIGHT">
|
<BorderPane>
|
||||||
<left>
|
<left>
|
||||||
<HBox prefWidth="210">
|
<HBox prefWidth="210" spacing="3" alignment="CENTER" BorderPane.alignment="CENTER">
|
||||||
<JFXTextField fx:id="txtWidth" promptText="800" prefWidth="100" />
|
<JFXTextField fx:id="txtWidth" promptText="800" prefWidth="100" />
|
||||||
<Label>x</Label>
|
<Label>x</Label>
|
||||||
<JFXTextField fx:id="txtHeight" promptText="480" prefWidth="100" />
|
<JFXTextField fx:id="txtHeight" promptText="480" prefWidth="100" />
|
||||||
</HBox>
|
</HBox>
|
||||||
</left>
|
</left>
|
||||||
<right>
|
<right>
|
||||||
<JFXCheckBox fx:id="chkFullscreen" text="%settings.fullscreen" alignment="CENTER">
|
<JFXCheckBox fx:id="chkFullscreen" text="%settings.fullscreen" alignment="CENTER" BorderPane.alignment="CENTER">
|
||||||
<BorderPane.margin>
|
<BorderPane.margin>
|
||||||
<Insets right="7"/>
|
<Insets right="7"/>
|
||||||
</BorderPane.margin>
|
</BorderPane.margin>
|
||||||
@ -101,11 +101,14 @@
|
|||||||
<Tooltip text="%advancedsettings.java_args_default" />
|
<Tooltip text="%advancedsettings.java_args_default" />
|
||||||
</tooltip>
|
</tooltip>
|
||||||
</JFXTextField>
|
</JFXTextField>
|
||||||
<JFXTextField labelFloat="true" promptText="%advancedsettings.Minecraft_arguments" styleClass="fit-width" fx:id="txtGameArgs" />
|
<fx:define>
|
||||||
<JFXTextField labelFloat="true" promptText="%advancedsettings.java_permanent_generation_space" styleClass="fit-width" fx:id="txtMetaspace" />
|
<Insets fx:id="insets" bottom="8" />
|
||||||
<JFXTextField labelFloat="true" promptText="%advancedsettings.wrapper_launcher" styleClass="fit-width" fx:id="txtWrapper" />
|
</fx:define>
|
||||||
<JFXTextField labelFloat="true" promptText="%advancedsettings.precall_command" styleClass="fit-width" fx:id="txtPrecallingCommand" />
|
<JFXTextField labelFloat="true" promptText="%advancedsettings.Minecraft_arguments" styleClass="fit-width" fx:id="txtGameArgs" StackPane.margin="$insets" />
|
||||||
<JFXTextField labelFloat="true" promptText="%advancedsettings.server_ip" styleClass="fit-width" fx:id="txtServerIP" />
|
<JFXTextField labelFloat="true" promptText="%advancedsettings.java_permanent_generation_space" styleClass="fit-width" fx:id="txtMetaspace" StackPane.margin="$insets" />
|
||||||
|
<JFXTextField labelFloat="true" promptText="%advancedsettings.wrapper_launcher" styleClass="fit-width" fx:id="txtWrapper" StackPane.margin="$insets" />
|
||||||
|
<JFXTextField labelFloat="true" promptText="%advancedsettings.precall_command" styleClass="fit-width" fx:id="txtPrecallingCommand" StackPane.margin="$insets" />
|
||||||
|
<JFXTextField labelFloat="true" promptText="%advancedsettings.server_ip" styleClass="fit-width" fx:id="txtServerIP" StackPane.margin="$insets" />
|
||||||
<BorderPane><left><Label text="%advancedsettings.no_jvm_args" /></left><right><JFXToggleButton fx:id="chkNoJVMArgs" size="7" /></right></BorderPane>
|
<BorderPane><left><Label text="%advancedsettings.no_jvm_args" /></left><right><JFXToggleButton fx:id="chkNoJVMArgs" size="7" /></right></BorderPane>
|
||||||
<BorderPane><left><Label text="%advancedsettings.no_common" /></left><right><JFXToggleButton fx:id="chkNoCommon" size="7" /></right></BorderPane>
|
<BorderPane><left><Label text="%advancedsettings.no_common" /></left><right><JFXToggleButton fx:id="chkNoCommon" size="7" /></right></BorderPane>
|
||||||
<BorderPane><left><Label text="%advancedsettings.dont_check_game_completeness" /></left><right><JFXToggleButton fx:id="chkNoGameCheck" size="7" /></right></BorderPane>
|
<BorderPane><left><Label text="%advancedsettings.dont_check_game_completeness" /></left><right><JFXToggleButton fx:id="chkNoGameCheck" size="7" /></right></BorderPane>
|
||||||
|
@ -36,8 +36,9 @@ class ForgeInstallTask(private val dependencyManager: DefaultDependencyManager,
|
|||||||
private val forgeVersionList = dependencyManager.getVersionList("forge")
|
private val forgeVersionList = dependencyManager.getVersionList("forge")
|
||||||
private val installer: File = File("forge-installer.jar").absoluteFile
|
private val installer: File = File("forge-installer.jar").absoluteFile
|
||||||
lateinit var remote: RemoteVersion<*>
|
lateinit var remote: RemoteVersion<*>
|
||||||
override val dependents: MutableCollection<Task> = mutableListOf()
|
override val dependents = mutableListOf<Task>()
|
||||||
override val dependencies: MutableCollection<Task> = mutableListOf()
|
override val dependencies = mutableListOf<Task>()
|
||||||
|
override val id = ID
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (!forgeVersionList.loaded)
|
if (!forgeVersionList.loaded)
|
||||||
@ -76,5 +77,7 @@ class ForgeInstallTask(private val dependencyManager: DefaultDependencyManager,
|
|||||||
check(installer.delete(), { "Unable to delete installer file $installer" })
|
check(installer.delete(), { "Unable to delete installer file $installer" })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val ID = "forge_install_task"
|
||||||
|
}
|
||||||
}
|
}
|
@ -37,7 +37,7 @@ import java.util.logging.Level
|
|||||||
* @param resolvedVersion the <b>resolved</b> version
|
* @param resolvedVersion the <b>resolved</b> version
|
||||||
*/
|
*/
|
||||||
class GameLibrariesTask(private val dependencyManager: DefaultDependencyManager, private val resolvedVersion: Version): Task() {
|
class GameLibrariesTask(private val dependencyManager: DefaultDependencyManager, private val resolvedVersion: Version): Task() {
|
||||||
override val dependencies: MutableCollection<Task> = LinkedList()
|
override val dependencies = LinkedList<Task>()
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
for (library in resolvedVersion.libraries)
|
for (library in resolvedVersion.libraries)
|
||||||
if (library.appliesToCurrentEnvironment) {
|
if (library.appliesToCurrentEnvironment) {
|
||||||
@ -50,7 +50,7 @@ class GameLibrariesTask(private val dependencyManager: DefaultDependencyManager,
|
|||||||
}
|
}
|
||||||
|
|
||||||
class GameLoggingDownloadTask(private val dependencyManager: DefaultDependencyManager, private val version: Version) : Task() {
|
class GameLoggingDownloadTask(private val dependencyManager: DefaultDependencyManager, private val version: Version) : Task() {
|
||||||
override val dependencies: MutableCollection<Task> = LinkedList()
|
override val dependencies = LinkedList<Task>()
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
val logging = version.logging?.get(DownloadType.CLIENT) ?: return
|
val logging = version.logging?.get(DownloadType.CLIENT) ?: return
|
||||||
val file = dependencyManager.repository.getLoggingObject(version.id, version.actualAssetIndex.id, logging)
|
val file = dependencyManager.repository.getLoggingObject(version.id, version.actualAssetIndex.id, logging)
|
||||||
@ -60,7 +60,7 @@ class GameLoggingDownloadTask(private val dependencyManager: DefaultDependencyMa
|
|||||||
}
|
}
|
||||||
|
|
||||||
class GameAssetIndexDownloadTask(private val dependencyManager: DefaultDependencyManager, private val version: Version) : Task() {
|
class GameAssetIndexDownloadTask(private val dependencyManager: DefaultDependencyManager, private val version: Version) : Task() {
|
||||||
override val dependencies: MutableCollection<Task> = LinkedList()
|
override val dependencies = LinkedList<Task>()
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
val assetIndexInfo = version.actualAssetIndex
|
val assetIndexInfo = version.actualAssetIndex
|
||||||
val assetDir = dependencyManager.repository.getAssetDirectory(version.id, assetIndexInfo.id)
|
val assetDir = dependencyManager.repository.getAssetDirectory(version.id, assetIndexInfo.id)
|
||||||
@ -76,7 +76,8 @@ class GameAssetRefreshTask(private val dependencyManager: DefaultDependencyManag
|
|||||||
private val assetIndexTask = GameAssetIndexDownloadTask(dependencyManager, version)
|
private val assetIndexTask = GameAssetIndexDownloadTask(dependencyManager, version)
|
||||||
private val assetIndexInfo = version.actualAssetIndex
|
private val assetIndexInfo = version.actualAssetIndex
|
||||||
private val assetIndexFile = dependencyManager.repository.getIndexFile(version.id, assetIndexInfo.id)
|
private val assetIndexFile = dependencyManager.repository.getIndexFile(version.id, assetIndexInfo.id)
|
||||||
override val dependents: MutableCollection<Task> = LinkedList()
|
override val dependents = LinkedList<Task>()
|
||||||
|
override val id = ID
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (!assetIndexFile.exists())
|
if (!assetIndexFile.exists())
|
||||||
@ -93,12 +94,16 @@ class GameAssetRefreshTask(private val dependencyManager: DefaultDependencyManag
|
|||||||
}
|
}
|
||||||
result = res
|
result = res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val ID = "game_asset_refresh_task"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GameAssetDownloadTask(private val dependencyManager: DefaultDependencyManager, private val version: Version) : Task() {
|
class GameAssetDownloadTask(private val dependencyManager: DefaultDependencyManager, private val version: Version) : Task() {
|
||||||
private val refreshTask = GameAssetRefreshTask(dependencyManager, version)
|
private val refreshTask = GameAssetRefreshTask(dependencyManager, version)
|
||||||
override val dependents: Collection<Task> = listOf(refreshTask)
|
override val dependents = listOf(refreshTask)
|
||||||
override val dependencies: MutableCollection<Task> = LinkedList()
|
override val dependencies = LinkedList<Task>()
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
val size = refreshTask.result?.size ?: 0
|
val size = refreshTask.result?.size ?: 0
|
||||||
refreshTask.result?.forEach single@{ (file, assetObject) ->
|
refreshTask.result?.forEach single@{ (file, assetObject) ->
|
||||||
|
@ -35,8 +35,9 @@ class LiteLoaderInstallTask(private val dependencyManager: DefaultDependencyMana
|
|||||||
private val remoteVersion: String): TaskResult<Version>() {
|
private val remoteVersion: String): TaskResult<Version>() {
|
||||||
private val liteLoaderVersionList = dependencyManager.getVersionList("liteloader") as LiteLoaderVersionList
|
private val liteLoaderVersionList = dependencyManager.getVersionList("liteloader") as LiteLoaderVersionList
|
||||||
lateinit var remote: RemoteVersion<LiteLoaderRemoteVersionTag>
|
lateinit var remote: RemoteVersion<LiteLoaderRemoteVersionTag>
|
||||||
override val dependents: MutableCollection<Task> = mutableListOf()
|
override val dependents = mutableListOf<Task>()
|
||||||
override val dependencies: MutableCollection<Task> = mutableListOf()
|
override val dependencies = mutableListOf<Task>()
|
||||||
|
override val id = ID
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (!liteLoaderVersionList.loaded)
|
if (!liteLoaderVersionList.loaded)
|
||||||
@ -70,4 +71,7 @@ class LiteLoaderInstallTask(private val dependencyManager: DefaultDependencyMana
|
|||||||
dependencies += GameLibrariesTask(dependencyManager, tempVersion)
|
dependencies += GameLibrariesTask(dependencyManager, tempVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val ID = "lite_loader_install_task"
|
||||||
|
}
|
||||||
}
|
}
|
@ -32,8 +32,9 @@ class OptiFineInstallTask(private val dependencyManager: DefaultDependencyManage
|
|||||||
private val remoteVersion: String): TaskResult<Version>() {
|
private val remoteVersion: String): TaskResult<Version>() {
|
||||||
private val optiFineVersionList = dependencyManager.getVersionList("optifine")
|
private val optiFineVersionList = dependencyManager.getVersionList("optifine")
|
||||||
lateinit var remote: RemoteVersion<*>
|
lateinit var remote: RemoteVersion<*>
|
||||||
override val dependents: MutableCollection<Task> = mutableListOf()
|
override val dependents = mutableListOf<Task>()
|
||||||
override val dependencies: MutableCollection<Task> = mutableListOf()
|
override val dependencies = mutableListOf<Task>()
|
||||||
|
override val id = ID
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (!optiFineVersionList.loaded)
|
if (!optiFineVersionList.loaded)
|
||||||
@ -45,6 +46,7 @@ class OptiFineInstallTask(private val dependencyManager: DefaultDependencyManage
|
|||||||
remote = optiFineVersionList.getVersion(gameVersion, remoteVersion) ?: throw IllegalArgumentException("Remote OptiFine version $gameVersion-$remoteVersion not found")
|
remote = optiFineVersionList.getVersion(gameVersion, remoteVersion) ?: throw IllegalArgumentException("Remote OptiFine version $gameVersion-$remoteVersion not found")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
val library = Library(
|
val library = Library(
|
||||||
groupId = "net.optifine",
|
groupId = "net.optifine",
|
||||||
@ -73,4 +75,8 @@ class OptiFineInstallTask(private val dependencyManager: DefaultDependencyManage
|
|||||||
result = version.copy(libraries = merge(version.libraries, libraries), mainClass = mainClass, minecraftArguments = arg)
|
result = version.copy(libraries = merge(version.libraries, libraries), mainClass = mainClass, minecraftArguments = arg)
|
||||||
dependencies += GameLibrariesTask(dependencyManager, version.copy(libraries = libraries))
|
dependencies += GameLibrariesTask(dependencyManager, version.copy(libraries = libraries))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val ID = "optifine_install_task"
|
||||||
|
}
|
||||||
}
|
}
|
@ -68,7 +68,7 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
|
|||||||
|
|
||||||
if (OS.CURRENT_OS == OS.OSX) {
|
if (OS.CURRENT_OS == OS.OSX) {
|
||||||
res.add("-Xdock:name=Minecraft ${version.id}")
|
res.add("-Xdock:name=Minecraft ${version.id}")
|
||||||
res.add("-Xdock:icon=" + repository.getAssetObject(version.id, version.actualAssetIndex.id, "icons/minecraft.icns").absolutePath);
|
res.add("-Xdock:icon=" + repository.getAssetObject(version.id, version.actualAssetIndex.id, "icons/minecraft.icns").absolutePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
val logging = version.logging
|
val logging = version.logging
|
||||||
@ -107,8 +107,8 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
|
|||||||
if (options.minMemory != null && options.minMemory > 0)
|
if (options.minMemory != null && options.minMemory > 0)
|
||||||
res.add("-Xms${options.minMemory}m")
|
res.add("-Xms${options.minMemory}m")
|
||||||
|
|
||||||
res.add("-Dfml.ignoreInvalidMinecraftCertificates=true");
|
res.add("-Dfml.ignoreInvalidMinecraftCertificates=true")
|
||||||
res.add("-Dfml.ignorePatchDiscrepancies=true");
|
res.add("-Dfml.ignorePatchDiscrepancies=true")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Classpath
|
// Classpath
|
||||||
@ -161,9 +161,9 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
|
|||||||
|
|
||||||
// Optional Minecraft arguments
|
// Optional Minecraft arguments
|
||||||
if (options.height != null && options.width != null) {
|
if (options.height != null && options.width != null) {
|
||||||
res.add("--height");
|
res.add("--height")
|
||||||
res.add(options.height.toString())
|
res.add(options.height.toString())
|
||||||
res.add("--width");
|
res.add("--width")
|
||||||
res.add(options.width.toString())
|
res.add(options.width.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,16 +180,16 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
|
|||||||
|
|
||||||
if (options.proxyHost != null && options.proxyHost.isNotBlank() &&
|
if (options.proxyHost != null && options.proxyHost.isNotBlank() &&
|
||||||
options.proxyPort != null && options.proxyPort.isNotBlank()) {
|
options.proxyPort != null && options.proxyPort.isNotBlank()) {
|
||||||
res.add("--proxyHost");
|
res.add("--proxyHost")
|
||||||
res.add(options.proxyHost)
|
res.add(options.proxyHost)
|
||||||
res.add("--proxyPort");
|
res.add("--proxyPort")
|
||||||
res.add(options.proxyPort)
|
res.add(options.proxyPort)
|
||||||
if (options.proxyUser != null && options.proxyUser.isNotBlank() &&
|
if (options.proxyUser != null && options.proxyUser.isNotBlank() &&
|
||||||
options.proxyPass != null && options.proxyPass.isNotBlank()) {
|
options.proxyPass != null && options.proxyPass.isNotBlank()) {
|
||||||
res.add("--proxyUser");
|
res.add("--proxyUser")
|
||||||
res.add(options.proxyUser);
|
res.add(options.proxyUser)
|
||||||
res.add("--proxyPass");
|
res.add("--proxyPass")
|
||||||
res.add(options.proxyPass);
|
res.add(options.proxyPass)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,6 +246,7 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
|
|||||||
|
|
||||||
fun launchAsync(): TaskResult<JavaProcess> {
|
fun launchAsync(): TaskResult<JavaProcess> {
|
||||||
return object : TaskResult<JavaProcess>() {
|
return object : TaskResult<JavaProcess>() {
|
||||||
|
override val id = LAUNCH_ASYNC_ID
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
result = launch()
|
result = launch()
|
||||||
}
|
}
|
||||||
@ -259,11 +260,11 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
|
|||||||
throw IOException("Script file: $scriptFile cannot be created.")
|
throw IOException("Script file: $scriptFile cannot be created.")
|
||||||
scriptFile.bufferedWriter().use { writer ->
|
scriptFile.bufferedWriter().use { writer ->
|
||||||
if (isWindows) {
|
if (isWindows) {
|
||||||
writer.write("@echo off");
|
writer.write("@echo off")
|
||||||
writer.newLine()
|
writer.newLine()
|
||||||
writer.write("set APPDATA=" + options.gameDir.parent);
|
writer.write("set APPDATA=" + options.gameDir.parent)
|
||||||
writer.newLine()
|
writer.newLine()
|
||||||
writer.write("cd /D %APPDATA%");
|
writer.write("cd /D %APPDATA%")
|
||||||
writer.newLine()
|
writer.newLine()
|
||||||
}
|
}
|
||||||
if (options.precalledCommand != null && options.precalledCommand.isNotBlank()) {
|
if (options.precalledCommand != null && options.precalledCommand.isNotBlank()) {
|
||||||
@ -287,4 +288,8 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
|
|||||||
thread(name = "stderr-pump", isDaemon = isDaemon, block = StreamPump(javaProcess.process.errorStream, processListener::onErrorLog)::run)
|
thread(name = "stderr-pump", isDaemon = isDaemon, block = StreamPump(javaProcess.process.errorStream, processListener::onErrorLog)::run)
|
||||||
thread(name = "exit-waiter", isDaemon = isDaemon, block = ExitWaiter(javaProcess.process, processListener::onExit)::run)
|
thread(name = "exit-waiter", isDaemon = isDaemon, block = ExitWaiter(javaProcess.process, processListener::onExit)::run)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val LAUNCH_ASYNC_ID = "process"
|
||||||
|
}
|
||||||
}
|
}
|
@ -27,6 +27,7 @@ import java.nio.charset.Charset
|
|||||||
|
|
||||||
class GetTask @JvmOverloads constructor(val url: URL, val encoding: Charset = Charsets.UTF_8, private val retry: Int = 5, private val proxy: Proxy = Proxy.NO_PROXY): TaskResult<String>() {
|
class GetTask @JvmOverloads constructor(val url: URL, val encoding: Charset = Charsets.UTF_8, private val retry: Int = 5, private val proxy: Proxy = Proxy.NO_PROXY): TaskResult<String>() {
|
||||||
override val scheduler: Scheduler = Scheduler.IO_THREAD
|
override val scheduler: Scheduler = Scheduler.IO_THREAD
|
||||||
|
override val id = ID
|
||||||
|
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
var exception: IOException? = null
|
var exception: IOException? = null
|
||||||
@ -63,4 +64,8 @@ class GetTask @JvmOverloads constructor(val url: URL, val encoding: Charset = Ch
|
|||||||
if (exception != null)
|
if (exception != null)
|
||||||
throw exception
|
throw exception
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val ID = "http_get"
|
||||||
|
}
|
||||||
}
|
}
|
@ -17,8 +17,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.task
|
package org.jackhuang.hmcl.task
|
||||||
|
|
||||||
internal class SimpleTask @JvmOverloads constructor(private val runnable: () -> Unit, override val scheduler: Scheduler = Scheduler.DEFAULT) : Task() {
|
import org.jackhuang.hmcl.util.AutoTypingMap
|
||||||
|
|
||||||
|
internal class SimpleTask @JvmOverloads constructor(private val runnable: (AutoTypingMap<String>) -> Unit, override val scheduler: Scheduler = Scheduler.DEFAULT) : Task() {
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
runnable()
|
runnable(variables!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -49,6 +49,8 @@ abstract class Task {
|
|||||||
|
|
||||||
var title: String = this.javaClass.toString()
|
var title: String = this.javaClass.toString()
|
||||||
|
|
||||||
|
var variables: AutoTypingMap<String>? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see Thread.isInterrupted
|
* @see Thread.isInterrupted
|
||||||
* @throws InterruptedException if current thread is interrupted
|
* @throws InterruptedException if current thread is interrupted
|
||||||
@ -119,12 +121,13 @@ abstract class Task {
|
|||||||
submit(subscriber).start()
|
submit(subscriber).start()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun subscribe(scheduler: Scheduler = Scheduler.DEFAULT, closure: () -> Unit) = subscribe(task(scheduler, closure))
|
fun subscribe(scheduler: Scheduler = Scheduler.DEFAULT, closure: (AutoTypingMap<String>) -> Unit) = subscribe(task(scheduler, closure))
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return title
|
return title
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun task(scheduler: Scheduler = Scheduler.DEFAULT, closure: () -> Unit): Task = SimpleTask(closure, scheduler)
|
fun task(scheduler: Scheduler = Scheduler.DEFAULT, closure: (AutoTypingMap<String>) -> Unit): Task = SimpleTask(closure, scheduler)
|
||||||
fun <V> task(callable: Callable<V>): TaskResult<V> = TaskCallable(callable)
|
fun <V> taskResult(id: String, callable: Callable<V>): TaskResult<V> = TaskCallable(id, callable)
|
||||||
|
fun <V> taskResult(id: String, callable: (AutoTypingMap<String>) -> V): TaskResult<V> = TaskCallable2(id, callable)
|
@ -17,10 +17,17 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.task
|
package org.jackhuang.hmcl.task
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.util.AutoTypingMap
|
||||||
import java.util.concurrent.Callable
|
import java.util.concurrent.Callable
|
||||||
|
|
||||||
internal class TaskCallable<V>(private val callable: Callable<V>) : TaskResult<V>() {
|
internal class TaskCallable<V>(override val id: String, private val callable: Callable<V>) : TaskResult<V>() {
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
result = callable.call()
|
result = callable.call()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class TaskCallable2<V>(override val id: String, private val callable: (AutoTypingMap<String>) -> V) : TaskResult<V>() {
|
||||||
|
override fun execute() {
|
||||||
|
result = callable(variables!!)
|
||||||
|
}
|
||||||
}
|
}
|
@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.task
|
package org.jackhuang.hmcl.task
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.util.AutoTypingMap
|
||||||
import org.jackhuang.hmcl.util.LOG
|
import org.jackhuang.hmcl.util.LOG
|
||||||
import java.util.concurrent.*
|
import java.util.concurrent.*
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
@ -30,6 +31,7 @@ class TaskExecutor() {
|
|||||||
var canceled = false
|
var canceled = false
|
||||||
private set
|
private set
|
||||||
val totTask = AtomicInteger(0)
|
val totTask = AtomicInteger(0)
|
||||||
|
val variables = AutoTypingMap<String>(mutableMapOf())
|
||||||
private val taskQueue = ConcurrentLinkedQueue<Task>()
|
private val taskQueue = ConcurrentLinkedQueue<Task>()
|
||||||
private val workerQueue = ConcurrentLinkedQueue<Future<*>>()
|
private val workerQueue = ConcurrentLinkedQueue<Future<*>>()
|
||||||
|
|
||||||
@ -119,7 +121,10 @@ class TaskExecutor() {
|
|||||||
if (!doDependentsSucceeded && t.reliant || canceled)
|
if (!doDependentsSucceeded && t.reliant || canceled)
|
||||||
throw SilentException()
|
throw SilentException()
|
||||||
|
|
||||||
|
t.variables = variables
|
||||||
t.execute()
|
t.execute()
|
||||||
|
if (t is TaskResult<*>)
|
||||||
|
variables[t.id] = t.result
|
||||||
flag = true
|
flag = true
|
||||||
if (!t.hidden)
|
if (!t.hidden)
|
||||||
LOG.finer("Task finished: ${t.title}")
|
LOG.finer("Task finished: ${t.title}")
|
||||||
@ -142,6 +147,8 @@ class TaskExecutor() {
|
|||||||
t.onDone(TaskEvent(source = this, task = t, failed = true))
|
t.onDone(TaskEvent(source = this, task = t, failed = true))
|
||||||
taskListener?.onFailed(t)
|
taskListener?.onFailed(t)
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
t.variables = null
|
||||||
}
|
}
|
||||||
return flag
|
return flag
|
||||||
}
|
}
|
||||||
|
@ -19,4 +19,9 @@ package org.jackhuang.hmcl.task
|
|||||||
|
|
||||||
abstract class TaskResult<V> : Task() {
|
abstract class TaskResult<V> : Task() {
|
||||||
open var result: V? = null
|
open var result: V? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The task id, will be stored as key of [variables]
|
||||||
|
*/
|
||||||
|
abstract val id: String
|
||||||
}
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.util
|
||||||
|
|
||||||
|
class AutoTypingMap<K>(private val impl: MutableMap<K, Any>) {
|
||||||
|
|
||||||
|
fun clear() = impl.clear()
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
operator fun <V> get(key: K): V = impl[key] as V
|
||||||
|
operator fun set(key: K, value: Any?) {
|
||||||
|
if (value != null)
|
||||||
|
impl.set(key, value)
|
||||||
|
}
|
||||||
|
val values get() = impl.values
|
||||||
|
val keys get() = impl.keys
|
||||||
|
|
||||||
|
fun containsKey(key: K) = impl.containsKey(key)
|
||||||
|
fun remove(key: K) = impl.remove(key)
|
||||||
|
}
|
@ -21,17 +21,22 @@ import com.google.gson.annotations.SerializedName
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
import java.util.*
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
data class JavaVersion internal constructor(
|
data class JavaVersion internal constructor(
|
||||||
@SerializedName("location")
|
@SerializedName("location")
|
||||||
val binary: File,
|
val binary: File,
|
||||||
val version: Int,
|
val longVersion: String,
|
||||||
val platform: Platform) : Serializable
|
val platform: Platform) : Serializable
|
||||||
{
|
{
|
||||||
|
val version = parseVersion(longVersion)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val regex = Pattern.compile("java version \"(?<version>[1-9]*\\.[1-9]*\\.[0-9]*(.*?))\"")
|
private val regex = Pattern.compile("java version \"(?<version>[1-9]*\\.[1-9]*\\.[0-9]*(.*?))\"")
|
||||||
|
|
||||||
|
val JAVAS: Map<String, JavaVersion>
|
||||||
|
|
||||||
val UNKNOWN: Int = -1
|
val UNKNOWN: Int = -1
|
||||||
val JAVA_5: Int = 50
|
val JAVA_5: Int = 50
|
||||||
val JAVA_6: Int = 60
|
val JAVA_6: Int = 60
|
||||||
@ -54,12 +59,15 @@ data class JavaVersion internal constructor(
|
|||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun fromExecutable(file: File): JavaVersion {
|
fun fromExecutable(file: File): JavaVersion {
|
||||||
|
var actualFile = file
|
||||||
var platform = Platform.BIT_32
|
var platform = Platform.BIT_32
|
||||||
var version: String? = null
|
var version: String? = null
|
||||||
|
if (actualFile.nameWithoutExtension == "javaw") // javaw will not output version information
|
||||||
|
actualFile = actualFile.absoluteFile.parentFile.resolve("java")
|
||||||
try {
|
try {
|
||||||
val process = ProcessBuilder(file.absolutePath, "-version").start()
|
val process = ProcessBuilder(actualFile.absolutePath, "-version").start()
|
||||||
process.waitFor()
|
process.waitFor()
|
||||||
process.inputStream.bufferedReader().forEachLine { line ->
|
process.errorStream.bufferedReader().forEachLine { line ->
|
||||||
val m = regex.matcher(line)
|
val m = regex.matcher(line)
|
||||||
if (m.find())
|
if (m.find())
|
||||||
version = m.group("version")
|
version = m.group("version")
|
||||||
@ -74,10 +82,26 @@ data class JavaVersion internal constructor(
|
|||||||
val parsedVersion = parseVersion(thisVersion)
|
val parsedVersion = parseVersion(thisVersion)
|
||||||
if (parsedVersion == UNKNOWN)
|
if (parsedVersion == UNKNOWN)
|
||||||
throw IOException("Java version '$thisVersion' can not be recognized")
|
throw IOException("Java version '$thisVersion' can not be recognized")
|
||||||
return JavaVersion(file.parentFile, parsedVersion, platform)
|
return JavaVersion(file.parentFile, thisVersion, platform)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getJavaFile(home: File): File {
|
private fun fromExecutable(file: File, version: String) =
|
||||||
|
JavaVersion (
|
||||||
|
binary = file,
|
||||||
|
longVersion = version,
|
||||||
|
platform = Platform.UNKNOWN
|
||||||
|
)
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun fromJavaHome(home: File): JavaVersion {
|
||||||
|
return fromExecutable(getJavaFile(home))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fromJavaHome(home: File, version: String): JavaVersion {
|
||||||
|
return fromExecutable(getJavaFile(home), version)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getJavaFile(home: File): File {
|
||||||
val path = home.resolve("bin")
|
val path = home.resolve("bin")
|
||||||
val javaw = path.resolve("javaw.exe")
|
val javaw = path.resolve("javaw.exe")
|
||||||
if (OS.CURRENT_OS === OS.WINDOWS && javaw.isFile)
|
if (OS.CURRENT_OS === OS.WINDOWS && javaw.isFile)
|
||||||
@ -86,11 +110,80 @@ data class JavaVersion internal constructor(
|
|||||||
return path.resolve("java")
|
return path.resolve("java")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fromCurrentEnvironment(): JavaVersion {
|
private val currentJava: JavaVersion = JavaVersion(
|
||||||
return JavaVersion(
|
binary = getJavaFile(File(System.getProperty("java.home"))),
|
||||||
binary = getJavaFile(File(System.getProperty("java.home"))),
|
longVersion = System.getProperty("java.version"),
|
||||||
version = parseVersion(System.getProperty("java.version")),
|
platform = Platform.PLATFORM)
|
||||||
platform = Platform.PLATFORM)
|
fun fromCurrentEnvironment() = currentJava
|
||||||
|
|
||||||
|
init {
|
||||||
|
val temp = mutableMapOf<String, JavaVersion>()
|
||||||
|
(when (OS.CURRENT_OS) {
|
||||||
|
OS.WINDOWS -> queryWindows()
|
||||||
|
OS.OSX -> queryMacintosh()
|
||||||
|
else -> emptyList<JavaVersion>() /* Cannot detect Java in linux. */
|
||||||
|
}).forEach { javaVersion ->
|
||||||
|
temp.put(javaVersion.longVersion, javaVersion)
|
||||||
|
}
|
||||||
|
JAVAS = temp
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun queryMacintosh() = LinkedList<JavaVersion>().apply {
|
||||||
|
val currentJRE = File("/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home")
|
||||||
|
if (currentJRE.exists())
|
||||||
|
this += fromJavaHome(currentJRE)
|
||||||
|
File("/Library/Java/JavaVirtualMachines/").listFiles()?.forEach { file ->
|
||||||
|
this += fromJavaHome(file.resolve("Contents/Home"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun queryWindows() = LinkedList<JavaVersion>().apply {
|
||||||
|
ignoreException { this += queryRegisterKey("HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Runtime Environment\\") }
|
||||||
|
ignoreException { this += queryRegisterKey("HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Development Kit\\") }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun queryRegisterKey(location: String) = LinkedList<JavaVersion>().apply {
|
||||||
|
querySubFolders(location).forEach { java ->
|
||||||
|
val s = java.count { it == '.' }
|
||||||
|
if (s > 1) {
|
||||||
|
val home = queryRegisterValue(java, "JavaHome")
|
||||||
|
if (home != null)
|
||||||
|
this += fromJavaHome(File(home))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun querySubFolders(location: String) = LinkedList<String>().apply {
|
||||||
|
val cmd = arrayOf("cmd", "/c", "reg", "query", location)
|
||||||
|
val process = Runtime.getRuntime().exec(cmd)
|
||||||
|
process.waitFor()
|
||||||
|
process.inputStream.bufferedReader().readLines().forEach { s ->
|
||||||
|
if (s.startsWith(location) && s != location)
|
||||||
|
this += s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun queryRegisterValue(location: String, name: String): String? {
|
||||||
|
val cmd = arrayOf("cmd", "/c", "reg", "query", location, "/v", name)
|
||||||
|
var last = false
|
||||||
|
val process = Runtime.getRuntime().exec(cmd)
|
||||||
|
process.waitFor()
|
||||||
|
process.inputStream.bufferedReader().readLines().forEach { s ->
|
||||||
|
if (s.isNotBlank()) {
|
||||||
|
if (last && s.trim().startsWith(name)) {
|
||||||
|
var begins = s.indexOf(name)
|
||||||
|
if (begins > 0) {
|
||||||
|
val s2 = s.substring(begins + name.length)
|
||||||
|
begins = s2.indexOf("REG_SZ")
|
||||||
|
if (begins > 0)
|
||||||
|
return s2.substring(begins + "REG_SZ".length).trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (s.trim() == location)
|
||||||
|
last = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -18,6 +18,7 @@
|
|||||||
package org.jackhuang.hmcl.util.property
|
package org.jackhuang.hmcl.util.property
|
||||||
|
|
||||||
import javafx.beans.property.*
|
import javafx.beans.property.*
|
||||||
|
import javafx.beans.value.ChangeListener
|
||||||
import javafx.beans.value.ObservableValue
|
import javafx.beans.value.ObservableValue
|
||||||
|
|
||||||
open class ImmediateStringProperty(bean: Any, name: String, initialValue: String): SimpleStringProperty(bean, name, initialValue) {
|
open class ImmediateStringProperty(bean: Any, name: String, initialValue: String): SimpleStringProperty(bean, name, initialValue) {
|
||||||
@ -27,10 +28,6 @@ open class ImmediateStringProperty(bean: Any, name: String, initialValue: String
|
|||||||
super.set(newValue)
|
super.set(newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun superSet(newValue: String) {
|
|
||||||
super.set(newValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun bind(newObservable: ObservableValue<out String>) {
|
override fun bind(newObservable: ObservableValue<out String>) {
|
||||||
super.get()
|
super.get()
|
||||||
super.bind(newObservable)
|
super.bind(newObservable)
|
||||||
@ -41,30 +38,17 @@ open class ImmediateStringProperty(bean: Any, name: String, initialValue: String
|
|||||||
super.unbind()
|
super.unbind()
|
||||||
}
|
}
|
||||||
|
|
||||||
public override fun fireValueChangedEvent() {
|
private var myListener: (String) -> Unit = {}
|
||||||
super.fireValueChangedEvent()
|
private val changeListener = ChangeListener<String> { _, _, newValue ->
|
||||||
}
|
myListener(newValue)
|
||||||
}
|
|
||||||
|
|
||||||
open class ImmediateNullableStringProperty(bean: Any, name: String, initialValue: String?): SimpleStringProperty(bean, name, initialValue) {
|
|
||||||
|
|
||||||
override fun set(newValue: String?) {
|
|
||||||
super.get()
|
|
||||||
super.set(newValue)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun superSet(newValue: String?) {
|
fun setChangedListener(listener: (String) -> Unit) {
|
||||||
super.set(newValue)
|
myListener = listener
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun bind(newObservable: ObservableValue<out String?>) {
|
init {
|
||||||
super.get()
|
addListener(changeListener)
|
||||||
super.bind(newObservable)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun unbind() {
|
|
||||||
super.get()
|
|
||||||
super.unbind()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override fun fireValueChangedEvent() {
|
public override fun fireValueChangedEvent() {
|
||||||
@ -79,10 +63,6 @@ open class ImmediateBooleanProperty(bean: Any, name: String, initialValue: Boole
|
|||||||
super.set(newValue)
|
super.set(newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun superSet(newValue: Boolean) {
|
|
||||||
super.set(newValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun bind(rawObservable: ObservableValue<out Boolean>?) {
|
override fun bind(rawObservable: ObservableValue<out Boolean>?) {
|
||||||
super.get()
|
super.get()
|
||||||
super.bind(rawObservable)
|
super.bind(rawObservable)
|
||||||
@ -93,6 +73,19 @@ open class ImmediateBooleanProperty(bean: Any, name: String, initialValue: Boole
|
|||||||
super.unbind()
|
super.unbind()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var myListener: (Boolean) -> Unit = {}
|
||||||
|
private val changeListener = ChangeListener<Boolean> { _, _, newValue ->
|
||||||
|
myListener(newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setChangedListener(listener: (Boolean) -> Unit) {
|
||||||
|
myListener = listener
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
addListener(changeListener)
|
||||||
|
}
|
||||||
|
|
||||||
public override fun fireValueChangedEvent() {
|
public override fun fireValueChangedEvent() {
|
||||||
super.fireValueChangedEvent()
|
super.fireValueChangedEvent()
|
||||||
}
|
}
|
||||||
@ -105,10 +98,6 @@ open class ImmediateIntegerProperty(bean: Any, name: String, initialValue: Int):
|
|||||||
super.set(newValue)
|
super.set(newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun superSet(newValue: Int) {
|
|
||||||
super.set(newValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun bind(rawObservable: ObservableValue<out Number>) {
|
override fun bind(rawObservable: ObservableValue<out Number>) {
|
||||||
super.get()
|
super.get()
|
||||||
super.bind(rawObservable)
|
super.bind(rawObservable)
|
||||||
@ -119,6 +108,19 @@ open class ImmediateIntegerProperty(bean: Any, name: String, initialValue: Int):
|
|||||||
super.unbind()
|
super.unbind()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var myListener: (Int) -> Unit = {}
|
||||||
|
private val changeListener = ChangeListener<Number> { _, _, newValue ->
|
||||||
|
myListener(newValue.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setChangedListener(listener: (Int) -> Unit) {
|
||||||
|
myListener = listener
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
addListener(changeListener)
|
||||||
|
}
|
||||||
|
|
||||||
public override fun fireValueChangedEvent() {
|
public override fun fireValueChangedEvent() {
|
||||||
super.fireValueChangedEvent()
|
super.fireValueChangedEvent()
|
||||||
}
|
}
|
||||||
@ -131,10 +133,6 @@ open class ImmediateDoubleProperty(bean: Any, name: String, initialValue: Double
|
|||||||
super.set(newValue)
|
super.set(newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun superSet(newValue: Double) {
|
|
||||||
super.set(newValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun bind(rawObservable: ObservableValue<out Number>) {
|
override fun bind(rawObservable: ObservableValue<out Number>) {
|
||||||
super.get()
|
super.get()
|
||||||
super.bind(rawObservable)
|
super.bind(rawObservable)
|
||||||
@ -145,6 +143,19 @@ open class ImmediateDoubleProperty(bean: Any, name: String, initialValue: Double
|
|||||||
super.unbind()
|
super.unbind()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var myListener: (Double) -> Unit = {}
|
||||||
|
private val changeListener = ChangeListener<Number> { _, _, newValue ->
|
||||||
|
myListener(newValue.toDouble())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setChangedListener(listener: (Double) -> Unit) {
|
||||||
|
myListener = listener
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
addListener(changeListener)
|
||||||
|
}
|
||||||
|
|
||||||
public override fun fireValueChangedEvent() {
|
public override fun fireValueChangedEvent() {
|
||||||
super.fireValueChangedEvent()
|
super.fireValueChangedEvent()
|
||||||
}
|
}
|
||||||
@ -167,6 +178,19 @@ open class ImmediateObjectProperty<T>(bean: Any, name: String, initialValue: T):
|
|||||||
super.unbind()
|
super.unbind()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var myListener: (T) -> Unit = {}
|
||||||
|
private val changeListener = ChangeListener<T> { _, _, newValue ->
|
||||||
|
myListener(newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setChangedListener(listener: (T) -> Unit) {
|
||||||
|
myListener = listener
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
addListener(changeListener)
|
||||||
|
}
|
||||||
|
|
||||||
public override fun fireValueChangedEvent() {
|
public override fun fireValueChangedEvent() {
|
||||||
super.fireValueChangedEvent()
|
super.fireValueChangedEvent()
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user