emmmmm
This commit is contained in:
@@ -19,41 +19,44 @@ package org.jackhuang.hmcl.setting
|
|||||||
|
|
||||||
import com.google.gson.*
|
import com.google.gson.*
|
||||||
import javafx.beans.InvalidationListener
|
import javafx.beans.InvalidationListener
|
||||||
import javafx.beans.property.*
|
|
||||||
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider
|
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider
|
||||||
import org.jackhuang.hmcl.download.DefaultDependencyManager
|
import org.jackhuang.hmcl.download.DefaultDependencyManager
|
||||||
import org.jackhuang.hmcl.download.DependencyManager
|
|
||||||
import org.jackhuang.hmcl.game.HMCLGameRepository
|
import org.jackhuang.hmcl.game.HMCLGameRepository
|
||||||
|
import org.jackhuang.hmcl.mod.ModManager
|
||||||
import org.jackhuang.hmcl.util.*
|
import org.jackhuang.hmcl.util.*
|
||||||
|
import org.jackhuang.hmcl.util.property.ImmediateBooleanProperty
|
||||||
|
import org.jackhuang.hmcl.util.property.ImmediateObjectProperty
|
||||||
|
import org.jackhuang.hmcl.util.property.ImmediateStringProperty
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.lang.reflect.Type
|
import java.lang.reflect.Type
|
||||||
|
|
||||||
class Profile(var name: String = "Default", gameDir: File = File(".minecraft")) {
|
class Profile(var name: String = "Default", initialGameDir: File = File(".minecraft"), initialSelectedVersion: String = "") {
|
||||||
val globalProperty = SimpleObjectProperty<VersionSetting>(this, "global", VersionSetting())
|
val globalProperty = ImmediateObjectProperty<VersionSetting>(this, "global", VersionSetting())
|
||||||
var global: VersionSetting by globalProperty
|
var global: VersionSetting by globalProperty
|
||||||
|
|
||||||
val selectedVersionProperty = SimpleStringProperty(this, "selectedVersion", "")
|
val selectedVersionProperty = ImmediateStringProperty(this, "selectedVersion", initialSelectedVersion)
|
||||||
var selectedVersion: String by selectedVersionProperty
|
var selectedVersion: String by selectedVersionProperty
|
||||||
|
|
||||||
val gameDirProperty = SimpleObjectProperty<File>(this, "gameDir", gameDir)
|
val gameDirProperty = ImmediateObjectProperty<File>(this, "gameDir", initialGameDir)
|
||||||
var gameDir: File by gameDirProperty
|
var gameDir: File by gameDirProperty
|
||||||
|
|
||||||
val noCommonProperty = SimpleBooleanProperty(this, "noCommon", false)
|
val noCommonProperty = ImmediateBooleanProperty(this, "noCommon", false)
|
||||||
var noCommon: Boolean by noCommonProperty
|
var noCommon: Boolean by noCommonProperty
|
||||||
|
|
||||||
var repository = HMCLGameRepository(gameDir)
|
var repository = HMCLGameRepository(initialGameDir)
|
||||||
var dependency = DefaultDependencyManager(repository, BMCLAPIDownloadProvider)
|
var dependency = DefaultDependencyManager(repository, BMCLAPIDownloadProvider)
|
||||||
|
var modManager = ModManager(repository)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
gameDirProperty.addListener { _, _, newValue ->
|
gameDirProperty.addListener { _ ->
|
||||||
repository.baseDirectory = newValue
|
repository.baseDirectory = gameDir
|
||||||
repository.refreshVersions()
|
repository.refreshVersions()
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedVersionProperty.addListener { _, _, newValue ->
|
selectedVersionProperty.addListener { _ ->
|
||||||
if (newValue.isNotBlank() && !repository.hasVersion(newValue)) {
|
if (selectedVersion.isNotBlank() && !repository.hasVersion(selectedVersion)) {
|
||||||
val newVersion = repository.getVersions().firstOrNull()
|
val newVersion = repository.getVersions().firstOrNull()
|
||||||
// will cause anthor change event, we must insure that there will not be dead recursion.
|
// will cause anthor change event, we must ensure that there will not be dead recursion.
|
||||||
selectedVersion = newVersion?.id ?: ""
|
selectedVersion = newVersion?.id ?: ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,9 +113,8 @@ class Profile(var name: String = "Default", gameDir: File = File(".minecraft"))
|
|||||||
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext): Profile? {
|
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext): Profile? {
|
||||||
if (json == null || json == JsonNull.INSTANCE || json !is JsonObject) return null
|
if (json == null || json == JsonNull.INSTANCE || json !is JsonObject) return null
|
||||||
|
|
||||||
return Profile(gameDir = File(json["gameDir"]?.asString ?: "")).apply {
|
return Profile(initialGameDir = File(json["gameDir"]?.asString ?: ""), initialSelectedVersion = json["selectedVersion"]?.asString ?: "").apply {
|
||||||
global = context.deserialize(json["global"], VersionSetting::class.java)
|
global = context.deserialize(json["global"], VersionSetting::class.java)
|
||||||
selectedVersion = json["selectedVersion"]?.asString ?: ""
|
|
||||||
noCommon = json["noCommon"]?.asBoolean ?: false
|
noCommon = json["noCommon"]?.asBoolean ?: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,23 +24,20 @@ import org.jackhuang.hmcl.MainApplication
|
|||||||
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider
|
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider
|
||||||
import org.jackhuang.hmcl.download.DownloadProvider
|
import org.jackhuang.hmcl.download.DownloadProvider
|
||||||
import org.jackhuang.hmcl.download.MojangDownloadProvider
|
import org.jackhuang.hmcl.download.MojangDownloadProvider
|
||||||
import org.jackhuang.hmcl.util.GSON
|
|
||||||
import org.jackhuang.hmcl.util.LOG
|
import org.jackhuang.hmcl.util.LOG
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.logging.Level
|
import java.util.logging.Level
|
||||||
import org.jackhuang.hmcl.ProfileLoadingEvent
|
import org.jackhuang.hmcl.ProfileLoadingEvent
|
||||||
import org.jackhuang.hmcl.ProfileChangedEvent
|
import org.jackhuang.hmcl.ProfileChangedEvent
|
||||||
import org.jackhuang.hmcl.auth.Account
|
import org.jackhuang.hmcl.auth.Account
|
||||||
import org.jackhuang.hmcl.auth.Accounts
|
import org.jackhuang.hmcl.util.*
|
||||||
import org.jackhuang.hmcl.auth.OfflineAccount
|
import org.jackhuang.hmcl.auth.OfflineAccount
|
||||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
||||||
import org.jackhuang.hmcl.event.EVENT_BUS
|
import org.jackhuang.hmcl.event.EVENT_BUS
|
||||||
import org.jackhuang.hmcl.util.FileTypeAdapter
|
import org.jackhuang.hmcl.util.property.ImmediateObjectProperty
|
||||||
import org.jackhuang.hmcl.util.ignoreException
|
|
||||||
import java.net.Proxy
|
import java.net.Proxy
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
object Settings {
|
object Settings {
|
||||||
val GSON = GsonBuilder()
|
val GSON = GsonBuilder()
|
||||||
.registerTypeAdapter(VersionSetting::class.java, VersionSetting)
|
.registerTypeAdapter(VersionSetting::class.java, VersionSetting)
|
||||||
@@ -53,7 +50,7 @@ object Settings {
|
|||||||
|
|
||||||
val SETTINGS_FILE = File("hmcl.json").absoluteFile
|
val SETTINGS_FILE = File("hmcl.json").absoluteFile
|
||||||
|
|
||||||
val SETTINGS: Config
|
private val SETTINGS: Config
|
||||||
|
|
||||||
private val ACCOUNTS = mutableMapOf<String, Account>()
|
private val ACCOUNTS = mutableMapOf<String, Account>()
|
||||||
|
|
||||||
@@ -82,10 +79,10 @@ object Settings {
|
|||||||
|
|
||||||
save()
|
save()
|
||||||
|
|
||||||
if (!getProfiles().containsKey(DEFAULT_PROFILE))
|
if (!getProfileMap().containsKey(DEFAULT_PROFILE))
|
||||||
getProfiles().put(DEFAULT_PROFILE, Profile());
|
getProfileMap().put(DEFAULT_PROFILE, Profile());
|
||||||
|
|
||||||
for ((name, profile) in getProfiles().entries) {
|
for ((name, profile) in getProfileMap().entries) {
|
||||||
profile.name = name
|
profile.name = name
|
||||||
profile.addPropertyChangedListener(InvalidationListener { save() })
|
profile.addPropertyChangedListener(InvalidationListener { save() })
|
||||||
}
|
}
|
||||||
@@ -151,23 +148,35 @@ object Settings {
|
|||||||
return getProfile(SETTINGS.selectedProfile)
|
return getProfile(SETTINGS.selectedProfile)
|
||||||
}
|
}
|
||||||
|
|
||||||
val selectedAccount: Account?
|
val selectedAccountProperty = object : ImmediateObjectProperty<Account?>(this, "selectedAccount", getAccount(SETTINGS.selectedAccount)) {
|
||||||
get() {
|
override fun get(): Account? {
|
||||||
val a = getAccount(SETTINGS.selectedAccount)
|
val a = super.get()
|
||||||
if (a == null && ACCOUNTS.isNotEmpty()) {
|
if (a == null || !ACCOUNTS.containsKey(a.username)) {
|
||||||
val (key, acc) = ACCOUNTS.entries.first()
|
val acc = if (ACCOUNTS.isEmpty()) null else ACCOUNTS.values.first()
|
||||||
SETTINGS.selectedAccount = key
|
set(acc)
|
||||||
return acc
|
return acc
|
||||||
}
|
} else return a
|
||||||
return a
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSelectedAccount(name: String) {
|
override fun set(newValue: Account?) {
|
||||||
if (ACCOUNTS.containsKey(name))
|
if (newValue == null || ACCOUNTS.containsKey(newValue.username)) {
|
||||||
SETTINGS.selectedAccount = name
|
super.set(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invalidated() {
|
||||||
|
super.invalidated()
|
||||||
|
|
||||||
|
SETTINGS.selectedAccount = value?.username ?: ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
var selectedAccount: Account? by selectedAccountProperty
|
||||||
|
|
||||||
val PROXY: Proxy = Proxy.NO_PROXY
|
val PROXY: Proxy = Proxy.NO_PROXY
|
||||||
|
val PROXY_HOST: String? get() = SETTINGS.proxyHost
|
||||||
|
val PROXY_PORT: String? get() = SETTINGS.proxyPort
|
||||||
|
val PROXY_USER: String? get() = SETTINGS.proxyUserName
|
||||||
|
val PROXY_PASS: String? get() = SETTINGS.proxyPassword
|
||||||
|
|
||||||
fun addAccount(account: Account) {
|
fun addAccount(account: Account) {
|
||||||
ACCOUNTS[account.username] = account
|
ACCOUNTS[account.username] = account
|
||||||
@@ -183,36 +192,38 @@ object Settings {
|
|||||||
|
|
||||||
fun deleteAccount(name: String) {
|
fun deleteAccount(name: String) {
|
||||||
ACCOUNTS.remove(name)
|
ACCOUNTS.remove(name)
|
||||||
|
|
||||||
|
selectedAccountProperty.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getProfile(name: String?): Profile {
|
fun getProfile(name: String?): Profile {
|
||||||
var p: Profile? = getProfiles()[name ?: DEFAULT_PROFILE]
|
var p: Profile? = getProfileMap()[name ?: DEFAULT_PROFILE]
|
||||||
if (p == null)
|
if (p == null)
|
||||||
if (getProfiles().containsKey(DEFAULT_PROFILE))
|
if (getProfileMap().containsKey(DEFAULT_PROFILE))
|
||||||
p = getProfiles()[DEFAULT_PROFILE]!!
|
p = getProfileMap()[DEFAULT_PROFILE]!!
|
||||||
else {
|
else {
|
||||||
p = Profile()
|
p = Profile()
|
||||||
getProfiles().put(DEFAULT_PROFILE, p)
|
getProfileMap().put(DEFAULT_PROFILE, p)
|
||||||
}
|
}
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasProfile(name: String?): Boolean {
|
fun hasProfile(name: String?): Boolean {
|
||||||
return getProfiles().containsKey(name ?: DEFAULT_PROFILE)
|
return getProfileMap().containsKey(name ?: DEFAULT_PROFILE)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getProfiles(): MutableMap<String, Profile> {
|
fun getProfileMap(): MutableMap<String, Profile> {
|
||||||
return SETTINGS.configurations
|
return SETTINGS.configurations
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getProfilesFiltered(): Collection<Profile> {
|
fun getProfiles(): Collection<Profile> {
|
||||||
return getProfiles().values.filter { t -> t.name.isNotBlank() }
|
return getProfileMap().values.filter { t -> t.name.isNotBlank() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun putProfile(ver: Profile?): Boolean {
|
fun putProfile(ver: Profile?): Boolean {
|
||||||
if (ver == null || ver.name.isBlank() || getProfiles().containsKey(ver.name))
|
if (ver == null || ver.name.isBlank() || getProfileMap().containsKey(ver.name))
|
||||||
return false
|
return false
|
||||||
getProfiles().put(ver.name, ver)
|
getProfileMap().put(ver.name, ver)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,15 +238,15 @@ object Settings {
|
|||||||
var notify = false
|
var notify = false
|
||||||
if (selectedProfile.name == ver)
|
if (selectedProfile.name == ver)
|
||||||
notify = true
|
notify = true
|
||||||
val flag = getProfiles().remove(ver) != null
|
val flag = getProfileMap().remove(ver) != null
|
||||||
if (notify && flag)
|
if (notify && flag)
|
||||||
onProfileChanged()
|
onProfileChanged()
|
||||||
return flag
|
return flag
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun onProfileChanged() {
|
internal fun onProfileChanged() {
|
||||||
EVENT_BUS.fireEvent(ProfileChangedEvent(SETTINGS, selectedProfile))
|
|
||||||
selectedProfile.repository.refreshVersions()
|
selectedProfile.repository.refreshVersions()
|
||||||
|
EVENT_BUS.fireEvent(ProfileChangedEvent(SETTINGS, selectedProfile))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -19,10 +19,10 @@ package org.jackhuang.hmcl.setting
|
|||||||
|
|
||||||
import com.google.gson.*
|
import com.google.gson.*
|
||||||
import javafx.beans.InvalidationListener
|
import javafx.beans.InvalidationListener
|
||||||
import javafx.beans.property.*
|
|
||||||
import org.jackhuang.hmcl.MainApplication
|
import org.jackhuang.hmcl.MainApplication
|
||||||
import org.jackhuang.hmcl.game.LaunchOptions
|
import org.jackhuang.hmcl.game.LaunchOptions
|
||||||
import org.jackhuang.hmcl.util.*
|
import org.jackhuang.hmcl.util.*
|
||||||
|
import org.jackhuang.hmcl.util.property.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.lang.reflect.Type
|
import java.lang.reflect.Type
|
||||||
@@ -39,7 +39,7 @@ class VersionSetting() {
|
|||||||
*
|
*
|
||||||
* Defaults false because if one version uses global first, custom version file will not be generated.
|
* Defaults false because if one version uses global first, custom version file will not be generated.
|
||||||
*/
|
*/
|
||||||
val usesGlobalProperty = SimpleBooleanProperty(this, "usesGlobal", false)
|
val usesGlobalProperty = ImmediateBooleanProperty(this, "usesGlobal", false)
|
||||||
var usesGlobal: Boolean by usesGlobalProperty
|
var usesGlobal: Boolean by usesGlobalProperty
|
||||||
|
|
||||||
// java
|
// java
|
||||||
@@ -47,39 +47,39 @@ class VersionSetting() {
|
|||||||
/**
|
/**
|
||||||
* Java version or null if user customizes java directory.
|
* Java version or null if user customizes java directory.
|
||||||
*/
|
*/
|
||||||
val javaProperty = SimpleStringProperty(this, "java", null)
|
val javaProperty = ImmediateNullableStringProperty(this, "java", null)
|
||||||
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.
|
||||||
*/
|
*/
|
||||||
val javaDirProperty = SimpleStringProperty(this, "javaDir", "")
|
val javaDirProperty = ImmediateStringProperty(this, "javaDir", "")
|
||||||
var javaDir: String by javaDirProperty
|
var javaDir: String by javaDirProperty
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The command to launch java, i.e. optirun.
|
* The command to launch java, i.e. optirun.
|
||||||
*/
|
*/
|
||||||
val wrapperProperty = SimpleStringProperty(this, "wrapper", "")
|
val wrapperProperty = ImmediateStringProperty(this, "wrapper", "")
|
||||||
var wrapper: String by wrapperProperty
|
var wrapper: String by wrapperProperty
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The permanent generation size of JVM garbage collection.
|
* The permanent generation size of JVM garbage collection.
|
||||||
*/
|
*/
|
||||||
val permSizeProperty = SimpleStringProperty(this, "permSize", "")
|
val permSizeProperty = ImmediateStringProperty(this, "permSize", "")
|
||||||
var permSize: String by permSizeProperty
|
var permSize: String by permSizeProperty
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum memory that JVM can allocate.
|
* The maximum memory that JVM can allocate.
|
||||||
* The size of JVM heap.
|
* The size of JVM heap.
|
||||||
*/
|
*/
|
||||||
val maxMemoryProperty = SimpleIntegerProperty(this, "maxMemory", OS.SUGGESTED_MEMORY)
|
val maxMemoryProperty = ImmediateIntegerProperty(this, "maxMemory", OS.SUGGESTED_MEMORY)
|
||||||
var maxMemory: Int by maxMemoryProperty
|
var maxMemory: Int by maxMemoryProperty
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The command that will be executed before launching the Minecraft.
|
* The command that will be executed before launching the Minecraft.
|
||||||
* Operating system relevant.
|
* Operating system relevant.
|
||||||
*/
|
*/
|
||||||
val precalledCommandProperty = SimpleStringProperty(this, "precalledCommand", "")
|
val precalledCommandProperty = ImmediateStringProperty(this, "precalledCommand", "")
|
||||||
var precalledCommand: String by precalledCommandProperty
|
var precalledCommand: String by precalledCommandProperty
|
||||||
|
|
||||||
// options
|
// options
|
||||||
@@ -87,25 +87,25 @@ class VersionSetting() {
|
|||||||
/**
|
/**
|
||||||
* The user customized arguments passed to JVM.
|
* The user customized arguments passed to JVM.
|
||||||
*/
|
*/
|
||||||
val javaArgsProperty = SimpleStringProperty(this, "javaArgs", "")
|
val javaArgsProperty = ImmediateStringProperty(this, "javaArgs", "")
|
||||||
var javaArgs: String by javaArgsProperty
|
var javaArgs: String by javaArgsProperty
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The user customized arguments passed to Minecraft.
|
* The user customized arguments passed to Minecraft.
|
||||||
*/
|
*/
|
||||||
val minecraftArgsProperty = SimpleStringProperty(this, "minecraftArgs", "")
|
val minecraftArgsProperty = ImmediateStringProperty(this, "minecraftArgs", "")
|
||||||
var minecraftArgs: String by minecraftArgsProperty
|
var minecraftArgs: String by minecraftArgsProperty
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if disallow HMCL use default JVM arguments.
|
* True if disallow HMCL use default JVM arguments.
|
||||||
*/
|
*/
|
||||||
val noJVMArgsProperty = SimpleBooleanProperty(this, "noJVMArgs", false)
|
val noJVMArgsProperty = ImmediateBooleanProperty(this, "noJVMArgs", false)
|
||||||
var noJVMArgs: Boolean by noJVMArgsProperty
|
var noJVMArgs: Boolean by noJVMArgsProperty
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if HMCL does not check game's completeness.
|
* True if HMCL does not check game's completeness.
|
||||||
*/
|
*/
|
||||||
val notCheckGameProperty = SimpleBooleanProperty(this, "notCheckGame", false)
|
val notCheckGameProperty = ImmediateBooleanProperty(this, "notCheckGame", false)
|
||||||
var notCheckGame: Boolean by notCheckGameProperty
|
var notCheckGame: Boolean by notCheckGameProperty
|
||||||
|
|
||||||
// Minecraft settings.
|
// Minecraft settings.
|
||||||
@@ -115,13 +115,13 @@ class VersionSetting() {
|
|||||||
*
|
*
|
||||||
* Format: ip:port or without port.
|
* Format: ip:port or without port.
|
||||||
*/
|
*/
|
||||||
val serverIpProperty = SimpleStringProperty(this, "serverIp", "")
|
val serverIpProperty = ImmediateStringProperty(this, "serverIp", "")
|
||||||
var serverIp: String by serverIpProperty
|
var serverIp: String by serverIpProperty
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if Minecraft started in fullscreen mode.
|
* True if Minecraft started in fullscreen mode.
|
||||||
*/
|
*/
|
||||||
val fullscreenProperty = SimpleBooleanProperty(this, "fullscreen", false)
|
val fullscreenProperty = ImmediateBooleanProperty(this, "fullscreen", false)
|
||||||
var fullscreen: Boolean by fullscreenProperty
|
var fullscreen: Boolean by fullscreenProperty
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -131,7 +131,7 @@ class VersionSetting() {
|
|||||||
* String type prevents unexpected value from causing JsonSyntaxException.
|
* String type prevents unexpected value from causing JsonSyntaxException.
|
||||||
* We can only reset this field instead of recreating the whole setting file.
|
* We can only reset this field instead of recreating the whole setting file.
|
||||||
*/
|
*/
|
||||||
val widthProperty = SimpleIntegerProperty(this, "width", 854)
|
val widthProperty = ImmediateIntegerProperty(this, "width", 854)
|
||||||
var width: Int by widthProperty
|
var width: Int by widthProperty
|
||||||
|
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ class VersionSetting() {
|
|||||||
* String type prevents unexpected value from causing JsonSyntaxException.
|
* String type prevents unexpected value from causing JsonSyntaxException.
|
||||||
* We can only reset this field instead of recreating the whole setting file.
|
* We can only reset this field instead of recreating the whole setting file.
|
||||||
*/
|
*/
|
||||||
val heightProperty = SimpleIntegerProperty(this, "height", 480)
|
val heightProperty = ImmediateIntegerProperty(this, "height", 480)
|
||||||
var height: Int by heightProperty
|
var height: Int by heightProperty
|
||||||
|
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ class VersionSetting() {
|
|||||||
* 0 - .minecraft<br/>
|
* 0 - .minecraft<br/>
|
||||||
* 1 - .minecraft/versions/<version>/<br/>
|
* 1 - .minecraft/versions/<version>/<br/>
|
||||||
*/
|
*/
|
||||||
val gameDirTypeProperty = SimpleObjectProperty<EnumGameDirectory>(this, "gameDirTypeProperty", EnumGameDirectory.ROOT_FOLDER)
|
val gameDirTypeProperty = ImmediateObjectProperty<EnumGameDirectory>(this, "gameDirTypeProperty", EnumGameDirectory.ROOT_FOLDER)
|
||||||
var gameDirType: EnumGameDirectory by gameDirTypeProperty
|
var gameDirType: EnumGameDirectory by gameDirTypeProperty
|
||||||
|
|
||||||
// launcher settings
|
// launcher settings
|
||||||
@@ -160,7 +160,7 @@ class VersionSetting() {
|
|||||||
* 1 - Hide the launcher when the game starts.<br/>
|
* 1 - Hide the launcher when the game starts.<br/>
|
||||||
* 2 - Keep the launcher open.<br/>
|
* 2 - Keep the launcher open.<br/>
|
||||||
*/
|
*/
|
||||||
val launcherVisibilityProperty = SimpleObjectProperty<LauncherVisibility>(this, "launcherVisibility", LauncherVisibility.HIDE)
|
val launcherVisibilityProperty = ImmediateObjectProperty<LauncherVisibility>(this, "launcherVisibility", LauncherVisibility.HIDE)
|
||||||
var launcherVisibility: LauncherVisibility by launcherVisibilityProperty
|
var launcherVisibility: LauncherVisibility by launcherVisibilityProperty
|
||||||
|
|
||||||
fun addPropertyChangedListener(listener: InvalidationListener) {
|
fun addPropertyChangedListener(listener: InvalidationListener) {
|
||||||
@@ -200,10 +200,10 @@ class VersionSetting() {
|
|||||||
fullscreen = fullscreen,
|
fullscreen = fullscreen,
|
||||||
serverIp = serverIp,
|
serverIp = serverIp,
|
||||||
wrapper = wrapper,
|
wrapper = wrapper,
|
||||||
proxyHost = Settings.SETTINGS.proxyHost,
|
proxyHost = Settings.PROXY_HOST,
|
||||||
proxyPort = Settings.SETTINGS.proxyPort,
|
proxyPort = Settings.PROXY_PORT,
|
||||||
proxyUser = Settings.SETTINGS.proxyUserName,
|
proxyUser = Settings.PROXY_USER,
|
||||||
proxyPass = Settings.SETTINGS.proxyPassword,
|
proxyPass = Settings.PROXY_PASS,
|
||||||
precalledCommand = precalledCommand,
|
precalledCommand = precalledCommand,
|
||||||
noGeneratedJVMArgs = noJVMArgs
|
noGeneratedJVMArgs = noJVMArgs
|
||||||
)
|
)
|
||||||
@@ -217,7 +217,7 @@ class VersionSetting() {
|
|||||||
addProperty("usesGlobal", src.usesGlobal)
|
addProperty("usesGlobal", src.usesGlobal)
|
||||||
addProperty("javaArgs", src.javaArgs)
|
addProperty("javaArgs", src.javaArgs)
|
||||||
addProperty("minecraftArgs", src.minecraftArgs)
|
addProperty("minecraftArgs", src.minecraftArgs)
|
||||||
addProperty("maxMemory", src.maxMemory)
|
addProperty("maxMemory", if (src.maxMemory <= 0) OS.SUGGESTED_MEMORY else src.maxMemory)
|
||||||
addProperty("permSize", src.permSize)
|
addProperty("permSize", src.permSize)
|
||||||
addProperty("width", src.width)
|
addProperty("width", src.width)
|
||||||
addProperty("height", src.height)
|
addProperty("height", src.height)
|
||||||
@@ -239,11 +239,14 @@ class VersionSetting() {
|
|||||||
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): VersionSetting? {
|
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): VersionSetting? {
|
||||||
if (json == null || json == JsonNull.INSTANCE || json !is JsonObject) return null
|
if (json == null || json == JsonNull.INSTANCE || json !is JsonObject) return null
|
||||||
|
|
||||||
|
var maxMemoryN = parseJsonPrimitive(json["maxMemory"]?.asJsonPrimitive, OS.SUGGESTED_MEMORY)
|
||||||
|
if (maxMemoryN <= 0) maxMemoryN = OS.SUGGESTED_MEMORY
|
||||||
|
|
||||||
return VersionSetting().apply {
|
return VersionSetting().apply {
|
||||||
usesGlobal = json["usesGlobal"]?.asBoolean ?: false
|
usesGlobal = json["usesGlobal"]?.asBoolean ?: false
|
||||||
javaArgs = json["javaArgs"]?.asString ?: ""
|
javaArgs = json["javaArgs"]?.asString ?: ""
|
||||||
minecraftArgs = json["minecraftArgs"]?.asString ?: ""
|
minecraftArgs = json["minecraftArgs"]?.asString ?: ""
|
||||||
maxMemory = parseJsonPrimitive(json["maxMemory"]?.asJsonPrimitive)
|
maxMemory = maxMemoryN
|
||||||
permSize = json["permSize"]?.asString ?: ""
|
permSize = json["permSize"]?.asString ?: ""
|
||||||
width = parseJsonPrimitive(json["width"]?.asJsonPrimitive)
|
width = parseJsonPrimitive(json["width"]?.asJsonPrimitive)
|
||||||
height = parseJsonPrimitive(json["height"]?.asJsonPrimitive)
|
height = parseJsonPrimitive(json["height"]?.asJsonPrimitive)
|
||||||
|
|||||||
@@ -18,38 +18,36 @@
|
|||||||
package org.jackhuang.hmcl.ui
|
package org.jackhuang.hmcl.ui
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXButton
|
import com.jfoenix.controls.JFXButton
|
||||||
|
import com.jfoenix.controls.JFXCheckBox
|
||||||
|
import com.jfoenix.controls.JFXRadioButton
|
||||||
import com.jfoenix.effects.JFXDepthManager
|
import com.jfoenix.effects.JFXDepthManager
|
||||||
import javafx.beans.binding.Bindings
|
import javafx.beans.binding.Bindings
|
||||||
import javafx.fxml.FXML
|
import javafx.fxml.FXML
|
||||||
import javafx.scene.control.Label
|
import javafx.scene.control.Label
|
||||||
|
import javafx.scene.control.ToggleGroup
|
||||||
import javafx.scene.layout.Pane
|
import javafx.scene.layout.Pane
|
||||||
import javafx.scene.layout.StackPane
|
import javafx.scene.layout.StackPane
|
||||||
import javafx.scene.layout.VBox
|
import javafx.scene.layout.VBox
|
||||||
import java.util.concurrent.Callable
|
import java.util.concurrent.Callable
|
||||||
|
|
||||||
class AccountItem(i: Int, width: Double, height: Double) : StackPane() {
|
class AccountItem(i: Int, width: Double, height: Double, group: ToggleGroup) : StackPane() {
|
||||||
@FXML lateinit var icon: Pane
|
@FXML lateinit var icon: Pane
|
||||||
@FXML lateinit var content: VBox
|
@FXML lateinit var content: VBox
|
||||||
@FXML lateinit var header: StackPane
|
@FXML lateinit var header: StackPane
|
||||||
@FXML lateinit var body: StackPane
|
@FXML lateinit var body: StackPane
|
||||||
@FXML lateinit var btnDelete: JFXButton
|
@FXML lateinit var btnDelete: JFXButton
|
||||||
@FXML lateinit var btnEdit: JFXButton
|
|
||||||
@FXML lateinit var lblUser: Label
|
@FXML lateinit var lblUser: Label
|
||||||
|
@FXML lateinit var chkSelected: JFXRadioButton
|
||||||
@FXML lateinit var lblType: Label
|
@FXML lateinit var lblType: Label
|
||||||
|
|
||||||
init {
|
init {
|
||||||
loadFXML("/assets/fxml/account-item.fxml")
|
loadFXML("/assets/fxml/account-item.fxml")
|
||||||
|
|
||||||
minWidth = width
|
|
||||||
maxWidth = width
|
|
||||||
prefWidth = width
|
|
||||||
|
|
||||||
minHeight = height
|
|
||||||
maxHeight = height
|
|
||||||
prefHeight = height
|
|
||||||
|
|
||||||
JFXDepthManager.setDepth(this, 1)
|
JFXDepthManager.setDepth(this, 1)
|
||||||
|
|
||||||
|
chkSelected.toggleGroup = group
|
||||||
|
btnDelete.graphic = SVG.delete("white", 15.0, 15.0)
|
||||||
|
|
||||||
// create content
|
// create content
|
||||||
val headerColor = getDefaultColor(i % 12)
|
val headerColor = getDefaultColor(i % 12)
|
||||||
header.style = "-fx-background-radius: 5 5 0 0; -fx-background-color: " + headerColor
|
header.style = "-fx-background-radius: 5 5 0 0; -fx-background-color: " + headerColor
|
||||||
|
|||||||
@@ -25,18 +25,19 @@ import javafx.scene.control.ScrollPane
|
|||||||
import javafx.scene.layout.StackPane
|
import javafx.scene.layout.StackPane
|
||||||
import javafx.beans.property.SimpleStringProperty
|
import javafx.beans.property.SimpleStringProperty
|
||||||
import javafx.beans.property.StringProperty
|
import javafx.beans.property.StringProperty
|
||||||
|
import javafx.beans.value.ChangeListener
|
||||||
import javafx.scene.control.Label
|
import javafx.scene.control.Label
|
||||||
|
import javafx.scene.control.ToggleGroup
|
||||||
import org.jackhuang.hmcl.auth.Account
|
import org.jackhuang.hmcl.auth.Account
|
||||||
import org.jackhuang.hmcl.auth.OfflineAccount
|
import org.jackhuang.hmcl.auth.OfflineAccount
|
||||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
||||||
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.with
|
|
||||||
import org.jackhuang.hmcl.task.Task
|
import org.jackhuang.hmcl.task.Task
|
||||||
import org.jackhuang.hmcl.ui.wizard.HasTitle
|
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
||||||
import java.util.concurrent.Callable
|
import java.util.concurrent.Callable
|
||||||
|
|
||||||
class AccountsPage : StackPane(), HasTitle {
|
class AccountsPage() : StackPane(), DecoratorPage {
|
||||||
override val titleProperty: StringProperty = SimpleStringProperty(this, "title", "Accounts")
|
override val titleProperty: StringProperty = SimpleStringProperty(this, "title", "Accounts")
|
||||||
|
|
||||||
@FXML lateinit var scrollPane: ScrollPane
|
@FXML lateinit var scrollPane: ScrollPane
|
||||||
@@ -48,12 +49,30 @@ class AccountsPage : StackPane(), HasTitle {
|
|||||||
@FXML lateinit var lblCreationWarning: Label
|
@FXML lateinit var lblCreationWarning: Label
|
||||||
@FXML lateinit var cboType: JFXComboBox<String>
|
@FXML lateinit var cboType: JFXComboBox<String>
|
||||||
|
|
||||||
|
val listener = ChangeListener<Account?> { _, _, newValue ->
|
||||||
|
masonryPane.children.forEach {
|
||||||
|
if (it is AccountItem) {
|
||||||
|
it.chkSelected.isSelected = newValue?.username == it.lblUser.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
loadFXML("/assets/fxml/account.fxml")
|
loadFXML("/assets/fxml/account.fxml")
|
||||||
children.remove(dialog)
|
children.remove(dialog)
|
||||||
dialog.dialogContainer = this
|
dialog.dialogContainer = this
|
||||||
|
|
||||||
JFXScrollPane.smoothScrolling(scrollPane)
|
scrollPane.smoothScrolling()
|
||||||
|
|
||||||
|
txtUsername.textProperty().addListener { _ ->
|
||||||
|
txtUsername.validate()
|
||||||
|
}
|
||||||
|
txtUsername.validate()
|
||||||
|
|
||||||
|
txtPassword.textProperty().addListener { _ ->
|
||||||
|
txtPassword.validate()
|
||||||
|
}
|
||||||
|
txtPassword.validate()
|
||||||
|
|
||||||
cboType.selectionModel.selectedIndexProperty().addListener { _, _, newValue ->
|
cboType.selectionModel.selectedIndexProperty().addListener { _, _, newValue ->
|
||||||
val visible = newValue != 0
|
val visible = newValue != 0
|
||||||
@@ -62,27 +81,42 @@ class AccountsPage : StackPane(), HasTitle {
|
|||||||
}
|
}
|
||||||
cboType.selectionModel.select(0)
|
cboType.selectionModel.select(0)
|
||||||
|
|
||||||
|
Settings.selectedAccountProperty.addListener(listener)
|
||||||
|
|
||||||
loadAccounts()
|
loadAccounts()
|
||||||
|
|
||||||
|
if (Settings.getAccounts().isEmpty())
|
||||||
|
addNewAccount()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClose() {
|
||||||
|
Settings.selectedAccountProperty.removeListener(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadAccounts() {
|
fun loadAccounts() {
|
||||||
val children = mutableListOf<Node>()
|
val children = mutableListOf<Node>()
|
||||||
var i = 0
|
var i = 0
|
||||||
|
val group = ToggleGroup()
|
||||||
for ((_, account) in Settings.getAccounts()) {
|
for ((_, account) in Settings.getAccounts()) {
|
||||||
children += buildNode(++i, account)
|
children += buildNode(++i, account, group)
|
||||||
|
}
|
||||||
|
group.selectedToggleProperty().addListener { _, _, newValue ->
|
||||||
|
if (newValue != null)
|
||||||
|
Settings.selectedAccount = newValue.properties["account"] as Account
|
||||||
}
|
}
|
||||||
masonryPane.children.setAll(children)
|
masonryPane.children.setAll(children)
|
||||||
Platform.runLater { scrollPane.requestLayout() }
|
Platform.runLater {
|
||||||
|
masonryPane.requestLayout()
|
||||||
|
scrollPane.requestLayout()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildNode(i: Int, account: Account): Node {
|
private fun buildNode(i: Int, account: Account, group: ToggleGroup): Node {
|
||||||
return AccountItem(i, Math.random() * 100 + 100, Math.random() * 100 + 100).apply {
|
return AccountItem(i, Math.random() * 100 + 100, Math.random() * 100 + 100, group).apply {
|
||||||
|
chkSelected.properties["account"] = account
|
||||||
|
chkSelected.isSelected = Settings.selectedAccount == account
|
||||||
lblUser.text = account.username
|
lblUser.text = account.username
|
||||||
lblType.text = when(account) {
|
lblType.text = accountType(account)
|
||||||
is OfflineAccount -> "Offline Account"
|
|
||||||
is YggdrasilAccount -> "Yggdrasil Account"
|
|
||||||
else -> throw Error("Unsupported account: $account")
|
|
||||||
}
|
|
||||||
btnDelete.setOnMouseClicked {
|
btnDelete.setOnMouseClicked {
|
||||||
Settings.deleteAccount(account.username)
|
Settings.deleteAccount(account.username)
|
||||||
Platform.runLater(this@AccountsPage::loadAccounts)
|
Platform.runLater(this@AccountsPage::loadAccounts)
|
||||||
@@ -90,28 +124,6 @@ class AccountsPage : StackPane(), HasTitle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getDefaultColor(i: Int): String {
|
|
||||||
var color = "#FFFFFF"
|
|
||||||
when (i) {
|
|
||||||
0 -> color = "#8F3F7E"
|
|
||||||
1 -> color = "#B5305F"
|
|
||||||
2 -> color = "#CE584A"
|
|
||||||
3 -> color = "#DB8D5C"
|
|
||||||
4 -> color = "#DA854E"
|
|
||||||
5 -> color = "#E9AB44"
|
|
||||||
6 -> color = "#FEE435"
|
|
||||||
7 -> color = "#99C286"
|
|
||||||
8 -> color = "#01A05E"
|
|
||||||
9 -> color = "#4A8895"
|
|
||||||
10 -> color = "#16669B"
|
|
||||||
11 -> color = "#2F65A5"
|
|
||||||
12 -> color = "#4E6A9C"
|
|
||||||
else -> {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return color
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addNewAccount() {
|
fun addNewAccount() {
|
||||||
dialog.show()
|
dialog.show()
|
||||||
}
|
}
|
||||||
@@ -149,4 +161,11 @@ class AccountsPage : StackPane(), HasTitle {
|
|||||||
fun onCreationCancel() {
|
fun onCreationCancel() {
|
||||||
dialog.close()
|
dialog.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun accountType(account: Account) =
|
||||||
|
when(account) {
|
||||||
|
is OfflineAccount -> "Offline Account"
|
||||||
|
is YggdrasilAccount -> "Yggdrasil Account"
|
||||||
|
else -> throw Error("Unsupported account: $account")
|
||||||
|
}
|
||||||
@@ -38,16 +38,10 @@ object Controllers {
|
|||||||
fun initialize(stage: Stage) {
|
fun initialize(stage: Stage) {
|
||||||
this.stage = stage
|
this.stage = stage
|
||||||
|
|
||||||
decorator = Decorator(stage, max = false)
|
decorator = Decorator(stage, mainPane, max = false)
|
||||||
decorator.mainPage = mainPane
|
|
||||||
decorator.showPage(null)
|
decorator.showPage(null)
|
||||||
leftPaneController = LeftPaneController(decorator.leftPane)
|
leftPaneController = LeftPaneController(decorator.leftPane)
|
||||||
|
|
||||||
// Let root pane fix window size.
|
|
||||||
with(mainPane.parent as StackPane) {
|
|
||||||
mainPane.prefWidthProperty().bind(widthProperty())
|
|
||||||
mainPane.prefHeightProperty().bind(heightProperty())
|
|
||||||
}
|
|
||||||
decorator.isCustomMaximize = false
|
decorator.isCustomMaximize = false
|
||||||
|
|
||||||
scene = Scene(decorator, 800.0, 480.0)
|
scene = Scene(decorator, 800.0, 480.0)
|
||||||
|
|||||||
@@ -37,12 +37,9 @@ import javafx.scene.control.Tooltip
|
|||||||
import javafx.scene.input.MouseEvent
|
import javafx.scene.input.MouseEvent
|
||||||
import javafx.scene.layout.*
|
import javafx.scene.layout.*
|
||||||
import javafx.scene.paint.Color
|
import javafx.scene.paint.Color
|
||||||
import javafx.scene.shape.Rectangle
|
|
||||||
import javafx.stage.Screen
|
import javafx.stage.Screen
|
||||||
import javafx.stage.Stage
|
import javafx.stage.Stage
|
||||||
import javafx.stage.StageStyle
|
import javafx.stage.StageStyle
|
||||||
import javafx.scene.layout.BorderStrokeStyle
|
|
||||||
import javafx.scene.layout.BorderStroke
|
|
||||||
import org.jackhuang.hmcl.MainApplication
|
import org.jackhuang.hmcl.MainApplication
|
||||||
import org.jackhuang.hmcl.ui.animation.AnimationProducer
|
import org.jackhuang.hmcl.ui.animation.AnimationProducer
|
||||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations
|
import org.jackhuang.hmcl.ui.animation.ContainerAnimations
|
||||||
@@ -52,7 +49,7 @@ import org.jackhuang.hmcl.util.*
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue
|
import java.util.concurrent.ConcurrentLinkedQueue
|
||||||
|
|
||||||
class Decorator @JvmOverloads constructor(private val primaryStage: Stage, private val max: Boolean = true, min: Boolean = true) : GridPane(), AbstractWizardDisplayer {
|
class Decorator @JvmOverloads constructor(private val primaryStage: Stage, private val mainPage: Node, private val max: Boolean = true, min: Boolean = true) : GridPane(), AbstractWizardDisplayer {
|
||||||
override val wizardController: WizardController = WizardController(this)
|
override val wizardController: WizardController = WizardController(this)
|
||||||
|
|
||||||
private var xOffset: Double = 0.0
|
private var xOffset: Double = 0.0
|
||||||
@@ -370,16 +367,28 @@ class Decorator @JvmOverloads constructor(private val primaryStage: Stage, priva
|
|||||||
if (content is WizardPage)
|
if (content is WizardPage)
|
||||||
titleLabel.text = prefix + content.title
|
titleLabel.text = prefix + content.title
|
||||||
|
|
||||||
if (content is HasTitle)
|
if (content is DecoratorPage)
|
||||||
titleLabel.textProperty().bind(content.titleProperty)
|
titleLabel.textProperty().bind(content.titleProperty)
|
||||||
}
|
}
|
||||||
|
|
||||||
lateinit var mainPage: Node
|
|
||||||
var category: String? = null
|
var category: String? = null
|
||||||
|
var nowPage: Node? = null
|
||||||
|
|
||||||
fun showPage(content: Node?) {
|
fun showPage(content: Node?) {
|
||||||
|
val c = content ?: mainPage
|
||||||
onEnd()
|
onEnd()
|
||||||
setContent(content ?: mainPage, ContainerAnimations.FADE.animationProducer)
|
val nowPage = nowPage
|
||||||
|
if (nowPage is DecoratorPage)
|
||||||
|
nowPage.onClose()
|
||||||
|
this.nowPage = content
|
||||||
|
setContent(c, ContainerAnimations.FADE.animationProducer)
|
||||||
|
|
||||||
|
if (c is Region)
|
||||||
|
// Let root pane fix window size.
|
||||||
|
with(c.parent as StackPane) {
|
||||||
|
c.prefWidthProperty().bind(widthProperty())
|
||||||
|
c.prefHeightProperty().bind(heightProperty())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startWizard(wizardProvider: WizardProvider, category: String? = null) {
|
fun startWizard(wizardProvider: WizardProvider, category: String? = null) {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
package org.jackhuang.hmcl.ui
|
package org.jackhuang.hmcl.ui
|
||||||
|
|
||||||
import com.jfoenix.concurrency.JFXUtilities
|
import com.jfoenix.concurrency.JFXUtilities
|
||||||
|
import com.jfoenix.controls.JFXScrollPane
|
||||||
import javafx.animation.Animation
|
import javafx.animation.Animation
|
||||||
import javafx.animation.KeyFrame
|
import javafx.animation.KeyFrame
|
||||||
import javafx.animation.Timeline
|
import javafx.animation.Timeline
|
||||||
@@ -29,10 +30,10 @@ import javafx.scene.Parent
|
|||||||
import javafx.scene.Scene
|
import javafx.scene.Scene
|
||||||
import javafx.scene.control.ListView
|
import javafx.scene.control.ListView
|
||||||
import javafx.scene.control.ScrollBar
|
import javafx.scene.control.ScrollBar
|
||||||
|
import javafx.scene.control.ScrollPane
|
||||||
import javafx.scene.image.WritableImage
|
import javafx.scene.image.WritableImage
|
||||||
import javafx.scene.input.MouseEvent
|
import javafx.scene.input.MouseEvent
|
||||||
import javafx.scene.input.ScrollEvent
|
import javafx.scene.input.ScrollEvent
|
||||||
import javafx.scene.layout.Pane
|
|
||||||
import javafx.scene.layout.Region
|
import javafx.scene.layout.Region
|
||||||
import javafx.scene.shape.Rectangle
|
import javafx.scene.shape.Rectangle
|
||||||
import javafx.util.Duration
|
import javafx.util.Duration
|
||||||
@@ -90,6 +91,8 @@ fun ListView<*>.smoothScrolling() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun ScrollPane.smoothScrolling() = JFXScrollPane.smoothScrolling(this)
|
||||||
|
|
||||||
fun runOnUiThread(runnable: () -> Unit) = {
|
fun runOnUiThread(runnable: () -> Unit) = {
|
||||||
JFXUtilities.runInFX(runnable)
|
JFXUtilities.runInFX(runnable)
|
||||||
}
|
}
|
||||||
@@ -109,6 +112,5 @@ fun setOverflowHidden(node: Region) {
|
|||||||
|
|
||||||
val stylesheets = arrayOf(
|
val stylesheets = arrayOf(
|
||||||
Controllers::class.java.getResource("/css/jfoenix-fonts.css").toExternalForm(),
|
Controllers::class.java.getResource("/css/jfoenix-fonts.css").toExternalForm(),
|
||||||
Controllers::class.java.getResource("/css/jfoenix-design.css").toExternalForm(),
|
Controllers::class.java.getResource("/css/jfoenix-design.css").toExternalForm(),
|
||||||
//Controllers::class.java.getResource("/assets/css/jfoenix-components.css").toExternalForm(),
|
Controllers::class.java.getResource("/assets/css/jfoenix-main-demo.css").toExternalForm())
|
||||||
Controllers::class.java.getResource("/assets/css/jfoenix-main-demo.css").toExternalForm())
|
|
||||||
|
|||||||
@@ -18,11 +18,14 @@
|
|||||||
package org.jackhuang.hmcl.ui
|
package org.jackhuang.hmcl.ui
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXComboBox
|
import com.jfoenix.controls.JFXComboBox
|
||||||
|
import javafx.beans.property.StringProperty
|
||||||
|
import javafx.beans.value.ChangeListener
|
||||||
import javafx.scene.Node
|
import javafx.scene.Node
|
||||||
import javafx.scene.layout.*
|
import javafx.scene.layout.*
|
||||||
import javafx.scene.paint.Paint
|
import javafx.scene.paint.Paint
|
||||||
import org.jackhuang.hmcl.ProfileChangedEvent
|
import org.jackhuang.hmcl.ProfileChangedEvent
|
||||||
import org.jackhuang.hmcl.ProfileLoadingEvent
|
import org.jackhuang.hmcl.ProfileLoadingEvent
|
||||||
|
import org.jackhuang.hmcl.auth.Account
|
||||||
import org.jackhuang.hmcl.event.EVENT_BUS
|
import org.jackhuang.hmcl.event.EVENT_BUS
|
||||||
import org.jackhuang.hmcl.event.RefreshedVersionsEvent
|
import org.jackhuang.hmcl.event.RefreshedVersionsEvent
|
||||||
import org.jackhuang.hmcl.game.LauncherHelper
|
import org.jackhuang.hmcl.game.LauncherHelper
|
||||||
@@ -62,6 +65,21 @@ class LeftPaneController(val leftPane: VBox) {
|
|||||||
Settings.selectedProfile.repository.refreshVersions()
|
Settings.selectedProfile.repository.refreshVersions()
|
||||||
}
|
}
|
||||||
Controllers.mainPane.buttonLaunch.setOnMouseClicked { LauncherHelper.launch() }
|
Controllers.mainPane.buttonLaunch.setOnMouseClicked { LauncherHelper.launch() }
|
||||||
|
|
||||||
|
val listener = ChangeListener<Account?> { _, _, newValue ->
|
||||||
|
if (newValue == null) {
|
||||||
|
accountItem.lblVersionName.text = "mojang@mojang.com"
|
||||||
|
accountItem.lblGameVersion.text = "Yggdrasil"
|
||||||
|
} else {
|
||||||
|
accountItem.lblVersionName.text = newValue.username
|
||||||
|
accountItem.lblGameVersion.text = accountType(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Settings.selectedAccountProperty.addListener(listener)
|
||||||
|
listener.changed(null, null, Settings.selectedAccount)
|
||||||
|
|
||||||
|
if (Settings.getAccounts().isEmpty())
|
||||||
|
Controllers.navigate(AccountsPage())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addChildren(content: Node) {
|
private fun addChildren(content: Node) {
|
||||||
@@ -81,13 +99,10 @@ class LeftPaneController(val leftPane: VBox) {
|
|||||||
|
|
||||||
fun onProfileChanged(event: ProfileChangedEvent) {
|
fun onProfileChanged(event: ProfileChangedEvent) {
|
||||||
val profile = event.value
|
val profile = event.value
|
||||||
profile.selectedVersionProperty.addListener { _, _, newValue ->
|
profile.selectedVersionProperty.addListener { observable ->
|
||||||
versionChanged(newValue)
|
versionChanged(profile.selectedVersion)
|
||||||
}
|
}
|
||||||
}
|
profile.selectedVersionProperty.fireValueChangedEvent()
|
||||||
|
|
||||||
private fun loadAccounts() {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadVersions() {
|
private fun loadVersions() {
|
||||||
@@ -98,7 +113,7 @@ class LeftPaneController(val leftPane: VBox) {
|
|||||||
val ripplerContainer = RipplerContainer(item)
|
val ripplerContainer = RipplerContainer(item)
|
||||||
item.onSettingsButtonClicked {
|
item.onSettingsButtonClicked {
|
||||||
Controllers.decorator.showPage(Controllers.versionPane)
|
Controllers.decorator.showPage(Controllers.versionPane)
|
||||||
Controllers.versionPane.loadVersionSetting(item.versionName, profile.getVersionSetting(item.versionName))
|
Controllers.versionPane.load(item.versionName, profile)
|
||||||
}
|
}
|
||||||
ripplerContainer.ripplerFill = Paint.valueOf("#89E1F9")
|
ripplerContainer.ripplerFill = Paint.valueOf("#89E1F9")
|
||||||
ripplerContainer.setOnMouseClicked {
|
ripplerContainer.setOnMouseClicked {
|
||||||
|
|||||||
@@ -18,34 +18,16 @@
|
|||||||
package org.jackhuang.hmcl.ui
|
package org.jackhuang.hmcl.ui
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXButton
|
import com.jfoenix.controls.JFXButton
|
||||||
import com.jfoenix.controls.JFXComboBox
|
|
||||||
import com.jfoenix.controls.JFXListCell
|
|
||||||
import com.jfoenix.controls.JFXListView
|
|
||||||
import javafx.beans.property.SimpleStringProperty
|
import javafx.beans.property.SimpleStringProperty
|
||||||
import javafx.beans.property.StringProperty
|
import javafx.beans.property.StringProperty
|
||||||
import javafx.collections.FXCollections
|
|
||||||
import javafx.fxml.FXML
|
import javafx.fxml.FXML
|
||||||
import javafx.scene.Node
|
|
||||||
import javafx.scene.layout.BorderPane
|
|
||||||
import javafx.scene.layout.Pane
|
|
||||||
import javafx.scene.layout.StackPane
|
import javafx.scene.layout.StackPane
|
||||||
import org.jackhuang.hmcl.ProfileChangedEvent
|
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
||||||
import org.jackhuang.hmcl.ProfileLoadingEvent
|
|
||||||
import org.jackhuang.hmcl.event.EVENT_BUS
|
|
||||||
import org.jackhuang.hmcl.event.RefreshedVersionsEvent
|
|
||||||
import org.jackhuang.hmcl.game.minecraftVersion
|
|
||||||
import org.jackhuang.hmcl.setting.Settings
|
|
||||||
import org.jackhuang.hmcl.setting.VersionSetting
|
|
||||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations
|
|
||||||
import org.jackhuang.hmcl.ui.download.DownloadWizardProvider
|
|
||||||
import org.jackhuang.hmcl.ui.animation.TransitionHandler
|
|
||||||
import org.jackhuang.hmcl.ui.wizard.HasTitle
|
|
||||||
import org.jackhuang.hmcl.ui.wizard.Wizard
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see /assets/fxml/main.fxml
|
* @see /assets/fxml/main.fxml
|
||||||
*/
|
*/
|
||||||
class MainPage : BorderPane(), HasTitle {
|
class MainPage : StackPane(), DecoratorPage {
|
||||||
override val titleProperty: StringProperty = SimpleStringProperty(this, "title", "Main Page")
|
override val titleProperty: StringProperty = SimpleStringProperty(this, "title", "Main Page")
|
||||||
|
|
||||||
@FXML lateinit var buttonLaunch: JFXButton
|
@FXML lateinit var buttonLaunch: JFXButton
|
||||||
|
|||||||
51
HMCL/src/main/java/org/jackhuang/hmcl/ui/ModController.kt
Normal file
51
HMCL/src/main/java/org/jackhuang/hmcl/ui/ModController.kt
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* 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.ui
|
||||||
|
|
||||||
|
import javafx.fxml.FXML
|
||||||
|
import javafx.scene.Node
|
||||||
|
import javafx.scene.control.ScrollPane
|
||||||
|
import javafx.scene.layout.VBox
|
||||||
|
import org.jackhuang.hmcl.mod.ModManager
|
||||||
|
import org.jackhuang.hmcl.task.Scheduler
|
||||||
|
import org.jackhuang.hmcl.task.Task
|
||||||
|
import java.util.concurrent.Callable
|
||||||
|
|
||||||
|
class ModController {
|
||||||
|
@FXML lateinit var scrollPane: ScrollPane
|
||||||
|
@FXML lateinit var rootPane: VBox
|
||||||
|
lateinit var modManager: ModManager
|
||||||
|
lateinit var versionId: String
|
||||||
|
|
||||||
|
fun initialize() {
|
||||||
|
scrollPane.smoothScrolling()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadMods() {
|
||||||
|
Task.of(Callable {
|
||||||
|
modManager.refreshMods(versionId)
|
||||||
|
}).subscribe(Scheduler.JAVAFX) {
|
||||||
|
rootPane.children.clear()
|
||||||
|
for (modInfo in modManager.getMods(versionId)) {
|
||||||
|
rootPane.children += ModItem(modInfo) {
|
||||||
|
modManager.removeMods(versionId, modInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
45
HMCL/src/main/java/org/jackhuang/hmcl/ui/ModItem.kt
Normal file
45
HMCL/src/main/java/org/jackhuang/hmcl/ui/ModItem.kt
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 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.ui
|
||||||
|
|
||||||
|
import com.jfoenix.controls.JFXCheckBox
|
||||||
|
import javafx.fxml.FXML
|
||||||
|
import javafx.scene.control.Label
|
||||||
|
import javafx.scene.layout.BorderPane
|
||||||
|
import org.jackhuang.hmcl.mod.ModInfo
|
||||||
|
|
||||||
|
class ModItem(info: ModInfo, private val deleteCallback: () -> Unit) : BorderPane() {
|
||||||
|
@FXML lateinit var lblModFileName: Label
|
||||||
|
@FXML lateinit var lblModAuthor: Label
|
||||||
|
@FXML lateinit var chkEnabled: JFXCheckBox
|
||||||
|
|
||||||
|
init {
|
||||||
|
loadFXML("/assets/fxml/mod-item.fxml")
|
||||||
|
|
||||||
|
lblModFileName.text = info.fileName
|
||||||
|
lblModAuthor.text = "${info.name}, Version: ${info.version}, Game Version: ${info.mcversion}, Authors: ${info.authors}"
|
||||||
|
chkEnabled.isSelected = info.isActive
|
||||||
|
chkEnabled.selectedProperty().addListener { _, _, newValue ->
|
||||||
|
info.activeProperty.set(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onDelete() {
|
||||||
|
deleteCallback()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -81,42 +81,22 @@ open class RipplerContainer(@NamedArg("container") container: Node): StackPane()
|
|||||||
init {
|
init {
|
||||||
styleClass += "rippler-container"
|
styleClass += "rippler-container"
|
||||||
this.container = container
|
this.container = container
|
||||||
/*
|
|
||||||
armedProperty().addListener { o, oldVal, newVal ->
|
|
||||||
if (newVal!!.booleanValue()) {
|
|
||||||
this.releaseManualRippler = this.buttonRippler.createManualRipple()
|
|
||||||
if (this.clickedAnimation != null) {
|
|
||||||
this.clickedAnimation!!.rate = 1.0
|
|
||||||
this.clickedAnimation!!.play()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (this.releaseManualRippler != null) {
|
|
||||||
this.releaseManualRippler!!.run()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.clickedAnimation != null) {
|
|
||||||
this.clickedAnimation!!.rate = -1.0
|
|
||||||
this.clickedAnimation!!.play()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}*/
|
|
||||||
this.buttonContainer.children.add(this.buttonRippler)
|
this.buttonContainer.children.add(this.buttonRippler)
|
||||||
setOnMousePressed { e ->
|
setOnMousePressed {
|
||||||
if (this.clickedAnimation != null) {
|
if (this.clickedAnimation != null) {
|
||||||
this.clickedAnimation!!.rate = 1.0
|
this.clickedAnimation!!.rate = 1.0
|
||||||
this.clickedAnimation!!.play()
|
this.clickedAnimation!!.play()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
setOnMouseReleased { e ->
|
setOnMouseReleased {
|
||||||
if (this.clickedAnimation != null) {
|
if (this.clickedAnimation != null) {
|
||||||
this.clickedAnimation!!.rate = -1.0
|
this.clickedAnimation!!.rate = -1.0
|
||||||
this.clickedAnimation!!.play()
|
this.clickedAnimation!!.play()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
focusedProperty().addListener { o, oldVal, newVal ->
|
focusedProperty().addListener { _, _, newVal ->
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
if (!isPressed) {
|
if (!isPressed) {
|
||||||
this.buttonRippler.showOverlay()
|
this.buttonRippler.showOverlay()
|
||||||
@@ -126,7 +106,7 @@ open class RipplerContainer(@NamedArg("container") container: Node): StackPane()
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
pressedProperty().addListener { _, _, _ -> this.buttonRippler.hideOverlay() }
|
pressedProperty().addListener { _ -> this.buttonRippler.hideOverlay() }
|
||||||
isPickOnBounds = false
|
isPickOnBounds = false
|
||||||
this.buttonContainer.isPickOnBounds = false
|
this.buttonContainer.isPickOnBounds = false
|
||||||
this.buttonContainer.shapeProperty().bind(shapeProperty())
|
this.buttonContainer.shapeProperty().bind(shapeProperty())
|
||||||
@@ -157,8 +137,8 @@ open class RipplerContainer(@NamedArg("container") container: Node): StackPane()
|
|||||||
this.updateChildren()
|
this.updateChildren()
|
||||||
|
|
||||||
containerProperty.addListener { _ -> updateChildren() }
|
containerProperty.addListener { _ -> updateChildren() }
|
||||||
selectedProperty.addListener { _, _, newValue ->
|
selectedProperty.addListener { _ ->
|
||||||
if (newValue) background = Background(BackgroundFill(ripplerFill, defaultRadii, null))
|
if (selected) background = Background(BackgroundFill(ripplerFill, defaultRadii, null))
|
||||||
else background = Background(BackgroundFill(Color.TRANSPARENT, defaultRadii, null))
|
else background = Background(BackgroundFill(Color.TRANSPARENT, defaultRadii, null))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,107 +17,38 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui
|
package org.jackhuang.hmcl.ui
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXScrollPane
|
import com.jfoenix.controls.*
|
||||||
import com.jfoenix.controls.JFXTextField
|
|
||||||
import javafx.beans.InvalidationListener
|
import javafx.beans.InvalidationListener
|
||||||
import javafx.beans.property.Property
|
import javafx.beans.property.Property
|
||||||
import javafx.beans.property.SimpleStringProperty
|
import javafx.beans.property.SimpleStringProperty
|
||||||
import javafx.beans.property.StringProperty
|
import javafx.beans.property.StringProperty
|
||||||
|
import javafx.beans.value.ChangeListener
|
||||||
import javafx.fxml.FXML
|
import javafx.fxml.FXML
|
||||||
|
import javafx.scene.control.Label
|
||||||
import javafx.scene.control.ScrollPane
|
import javafx.scene.control.ScrollPane
|
||||||
import javafx.scene.layout.*
|
import javafx.scene.layout.*
|
||||||
import javafx.stage.DirectoryChooser
|
import javafx.stage.DirectoryChooser
|
||||||
|
import org.jackhuang.hmcl.setting.Profile
|
||||||
import org.jackhuang.hmcl.setting.VersionSetting
|
import org.jackhuang.hmcl.setting.VersionSetting
|
||||||
import org.jackhuang.hmcl.ui.wizard.HasTitle
|
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
||||||
|
import org.jackhuang.hmcl.util.OS
|
||||||
|
|
||||||
class VersionPage : StackPane(), HasTitle {
|
class VersionPage : StackPane(), DecoratorPage {
|
||||||
override val titleProperty: StringProperty = SimpleStringProperty(this, "title", null)
|
override val titleProperty: StringProperty = SimpleStringProperty(this, "title", null)
|
||||||
var lastVersionSetting: VersionSetting? = null
|
|
||||||
|
|
||||||
@FXML lateinit var rootPane: VBox
|
@FXML lateinit var versionSettingsController: VersionSettingsController
|
||||||
@FXML lateinit var scroll: ScrollPane
|
@FXML lateinit var modController: ModController
|
||||||
@FXML lateinit var settingsPane: GridPane
|
|
||||||
@FXML lateinit var txtWidth: JFXTextField
|
|
||||||
@FXML lateinit var txtHeight: JFXTextField
|
|
||||||
@FXML lateinit var txtMaxMemory: JFXTextField
|
|
||||||
@FXML lateinit var txtJVMArgs: JFXTextField
|
|
||||||
@FXML lateinit var txtGameArgs: JFXTextField
|
|
||||||
@FXML lateinit var txtMetaspace: JFXTextField
|
|
||||||
@FXML lateinit var txtWrapper: JFXTextField
|
|
||||||
@FXML lateinit var txtPrecallingCommand: JFXTextField
|
|
||||||
@FXML lateinit var txtServerIP: JFXTextField
|
|
||||||
@FXML lateinit var txtGameDir: JFXTextField
|
|
||||||
@FXML lateinit var advancedSettingsPane: VBox
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
loadFXML("/assets/fxml/version.fxml")
|
loadFXML("/assets/fxml/version.fxml")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun initialize() {
|
fun load(id: String, profile: Profile) {
|
||||||
JFXScrollPane.smoothScrolling(scroll)
|
|
||||||
|
|
||||||
fun validation(field: JFXTextField) = InvalidationListener { field.validate() }
|
|
||||||
fun validator(nullable: Boolean = false) = NumberValidator(nullable).apply { message = "Must be a number." }
|
|
||||||
|
|
||||||
txtWidth.setValidators(validator())
|
|
||||||
txtWidth.textProperty().addListener(validation(txtWidth))
|
|
||||||
txtHeight.setValidators(validator())
|
|
||||||
txtHeight.textProperty().addListener(validation(txtHeight))
|
|
||||||
txtMaxMemory.setValidators(validator())
|
|
||||||
txtMaxMemory.textProperty().addListener(validation(txtMaxMemory))
|
|
||||||
txtMetaspace.setValidators(validator(true))
|
|
||||||
txtMetaspace.textProperty().addListener(validation(txtMetaspace))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun loadVersionSetting(id: String, version: VersionSetting) {
|
|
||||||
titleProperty.set("Version settings - " + id)
|
titleProperty.set("Version settings - " + id)
|
||||||
rootPane.children -= advancedSettingsPane
|
|
||||||
|
|
||||||
lastVersionSetting?.apply {
|
versionSettingsController.loadVersionSetting(id, profile.getVersionSetting(id))
|
||||||
widthProperty.unbind()
|
modController.modManager = profile.modManager
|
||||||
heightProperty.unbind()
|
modController.versionId = id
|
||||||
maxMemoryProperty.unbind()
|
modController.loadMods()
|
||||||
javaArgsProperty.unbind()
|
|
||||||
minecraftArgsProperty.unbind()
|
|
||||||
permSizeProperty.unbind()
|
|
||||||
wrapperProperty.unbind()
|
|
||||||
precalledCommandProperty.unbind()
|
|
||||||
serverIpProperty.unbind()
|
|
||||||
}
|
|
||||||
|
|
||||||
bindInt(txtWidth, version.widthProperty)
|
|
||||||
bindInt(txtHeight, version.heightProperty)
|
|
||||||
bindInt(txtMaxMemory, version.maxMemoryProperty)
|
|
||||||
bindString(txtJVMArgs, version.javaArgsProperty)
|
|
||||||
bindString(txtGameArgs, version.minecraftArgsProperty)
|
|
||||||
bindString(txtMetaspace, version.permSizeProperty)
|
|
||||||
bindString(txtWrapper, version.wrapperProperty)
|
|
||||||
bindString(txtPrecallingCommand, version.precalledCommandProperty)
|
|
||||||
bindString(txtServerIP, version.serverIpProperty)
|
|
||||||
|
|
||||||
lastVersionSetting = version
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun bindInt(textField: JFXTextField, property: Property<*>) {
|
|
||||||
textField.textProperty().unbind()
|
|
||||||
textField.textProperty().bindBidirectional(property as Property<Int>, SafeIntStringConverter())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun bindString(textField: JFXTextField, property: Property<String>) {
|
|
||||||
textField.textProperty().unbind()
|
|
||||||
textField.textProperty().bindBidirectional(property)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onShowAdvanced() {
|
|
||||||
if (!rootPane.children.contains(advancedSettingsPane))
|
|
||||||
rootPane.children += advancedSettingsPane
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onExploreJavaDir() {
|
|
||||||
val chooser = DirectoryChooser()
|
|
||||||
chooser.title = "Selecting Java Directory"
|
|
||||||
val selectedDir = chooser.showDialog(Controllers.stage)
|
|
||||||
if (selectedDir != null)
|
|
||||||
txtGameDir.text = selectedDir.absolutePath
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* 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.ui
|
||||||
|
|
||||||
|
import com.jfoenix.controls.JFXCheckBox
|
||||||
|
import com.jfoenix.controls.JFXComboBox
|
||||||
|
import com.jfoenix.controls.JFXTextField
|
||||||
|
import javafx.beans.InvalidationListener
|
||||||
|
import javafx.beans.property.Property
|
||||||
|
import javafx.beans.value.ChangeListener
|
||||||
|
import javafx.fxml.FXML
|
||||||
|
import javafx.scene.control.Label
|
||||||
|
import javafx.scene.control.ScrollPane
|
||||||
|
import javafx.scene.layout.GridPane
|
||||||
|
import javafx.scene.layout.VBox
|
||||||
|
import javafx.stage.DirectoryChooser
|
||||||
|
import org.jackhuang.hmcl.setting.VersionSetting
|
||||||
|
import org.jackhuang.hmcl.util.OS
|
||||||
|
|
||||||
|
class VersionSettingsController {
|
||||||
|
var lastVersionSetting: VersionSetting? = null
|
||||||
|
@FXML lateinit var rootPane: VBox
|
||||||
|
@FXML lateinit var scroll: ScrollPane
|
||||||
|
@FXML lateinit var settingsPane: GridPane
|
||||||
|
@FXML lateinit var txtWidth: JFXTextField
|
||||||
|
@FXML lateinit var txtHeight: JFXTextField
|
||||||
|
@FXML lateinit var txtMaxMemory: JFXTextField
|
||||||
|
@FXML lateinit var txtJVMArgs: JFXTextField
|
||||||
|
@FXML lateinit var txtGameArgs: JFXTextField
|
||||||
|
@FXML lateinit var txtMetaspace: JFXTextField
|
||||||
|
@FXML lateinit var txtWrapper: JFXTextField
|
||||||
|
@FXML lateinit var txtPrecallingCommand: JFXTextField
|
||||||
|
@FXML lateinit var txtServerIP: JFXTextField
|
||||||
|
@FXML lateinit var txtGameDir: JFXTextField
|
||||||
|
@FXML lateinit var advancedSettingsPane: VBox
|
||||||
|
@FXML lateinit var cboLauncherVisibility: JFXComboBox<*>
|
||||||
|
@FXML lateinit var cboRunDirectory: JFXComboBox<*>
|
||||||
|
@FXML lateinit var chkFullscreen: JFXCheckBox
|
||||||
|
@FXML lateinit var lblPhysicalMemory: Label
|
||||||
|
|
||||||
|
fun initialize() {
|
||||||
|
lblPhysicalMemory.text = "Physical Memory: ${OS.TOTAL_MEMORY}MB"
|
||||||
|
|
||||||
|
scroll.smoothScrolling()
|
||||||
|
|
||||||
|
fun validation(field: JFXTextField) = InvalidationListener { field.validate() }
|
||||||
|
fun validator(nullable: Boolean = false) = NumberValidator(nullable).apply { message = "Must be a number." }
|
||||||
|
|
||||||
|
txtWidth.setValidators(validator())
|
||||||
|
txtWidth.textProperty().addListener(validation(txtWidth))
|
||||||
|
txtHeight.setValidators(validator())
|
||||||
|
txtHeight.textProperty().addListener(validation(txtHeight))
|
||||||
|
txtMaxMemory.setValidators(validator())
|
||||||
|
txtMaxMemory.textProperty().addListener(validation(txtMaxMemory))
|
||||||
|
txtMetaspace.setValidators(validator(true))
|
||||||
|
txtMetaspace.textProperty().addListener(validation(txtMetaspace))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadVersionSetting(id: String, version: VersionSetting) {
|
||||||
|
rootPane.children -= advancedSettingsPane
|
||||||
|
|
||||||
|
lastVersionSetting?.apply {
|
||||||
|
widthProperty.unbind()
|
||||||
|
heightProperty.unbind()
|
||||||
|
maxMemoryProperty.unbind()
|
||||||
|
javaArgsProperty.unbind()
|
||||||
|
minecraftArgsProperty.unbind()
|
||||||
|
permSizeProperty.unbind()
|
||||||
|
wrapperProperty.unbind()
|
||||||
|
precalledCommandProperty.unbind()
|
||||||
|
serverIpProperty.unbind()
|
||||||
|
fullscreenProperty.unbind()
|
||||||
|
unbindEnum(cboLauncherVisibility)
|
||||||
|
unbindEnum(cboRunDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
bindInt(txtWidth, version.widthProperty)
|
||||||
|
bindInt(txtHeight, version.heightProperty)
|
||||||
|
bindInt(txtMaxMemory, version.maxMemoryProperty)
|
||||||
|
bindString(txtJVMArgs, version.javaArgsProperty)
|
||||||
|
bindString(txtGameArgs, version.minecraftArgsProperty)
|
||||||
|
bindString(txtMetaspace, version.permSizeProperty)
|
||||||
|
bindString(txtWrapper, version.wrapperProperty)
|
||||||
|
bindString(txtPrecallingCommand, version.precalledCommandProperty)
|
||||||
|
bindString(txtServerIP, version.serverIpProperty)
|
||||||
|
bindEnum(cboLauncherVisibility, version.launcherVisibilityProperty)
|
||||||
|
bindEnum(cboRunDirectory, version.gameDirTypeProperty)
|
||||||
|
|
||||||
|
chkFullscreen.selectedProperty().unbind()
|
||||||
|
chkFullscreen.selectedProperty().bindBidirectional(version.fullscreenProperty)
|
||||||
|
|
||||||
|
lastVersionSetting = version
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindInt(textField: JFXTextField, property: Property<*>) {
|
||||||
|
textField.textProperty().unbind()
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
textField.textProperty().bindBidirectional(property as Property<Int>, SafeIntStringConverter())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindString(textField: JFXTextField, property: Property<String>) {
|
||||||
|
textField.textProperty().unbind()
|
||||||
|
textField.textProperty().bindBidirectional(property)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindEnum(comboBox: JFXComboBox<*>, property: Property<out Enum<*>>) {
|
||||||
|
unbindEnum(comboBox)
|
||||||
|
val listener = ChangeListener<Number> { _, _, newValue ->
|
||||||
|
property.value = property.value.javaClass.enumConstants[newValue.toInt()]
|
||||||
|
}
|
||||||
|
comboBox.selectionModel.select(property.value.ordinal)
|
||||||
|
comboBox.properties["listener"] = listener
|
||||||
|
comboBox.selectionModel.selectedIndexProperty().addListener(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unbindEnum(comboBox: JFXComboBox<*>) {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val listener = comboBox.properties["listener"] as? ChangeListener<Number> ?: return
|
||||||
|
comboBox.selectionModel.selectedIndexProperty().removeListener(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onShowAdvanced() {
|
||||||
|
if (!rootPane.children.contains(advancedSettingsPane))
|
||||||
|
rootPane.children += advancedSettingsPane
|
||||||
|
else
|
||||||
|
rootPane.children.remove(advancedSettingsPane)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onExploreJavaDir() {
|
||||||
|
val chooser = DirectoryChooser()
|
||||||
|
chooser.title = "Selecting Java Directory"
|
||||||
|
val selectedDir = chooser.showDialog(Controllers.stage)
|
||||||
|
if (selectedDir != null)
|
||||||
|
txtGameDir.text = selectedDir.absolutePath
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,8 @@ package org.jackhuang.hmcl.ui.wizard
|
|||||||
|
|
||||||
import javafx.beans.property.StringProperty
|
import javafx.beans.property.StringProperty
|
||||||
|
|
||||||
interface HasTitle {
|
interface DecoratorPage {
|
||||||
val titleProperty: StringProperty
|
val titleProperty: StringProperty
|
||||||
|
|
||||||
|
fun onClose() {}
|
||||||
}
|
}
|
||||||
@@ -3,11 +3,11 @@
|
|||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
<?import com.jfoenix.controls.JFXButton?>
|
<?import com.jfoenix.controls.JFXButton?>
|
||||||
<?import javafx.scene.shape.SVGPath?>
|
|
||||||
<?import javafx.scene.image.ImageView?>
|
<?import javafx.scene.image.ImageView?>
|
||||||
<?import javafx.scene.image.Image?>
|
<?import javafx.scene.image.Image?>
|
||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import com.jfoenix.controls.JFXRadioButton?>
|
||||||
<fx:root xmlns="http://javafx.com/javafx"
|
<fx:root xmlns="http://javafx.com/javafx"
|
||||||
xmlns:fx="http://javafx.com/fxml"
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
type="StackPane">
|
type="StackPane">
|
||||||
@@ -27,9 +27,11 @@
|
|||||||
</center>
|
</center>
|
||||||
</BorderPane>
|
</BorderPane>
|
||||||
</StackPane>
|
</StackPane>
|
||||||
<StackPane fx:id="body" style="-fx-background-radius: 0 0 5 5; -fx-background-color: rgb(255,255,255,0.87);" />
|
<StackPane fx:id="body" style="-fx-background-radius: 0 0 5 5; -fx-background-color: rgb(255,255,255,0.87); -fx-padding: 0 0 10 0;">
|
||||||
|
<JFXRadioButton fx:id="chkSelected" StackPane.alignment="BOTTOM_RIGHT" />
|
||||||
|
</StackPane>
|
||||||
</VBox>
|
</VBox>
|
||||||
<StackPane fx:id="icon" StackPane.alignment="TOP_RIGHT">
|
<StackPane fx:id="icon" StackPane.alignment="TOP_RIGHT" pickOnBounds="false">
|
||||||
<ImageView StackPane.alignment="CENTER_RIGHT">
|
<ImageView StackPane.alignment="CENTER_RIGHT">
|
||||||
<StackPane.margin>
|
<StackPane.margin>
|
||||||
<Insets right="12" />
|
<Insets right="12" />
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
<?import javafx.collections.FXCollections?>
|
<?import javafx.collections.FXCollections?>
|
||||||
<?import java.lang.String?>
|
<?import java.lang.String?>
|
||||||
<?import com.jfoenix.validation.RequiredFieldValidator?>
|
<?import com.jfoenix.validation.RequiredFieldValidator?>
|
||||||
|
<?import com.jfoenix.controls.JFXNodesList?>
|
||||||
<fx:root xmlns="http://javafx.com/javafx"
|
<fx:root xmlns="http://javafx.com/javafx"
|
||||||
xmlns:fx="http://javafx.com/fxml"
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
type="StackPane">
|
type="StackPane">
|
||||||
@@ -34,10 +35,10 @@
|
|||||||
<Label>Create a new account</Label>
|
<Label>Create a new account</Label>
|
||||||
</heading>
|
</heading>
|
||||||
<body>
|
<body>
|
||||||
<GridPane vgap="30" hgap="30">
|
<GridPane vgap="15" hgap="15" style="-fx-padding: 15 0 0 0;">
|
||||||
<columnConstraints>
|
<columnConstraints>
|
||||||
|
<ColumnConstraints maxWidth="100" />
|
||||||
<ColumnConstraints />
|
<ColumnConstraints />
|
||||||
<ColumnConstraints hgrow="ALWAYS" />
|
|
||||||
</columnConstraints>
|
</columnConstraints>
|
||||||
<Label text="Type" GridPane.halignment="RIGHT" GridPane.columnIndex="0" GridPane.rowIndex="0" />
|
<Label text="Type" GridPane.halignment="RIGHT" GridPane.columnIndex="0" GridPane.rowIndex="0" />
|
||||||
<Label text="Username" GridPane.halignment="RIGHT" GridPane.columnIndex="0" GridPane.rowIndex="1" />
|
<Label text="Username" GridPane.halignment="RIGHT" GridPane.columnIndex="0" GridPane.rowIndex="1" />
|
||||||
|
|||||||
@@ -39,13 +39,13 @@
|
|||||||
<bottom>
|
<bottom>
|
||||||
<BorderPane fx:id="menuBottomBar">
|
<BorderPane fx:id="menuBottomBar">
|
||||||
<left>
|
<left>
|
||||||
<JFXButton fx:id="refreshMenuButton" styleClass="toggle-icon3">
|
<JFXButton fx:id="refreshMenuButton" styleClass="toggle-icon4">
|
||||||
<graphic>
|
<graphic>
|
||||||
<fx:include source="/assets/svg/refresh-black.fxml"/>
|
<fx:include source="/assets/svg/refresh-black.fxml"/>
|
||||||
</graphic></JFXButton>
|
</graphic></JFXButton>
|
||||||
</left>
|
</left>
|
||||||
<right>
|
<right>
|
||||||
<JFXButton fx:id="addMenuButton" styleClass="toggle-icon3">
|
<JFXButton fx:id="addMenuButton" styleClass="toggle-icon4">
|
||||||
<graphic>
|
<graphic>
|
||||||
<fx:include source="/assets/svg/plus-black.fxml"/>
|
<fx:include source="/assets/svg/plus-black.fxml"/>
|
||||||
</graphic></JFXButton>
|
</graphic></JFXButton>
|
||||||
|
|||||||
@@ -4,13 +4,12 @@
|
|||||||
<?import com.jfoenix.controls.*?>
|
<?import com.jfoenix.controls.*?>
|
||||||
<fx:root
|
<fx:root
|
||||||
maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
|
maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
|
||||||
style="-fx-background-color: white;" type="BorderPane"
|
style="-fx-background-color: white;" type="StackPane"
|
||||||
xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1">
|
xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1">
|
||||||
|
<BorderPane>
|
||||||
<center>
|
<center>
|
||||||
<StackPane fx:id="page" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER" />
|
<StackPane fx:id="page" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER" />
|
||||||
</center>
|
</center>
|
||||||
<left>
|
|
||||||
</left>
|
|
||||||
<bottom>
|
<bottom>
|
||||||
<BorderPane prefHeight="50.0" prefWidth="200.0" BorderPane.alignment="CENTER">
|
<BorderPane prefHeight="50.0" prefWidth="200.0" BorderPane.alignment="CENTER">
|
||||||
<right>
|
<right>
|
||||||
@@ -22,4 +21,5 @@
|
|||||||
</right>
|
</right>
|
||||||
</BorderPane>
|
</BorderPane>
|
||||||
</bottom>
|
</bottom>
|
||||||
|
</BorderPane>
|
||||||
</fx:root>
|
</fx:root>
|
||||||
|
|||||||
30
HMCL/src/main/resources/assets/fxml/mod-item.fxml
Normal file
30
HMCL/src/main/resources/assets/fxml/mod-item.fxml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import java.lang.*?>
|
||||||
|
<?import java.util.*?>
|
||||||
|
<?import javafx.scene.*?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<?import com.jfoenix.controls.JFXCheckBox?>
|
||||||
|
<?import com.jfoenix.controls.JFXButton?>
|
||||||
|
<fx:root xmlns="http://javafx.com/javafx"
|
||||||
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
|
type="BorderPane">
|
||||||
|
<left>
|
||||||
|
<JFXCheckBox fx:id="chkEnabled" BorderPane.alignment="CENTER" />
|
||||||
|
</left>
|
||||||
|
<center>
|
||||||
|
<VBox BorderPane.alignment="CENTER">
|
||||||
|
<Label fx:id="lblModFileName" style="-fx-font-size: 15;" />
|
||||||
|
<Label fx:id="lblModAuthor" style="-fx-font-size: 10;" />
|
||||||
|
</VBox>
|
||||||
|
</center>
|
||||||
|
<right>
|
||||||
|
<JFXButton onMouseClicked="#onDelete" styleClass="toggle-icon4" BorderPane.alignment="CENTER">
|
||||||
|
<graphic>
|
||||||
|
<fx:include source="/assets/svg/delete-black.fxml"/>
|
||||||
|
</graphic>
|
||||||
|
</JFXButton>
|
||||||
|
</right>
|
||||||
|
</fx:root>
|
||||||
17
HMCL/src/main/resources/assets/fxml/mod.fxml
Normal file
17
HMCL/src/main/resources/assets/fxml/mod.fxml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import java.lang.*?>
|
||||||
|
<?import java.util.*?>
|
||||||
|
<?import javafx.scene.*?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<StackPane xmlns="http://javafx.com/javafx"
|
||||||
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
|
fx:controller="org.jackhuang.hmcl.ui.ModController">
|
||||||
|
<ScrollPane fx:id="scrollPane" fitToWidth="true" fitToHeight="true">
|
||||||
|
<VBox fx:id="rootPane" spacing="10" style="-fx-padding: 10 0 40 0;">
|
||||||
|
|
||||||
|
</VBox>
|
||||||
|
</ScrollPane>
|
||||||
|
</StackPane>
|
||||||
97
HMCL/src/main/resources/assets/fxml/version-settings.fxml
Normal file
97
HMCL/src/main/resources/assets/fxml/version-settings.fxml
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import java.lang.*?>
|
||||||
|
<?import java.util.*?>
|
||||||
|
<?import javafx.scene.*?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<?import com.jfoenix.controls.JFXTextField?>
|
||||||
|
<?import com.jfoenix.controls.JFXButton?>
|
||||||
|
<?import com.jfoenix.controls.JFXCheckBox?>
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import com.jfoenix.controls.JFXComboBox?>
|
||||||
|
<?import javafx.collections.FXCollections?>
|
||||||
|
<StackPane xmlns="http://javafx.com/javafx"
|
||||||
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
|
fx:controller="org.jackhuang.hmcl.ui.VersionSettingsController">
|
||||||
|
<ScrollPane fx:id="scroll"
|
||||||
|
style="-fx-font-size: 14; -fx-pref-width: 100%; "
|
||||||
|
fitToHeight="true" fitToWidth="true">
|
||||||
|
<VBox fx:id="rootPane" style="-fx-padding: 20;">
|
||||||
|
<GridPane fx:id="settingsPane" hgap="5" vgap="10">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints />
|
||||||
|
<ColumnConstraints hgrow="ALWAYS" />
|
||||||
|
<ColumnConstraints />
|
||||||
|
</columnConstraints>
|
||||||
|
<Label GridPane.rowIndex="0" GridPane.columnIndex="0">Java Directory</Label>
|
||||||
|
<Label GridPane.rowIndex="1" GridPane.columnIndex="0">Max Memory</Label>
|
||||||
|
<Label GridPane.rowIndex="2" GridPane.columnIndex="0">Launcher Visibility</Label>
|
||||||
|
<Label GridPane.rowIndex="3" GridPane.columnIndex="0">Run Directory</Label>
|
||||||
|
<Label GridPane.rowIndex="4" GridPane.columnIndex="0">Dimension</Label>
|
||||||
|
<Label GridPane.rowIndex="5" GridPane.columnIndex="0"> </Label>
|
||||||
|
|
||||||
|
<JFXTextField styleClass="fit-width" fx:id="txtGameDir" GridPane.rowIndex="0" GridPane.columnIndex="1"
|
||||||
|
maxWidth="Infinity"/>
|
||||||
|
<BorderPane GridPane.rowIndex="1" GridPane.columnIndex="1" GridPane.columnSpan="2">
|
||||||
|
<left>
|
||||||
|
<JFXTextField fx:id="txtMaxMemory" prefWidth="Infinity" />
|
||||||
|
</left>
|
||||||
|
<right>
|
||||||
|
<Label fx:id="lblPhysicalMemory" />
|
||||||
|
</right>
|
||||||
|
</BorderPane>
|
||||||
|
<JFXComboBox fx:id="cboLauncherVisibility" GridPane.rowIndex="2" GridPane.columnIndex="1"
|
||||||
|
GridPane.columnSpan="2" prefWidth="Infinity">
|
||||||
|
<items>
|
||||||
|
<FXCollections fx:factory="observableArrayList">
|
||||||
|
<Label>Close</Label>
|
||||||
|
<Label>Hide</Label>
|
||||||
|
<Label>Keep</Label>
|
||||||
|
<Label>Hide and Reopen</Label>
|
||||||
|
</FXCollections>
|
||||||
|
</items>
|
||||||
|
</JFXComboBox>
|
||||||
|
<JFXComboBox fx:id="cboRunDirectory" GridPane.rowIndex="3" GridPane.columnIndex="1"
|
||||||
|
GridPane.columnSpan="2" maxWidth="Infinity">
|
||||||
|
<items>
|
||||||
|
<FXCollections fx:factory="observableArrayList">
|
||||||
|
<Label>Default(.minecraft/)</Label>
|
||||||
|
<Label>Divided(.minecraft/versions/<versionName>)</Label>
|
||||||
|
</FXCollections>
|
||||||
|
</items>
|
||||||
|
</JFXComboBox>
|
||||||
|
<BorderPane GridPane.rowIndex="4" GridPane.columnIndex="1" GridPane.columnSpan="2">
|
||||||
|
<left>
|
||||||
|
<HBox prefWidth="210">
|
||||||
|
<JFXTextField fx:id="txtWidth" promptText="800" prefWidth="100" />
|
||||||
|
<Label>x</Label>
|
||||||
|
<JFXTextField fx:id="txtHeight" promptText="480" prefWidth="100" />
|
||||||
|
</HBox>
|
||||||
|
</left>
|
||||||
|
<right>
|
||||||
|
<JFXCheckBox fx:id="chkFullscreen" text="Fullscreen" alignment="CENTER">
|
||||||
|
<BorderPane.margin>
|
||||||
|
<Insets right="7"/>
|
||||||
|
</BorderPane.margin>
|
||||||
|
</JFXCheckBox>
|
||||||
|
</right>
|
||||||
|
</BorderPane>
|
||||||
|
|
||||||
|
<JFXButton GridPane.rowIndex="0" GridPane.columnIndex="2" text="Explore" onMouseClicked="#onExploreJavaDir" />
|
||||||
|
</GridPane>
|
||||||
|
<HBox alignment="CENTER">
|
||||||
|
<JFXButton text="Show advanced settings" onMouseClicked="#onShowAdvanced" />
|
||||||
|
</HBox>
|
||||||
|
<VBox fx:id="advancedSettingsPane" spacing="30">
|
||||||
|
<JFXTextField labelFloat="true" promptText="JVM Args" styleClass="fit-width" fx:id="txtJVMArgs" prefWidth="${advancedSettingsPane.width}" />
|
||||||
|
<JFXTextField labelFloat="true" promptText="Game Args" styleClass="fit-width" fx:id="txtGameArgs" prefWidth="${advancedSettingsPane.width}" />
|
||||||
|
<JFXTextField labelFloat="true" promptText="Metaspace" styleClass="fit-width" fx:id="txtMetaspace" prefWidth="${advancedSettingsPane.width}" />
|
||||||
|
<JFXTextField labelFloat="true" promptText="Wrapper Launcher(like optirun)" styleClass="fit-width" fx:id="txtWrapper" prefWidth="${advancedSettingsPane.width}" />
|
||||||
|
<JFXTextField labelFloat="true" promptText="Pre-calling command" styleClass="fit-width" fx:id="txtPrecallingCommand" prefWidth="${advancedSettingsPane.width}" />
|
||||||
|
<JFXTextField labelFloat="true" promptText="Server IP" styleClass="fit-width" fx:id="txtServerIP" prefWidth="${advancedSettingsPane.width}" />
|
||||||
|
</VBox>
|
||||||
|
</VBox>
|
||||||
|
</ScrollPane>
|
||||||
|
</StackPane>
|
||||||
@@ -1,94 +1,17 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<?import com.jfoenix.controls.*?>
|
<?import com.jfoenix.controls.*?>
|
||||||
<?import javafx.collections.FXCollections?>
|
|
||||||
<?import javafx.geometry.Insets?>
|
|
||||||
<?import javafx.scene.control.*?>
|
<?import javafx.scene.control.*?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
<fx:root xmlns="http://javafx.com/javafx"
|
<fx:root xmlns="http://javafx.com/javafx"
|
||||||
xmlns:fx="http://javafx.com/fxml"
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
type="StackPane">
|
type="StackPane">
|
||||||
<ScrollPane fx:id="scroll"
|
<JFXTabPane>
|
||||||
style="-fx-font-size: 14; -fx-pref-width: 100%; "
|
<Tab text="Settings">
|
||||||
fitToHeight="true" fitToWidth="true">
|
<fx:include source="version-settings.fxml" fx:id="versionSettings" />
|
||||||
<VBox fx:id="rootPane" style="-fx-padding: 20;">
|
</Tab>
|
||||||
<GridPane fx:id="settingsPane" hgap="5" vgap="10">
|
<Tab text="Mods">
|
||||||
<columnConstraints>
|
<fx:include source="mod.fxml" fx:id="mod" />
|
||||||
<ColumnConstraints />
|
</Tab>
|
||||||
<ColumnConstraints hgrow="ALWAYS" />
|
</JFXTabPane>
|
||||||
<ColumnConstraints />
|
|
||||||
</columnConstraints>
|
|
||||||
<Label GridPane.rowIndex="0" GridPane.columnIndex="0">Java Directory</Label>
|
|
||||||
<Label GridPane.rowIndex="1" GridPane.columnIndex="0">Max Memory</Label>
|
|
||||||
<Label GridPane.rowIndex="2" GridPane.columnIndex="0">Launcher Visibility</Label>
|
|
||||||
<Label GridPane.rowIndex="3" GridPane.columnIndex="0">Run Directory</Label>
|
|
||||||
<Label GridPane.rowIndex="4" GridPane.columnIndex="0">Dimension</Label>
|
|
||||||
<Label GridPane.rowIndex="5" GridPane.columnIndex="0"> </Label>
|
|
||||||
|
|
||||||
<JFXTextField styleClass="fit-width" fx:id="txtGameDir" GridPane.rowIndex="0" GridPane.columnIndex="1"
|
|
||||||
maxWidth="Infinity"/>
|
|
||||||
<BorderPane GridPane.rowIndex="1" GridPane.columnIndex="1" GridPane.columnSpan="2">
|
|
||||||
<left>
|
|
||||||
<JFXTextField fx:id="txtMaxMemory" maxWidth="Infinity"/>
|
|
||||||
</left>
|
|
||||||
<right>
|
|
||||||
<Label>Physical Memory: 16000MB
|
|
||||||
<BorderPane.margin>
|
|
||||||
<Insets right="7"/>
|
|
||||||
</BorderPane.margin>
|
|
||||||
</Label>
|
|
||||||
</right>
|
|
||||||
</BorderPane>
|
|
||||||
<JFXComboBox fx:id="cboLauncherVisibility" GridPane.rowIndex="2" GridPane.columnIndex="1"
|
|
||||||
GridPane.columnSpan="2" maxWidth="Infinity">
|
|
||||||
<items>
|
|
||||||
<FXCollections fx:factory="observableArrayList">
|
|
||||||
<Label>Close</Label>
|
|
||||||
<Label>Hide</Label>
|
|
||||||
<Label>Keep</Label>
|
|
||||||
<Label>Hide and Reopen</Label>
|
|
||||||
</FXCollections>
|
|
||||||
</items>
|
|
||||||
</JFXComboBox>
|
|
||||||
<JFXComboBox fx:id="cboRunDirectory" GridPane.rowIndex="3" GridPane.columnIndex="1"
|
|
||||||
GridPane.columnSpan="2" maxWidth="Infinity">
|
|
||||||
<items>
|
|
||||||
<FXCollections fx:factory="observableArrayList">
|
|
||||||
<Label>Default(.minecraft/)</Label>
|
|
||||||
<Label>Divided(.minecraft/versions/<versionName>)</Label>
|
|
||||||
</FXCollections>
|
|
||||||
</items>
|
|
||||||
</JFXComboBox>
|
|
||||||
<BorderPane GridPane.rowIndex="4" GridPane.columnIndex="1" GridPane.columnSpan="2">
|
|
||||||
<left>
|
|
||||||
<HBox prefWidth="210">
|
|
||||||
<JFXTextField fx:id="txtWidth" promptText="800" prefWidth="100" />
|
|
||||||
<Label>x</Label>
|
|
||||||
<JFXTextField fx:id="txtHeight" promptText="480" prefWidth="100" />
|
|
||||||
</HBox>
|
|
||||||
</left>
|
|
||||||
<right>
|
|
||||||
<JFXCheckBox fx:id="chkFullscreen" text="Fullscreen" alignment="CENTER">
|
|
||||||
<BorderPane.margin>
|
|
||||||
<Insets right="7"/>
|
|
||||||
</BorderPane.margin>
|
|
||||||
</JFXCheckBox>
|
|
||||||
</right>
|
|
||||||
</BorderPane>
|
|
||||||
|
|
||||||
<JFXButton GridPane.rowIndex="0" GridPane.columnIndex="2" text="Explore" onMouseClicked="#onExploreJavaDir" />
|
|
||||||
</GridPane>
|
|
||||||
<HBox alignment="CENTER">
|
|
||||||
<JFXButton text="Show advanced settings" onMouseClicked="#onShowAdvanced" />
|
|
||||||
</HBox>
|
|
||||||
<VBox fx:id="advancedSettingsPane" spacing="30">
|
|
||||||
<JFXTextField labelFloat="true" promptText="JVM Args" styleClass="fit-width" fx:id="txtJVMArgs" prefWidth="${advancedSettingsPane.width}" />
|
|
||||||
<JFXTextField labelFloat="true" promptText="Game Args" styleClass="fit-width" fx:id="txtGameArgs" prefWidth="${advancedSettingsPane.width}" />
|
|
||||||
<JFXTextField labelFloat="true" promptText="Metaspace" styleClass="fit-width" fx:id="txtMetaspace" prefWidth="${advancedSettingsPane.width}" />
|
|
||||||
<JFXTextField labelFloat="true" promptText="Wrapper Launcher(like optirun)" styleClass="fit-width" fx:id="txtWrapper" prefWidth="${advancedSettingsPane.width}" />
|
|
||||||
<JFXTextField labelFloat="true" promptText="Pre-calling command" styleClass="fit-width" fx:id="txtPrecallingCommand" prefWidth="${advancedSettingsPane.width}" />
|
|
||||||
<JFXTextField labelFloat="true" promptText="Server IP" styleClass="fit-width" fx:id="txtServerIP" prefWidth="${advancedSettingsPane.width}" />
|
|
||||||
</VBox>
|
|
||||||
</VBox>
|
|
||||||
</ScrollPane>
|
|
||||||
</fx:root>
|
</fx:root>
|
||||||
|
|||||||
2
HMCL/src/main/resources/assets/svg/delete-black.fxml
Normal file
2
HMCL/src/main/resources/assets/svg/delete-black.fxml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<javafx.scene.shape.SVGPath fill="black" content="M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z" />
|
||||||
@@ -21,6 +21,12 @@ import org.jackhuang.hmcl.util.DigestUtils
|
|||||||
import java.net.Proxy
|
import java.net.Proxy
|
||||||
|
|
||||||
class OfflineAccount private constructor(val uuid: String, override val username: String): Account() {
|
class OfflineAccount private constructor(val uuid: String, override val username: String): Account() {
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (username.isBlank())
|
||||||
|
throw IllegalArgumentException("Username cannot be blank")
|
||||||
|
}
|
||||||
|
|
||||||
override fun logIn(proxy: Proxy): AuthInfo {
|
override fun logIn(proxy: Proxy): AuthInfo {
|
||||||
if (username.isBlank() || uuid.isBlank())
|
if (username.isBlank() || uuid.isBlank())
|
||||||
throw AuthenticationException("Username cannot be empty")
|
throw AuthenticationException("Username cannot be empty")
|
||||||
|
|||||||
@@ -38,19 +38,19 @@ class DefaultDependencyManager(override val repository: DefaultGameRepository, o
|
|||||||
return ParallelTask(*tasks)
|
return ParallelTask(*tasks)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun installLibraryAsync(version: Version, libraryId: String, libraryVersion: String): Task {
|
override fun installLibraryAsync(gameVersion: String, version: Version, libraryId: String, libraryVersion: String): Task {
|
||||||
if (libraryId == "forge")
|
if (libraryId == "forge")
|
||||||
return ForgeInstallTask(this, version, libraryVersion) then { task ->
|
return ForgeInstallTask(this, gameVersion, version, libraryVersion) then { task ->
|
||||||
val newVersion = task.result!!
|
val newVersion = task.result!!
|
||||||
VersionJSONSaveTask(this@DefaultDependencyManager, newVersion)
|
VersionJSONSaveTask(this@DefaultDependencyManager, newVersion)
|
||||||
}
|
}
|
||||||
else if (libraryId == "liteloader")
|
else if (libraryId == "liteloader")
|
||||||
return LiteLoaderInstallTask(this, version, libraryVersion) then { task ->
|
return LiteLoaderInstallTask(this, gameVersion, version, libraryVersion) then { task ->
|
||||||
val newVersion = task.result!!
|
val newVersion = task.result!!
|
||||||
VersionJSONSaveTask(this@DefaultDependencyManager, newVersion)
|
VersionJSONSaveTask(this@DefaultDependencyManager, newVersion)
|
||||||
}
|
}
|
||||||
else if (libraryId == "optifine")
|
else if (libraryId == "optifine")
|
||||||
return OptiFineInstallTask(this, version, libraryVersion) then { task ->
|
return OptiFineInstallTask(this, gameVersion, version, libraryVersion) then { task ->
|
||||||
val newVersion = task.result!!
|
val newVersion = task.result!!
|
||||||
VersionJSONSaveTask(this@DefaultDependencyManager, newVersion)
|
VersionJSONSaveTask(this@DefaultDependencyManager, newVersion)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,9 +27,10 @@ class DefaultGameBuilder(val dependencyManager: DefaultDependencyManager): GameB
|
|||||||
val downloadProvider = dependencyManager.downloadProvider
|
val downloadProvider = dependencyManager.downloadProvider
|
||||||
|
|
||||||
override fun buildAsync(): Task {
|
override fun buildAsync(): Task {
|
||||||
|
val gameVersion = gameVersion
|
||||||
return VersionJSONDownloadTask(gameVersion = gameVersion) then a@{ task ->
|
return VersionJSONDownloadTask(gameVersion = gameVersion) then a@{ task ->
|
||||||
var version = GSON.fromJson<Version>(task.result!!) ?: return@a null
|
var version = GSON.fromJson<Version>(task.result!!) ?: return@a null
|
||||||
version = version.copy(jar = version.id, id = name)
|
version = version.copy(id = name)
|
||||||
var result = ParallelTask(
|
var result = ParallelTask(
|
||||||
GameAssetDownloadTask(dependencyManager, version),
|
GameAssetDownloadTask(dependencyManager, version),
|
||||||
GameLoggingDownloadTask(dependencyManager, version),
|
GameLoggingDownloadTask(dependencyManager, version),
|
||||||
@@ -38,21 +39,21 @@ class DefaultGameBuilder(val dependencyManager: DefaultDependencyManager): GameB
|
|||||||
) then VersionJSONSaveTask(dependencyManager, version)
|
) then VersionJSONSaveTask(dependencyManager, version)
|
||||||
|
|
||||||
if (toolVersions.containsKey("forge"))
|
if (toolVersions.containsKey("forge"))
|
||||||
result = result then libraryTaskHelper(version, "forge")
|
result = result then libraryTaskHelper(gameVersion, version, "forge")
|
||||||
if (toolVersions.containsKey("liteloader"))
|
if (toolVersions.containsKey("liteloader"))
|
||||||
result = result then libraryTaskHelper(version, "liteloader")
|
result = result then libraryTaskHelper(gameVersion, version, "liteloader")
|
||||||
if (toolVersions.containsKey("optifine"))
|
if (toolVersions.containsKey("optifine"))
|
||||||
result = result then libraryTaskHelper(version, "optifine")
|
result = result then libraryTaskHelper(gameVersion, version, "optifine")
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun libraryTaskHelper(version: Version, libraryId: String): Task.(Task) -> Task = { prev ->
|
private fun libraryTaskHelper(gameVersion: String, version: Version, libraryId: String): Task.(Task) -> Task = { prev ->
|
||||||
var thisVersion = version
|
var thisVersion = version
|
||||||
if (prev is TaskResult<*> && prev.result is Version) {
|
if (prev is TaskResult<*> && prev.result is Version) {
|
||||||
thisVersion = prev.result as Version
|
thisVersion = prev.result as Version
|
||||||
}
|
}
|
||||||
dependencyManager.installLibraryAsync(thisVersion, libraryId, toolVersions[libraryId]!!)
|
dependencyManager.installLibraryAsync(gameVersion, thisVersion, libraryId, toolVersions[libraryId]!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class VersionJSONDownloadTask(val gameVersion: String): Task() {
|
inner class VersionJSONDownloadTask(val gameVersion: String): Task() {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ abstract class DependencyManager(open val repository: GameRepository) {
|
|||||||
*/
|
*/
|
||||||
abstract fun gameBuilder(): GameBuilder
|
abstract fun gameBuilder(): GameBuilder
|
||||||
|
|
||||||
abstract fun installLibraryAsync(version: Version, libraryId: String, libraryVersion: String): Task
|
abstract fun installLibraryAsync(gameVersion: String, version: Version, libraryId: String, libraryVersion: String): Task
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get registered version list.
|
* Get registered version list.
|
||||||
|
|||||||
@@ -30,8 +30,9 @@ import java.io.IOException
|
|||||||
import java.util.zip.ZipFile
|
import java.util.zip.ZipFile
|
||||||
|
|
||||||
class ForgeInstallTask(private val dependencyManager: DefaultDependencyManager,
|
class ForgeInstallTask(private val dependencyManager: DefaultDependencyManager,
|
||||||
private val version: Version,
|
private val gameVersion: String,
|
||||||
private val remoteVersion: String) : TaskResult<Version>() {
|
private val version: Version,
|
||||||
|
private val remoteVersion: String) : TaskResult<Version>() {
|
||||||
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<*>
|
||||||
@@ -39,15 +40,13 @@ class ForgeInstallTask(private val dependencyManager: DefaultDependencyManager,
|
|||||||
override val dependencies: MutableCollection<Task> = mutableListOf()
|
override val dependencies: MutableCollection<Task> = mutableListOf()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (version.jar == null)
|
|
||||||
throw IllegalArgumentException()
|
|
||||||
if (!forgeVersionList.loaded)
|
if (!forgeVersionList.loaded)
|
||||||
dependents += forgeVersionList.refreshAsync(dependencyManager.downloadProvider) then {
|
dependents += forgeVersionList.refreshAsync(dependencyManager.downloadProvider) then {
|
||||||
remote = forgeVersionList.getVersion(version.jar, remoteVersion) ?: throw IllegalArgumentException("Remote forge version ${version.jar}, $remoteVersion not found")
|
remote = forgeVersionList.getVersion(gameVersion, remoteVersion) ?: throw IllegalArgumentException("Remote forge version $gameVersion, $remoteVersion not found")
|
||||||
FileDownloadTask(remote.url.toURL(), installer)
|
FileDownloadTask(remote.url.toURL(), installer)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
remote = forgeVersionList.getVersion(version.jar, remoteVersion) ?: throw IllegalArgumentException("Remote forge version ${version.jar}, $remoteVersion not found")
|
remote = forgeVersionList.getVersion(gameVersion, remoteVersion) ?: throw IllegalArgumentException("Remote forge version $gameVersion, $remoteVersion not found")
|
||||||
dependents += FileDownloadTask(remote.url.toURL(), installer)
|
dependents += FileDownloadTask(remote.url.toURL(), installer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,23 +30,22 @@ import org.jackhuang.hmcl.util.merge
|
|||||||
* LiteLoader must be installed after Forge.
|
* LiteLoader must be installed after Forge.
|
||||||
*/
|
*/
|
||||||
class LiteLoaderInstallTask(private val dependencyManager: DefaultDependencyManager,
|
class LiteLoaderInstallTask(private val dependencyManager: DefaultDependencyManager,
|
||||||
private val version: Version,
|
private val gameVersion: String,
|
||||||
private val remoteVersion: String): TaskResult<Version>() {
|
private val version: 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: MutableCollection<Task> = mutableListOf()
|
||||||
override val dependencies: MutableCollection<Task> = mutableListOf()
|
override val dependencies: MutableCollection<Task> = mutableListOf()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (version.jar == null)
|
|
||||||
throw IllegalArgumentException()
|
|
||||||
if (!liteLoaderVersionList.loaded)
|
if (!liteLoaderVersionList.loaded)
|
||||||
dependents += LiteLoaderVersionList.refreshAsync(dependencyManager.downloadProvider) then {
|
dependents += LiteLoaderVersionList.refreshAsync(dependencyManager.downloadProvider) then {
|
||||||
remote = liteLoaderVersionList.getVersion(version.jar, remoteVersion) ?: throw IllegalArgumentException("Remote LiteLoader version ${version.jar}, $remoteVersion not found")
|
remote = liteLoaderVersionList.getVersion(gameVersion, remoteVersion) ?: throw IllegalArgumentException("Remote LiteLoader version $gameVersion, $remoteVersion not found")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
remote = liteLoaderVersionList.getVersion(version.jar, remoteVersion) ?: throw IllegalArgumentException("Remote LiteLoader version ${version.jar}, $remoteVersion not found")
|
remote = liteLoaderVersionList.getVersion(gameVersion, remoteVersion) ?: throw IllegalArgumentException("Remote LiteLoader version $gameVersion, $remoteVersion not found")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,23 +27,22 @@ import org.jackhuang.hmcl.task.then
|
|||||||
import org.jackhuang.hmcl.util.merge
|
import org.jackhuang.hmcl.util.merge
|
||||||
|
|
||||||
class OptiFineInstallTask(private val dependencyManager: DefaultDependencyManager,
|
class OptiFineInstallTask(private val dependencyManager: DefaultDependencyManager,
|
||||||
private val version: Version,
|
private val gameVersion: String,
|
||||||
private val remoteVersion: String): TaskResult<Version>() {
|
private val version: 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: MutableCollection<Task> = mutableListOf()
|
||||||
override val dependencies: MutableCollection<Task> = mutableListOf()
|
override val dependencies: MutableCollection<Task> = mutableListOf()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (version.jar == null)
|
|
||||||
throw IllegalArgumentException()
|
|
||||||
if (!optiFineVersionList.loaded)
|
if (!optiFineVersionList.loaded)
|
||||||
dependents += optiFineVersionList.refreshAsync(dependencyManager.downloadProvider) then {
|
dependents += optiFineVersionList.refreshAsync(dependencyManager.downloadProvider) then {
|
||||||
remote = optiFineVersionList.getVersion(version.jar, remoteVersion) ?: throw IllegalArgumentException("Remote LiteLoader version $remoteVersion not found")
|
remote = optiFineVersionList.getVersion(gameVersion, remoteVersion) ?: throw IllegalArgumentException("Remote OptiFine version $gameVersion-$remoteVersion not found")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
remote = optiFineVersionList.getVersion(version.jar, remoteVersion) ?: throw IllegalArgumentException("Remote LiteLoader version $remoteVersion not found")
|
remote = optiFineVersionList.getVersion(gameVersion, remoteVersion) ?: throw IllegalArgumentException("Remote OptiFine version $gameVersion-$remoteVersion not found")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ open class DefaultGameRepository(var baseDirectory: File): GameRepository {
|
|||||||
override fun getRunDirectory(id: String) = baseDirectory
|
override fun getRunDirectory(id: String) = baseDirectory
|
||||||
override fun getVersionJar(version: Version): File {
|
override fun getVersionJar(version: Version): File {
|
||||||
val v = version.resolve(this)
|
val v = version.resolve(this)
|
||||||
val id = v.id
|
val id = v.jar ?: v.id
|
||||||
return getVersionRoot(id).resolve("$id.jar")
|
return getVersionRoot(id).resolve("$id.jar")
|
||||||
}
|
}
|
||||||
override fun getNativeDirectory(id: String) = File(getVersionRoot(id), "$id-natives")
|
override fun getNativeDirectory(id: String) = File(getVersionRoot(id), "$id-natives")
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ open class DefaultLauncher(repository: GameRepository, versionId: String, accoun
|
|||||||
res.add(options.width.toString())
|
res.add(options.width.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.serverIp != null) {
|
if (options.serverIp != null && options.serverIp.isNotBlank()) {
|
||||||
val args = options.serverIp.split(":")
|
val args = options.serverIp.split(":")
|
||||||
res.add("--server")
|
res.add("--server")
|
||||||
res.add(args[0])
|
res.add(args[0])
|
||||||
|
|||||||
@@ -18,19 +18,34 @@
|
|||||||
package org.jackhuang.hmcl.mod
|
package org.jackhuang.hmcl.mod
|
||||||
|
|
||||||
import com.google.gson.JsonParseException
|
import com.google.gson.JsonParseException
|
||||||
|
import org.jackhuang.hmcl.util.property.ImmediateBooleanProperty
|
||||||
|
import org.jackhuang.hmcl.util.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class ModInfo (
|
class ModInfo (
|
||||||
val file: File,
|
var file: File,
|
||||||
val name: String,
|
val name: String,
|
||||||
val description: String = "",
|
val description: String = "",
|
||||||
val authors: String = "",
|
val authors: String = "unknown",
|
||||||
val version: String = "",
|
val version: String = "unknown",
|
||||||
val mcversion: String = "",
|
val mcversion: String = "unknown",
|
||||||
val url: String = ""
|
val url: String = ""
|
||||||
): Comparable<ModInfo> {
|
): Comparable<ModInfo> {
|
||||||
val isActive: Boolean
|
val activeProperty = object : ImmediateBooleanProperty(this, "active", file.extension != DISABLED_EXTENSION) {
|
||||||
get() = file.extension != DISABLED_EXTENSION
|
override fun invalidated() {
|
||||||
|
val f = file.absoluteFile
|
||||||
|
val newf: File
|
||||||
|
if (f.extension == DISABLED_EXTENSION)
|
||||||
|
newf = File(f.parentFile, f.nameWithoutExtension)
|
||||||
|
else
|
||||||
|
newf = File(f.parentFile, f.name + ".disabled")
|
||||||
|
if (f.renameTo(newf))
|
||||||
|
file = newf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@JvmName("activeProperty") get
|
||||||
|
|
||||||
|
var isActive: Boolean by activeProperty
|
||||||
|
|
||||||
val fileName: String = (if (isActive) file.name else file.nameWithoutExtension).substringBeforeLast(".")
|
val fileName: String = (if (isActive) file.name else file.nameWithoutExtension).substringBeforeLast(".")
|
||||||
|
|
||||||
@@ -53,22 +68,23 @@ class ModInfo (
|
|||||||
val file = if (modFile.extension == DISABLED_EXTENSION)
|
val file = if (modFile.extension == DISABLED_EXTENSION)
|
||||||
modFile.absoluteFile.parentFile.resolve(modFile.nameWithoutExtension)
|
modFile.absoluteFile.parentFile.resolve(modFile.nameWithoutExtension)
|
||||||
else modFile
|
else modFile
|
||||||
|
var description = "Unrecognized mod file"
|
||||||
if (file.extension == "zip" || file.extension == "jar")
|
if (file.extension == "zip" || file.extension == "jar")
|
||||||
try {
|
try {
|
||||||
return ForgeModMetadata.fromFile(modFile)
|
return ForgeModMetadata.fromFile(modFile)
|
||||||
} catch (e: JsonParseException) {
|
} catch (ignore: Exception) {
|
||||||
throw e
|
description = "May be Forge mod"
|
||||||
} catch (ignore: Exception) {}
|
}
|
||||||
|
|
||||||
else if (file.extension == "litemod")
|
else if (file.extension == "litemod")
|
||||||
try {
|
try {
|
||||||
return LiteModMetadata.fromFile(modFile)
|
return LiteModMetadata.fromFile(modFile)
|
||||||
} catch (e: JsonParseException) {
|
} catch (ignore: Exception) {
|
||||||
throw e
|
description = "May be LiteLoader mod"
|
||||||
} catch (ignore: Exception) {}
|
}
|
||||||
else throw IllegalArgumentException("File $modFile is not mod")
|
else throw IllegalArgumentException("File $modFile is not mod")
|
||||||
|
|
||||||
return ModInfo(file = modFile, name = modFile.nameWithoutExtension, description = "Unrecognized mod file")
|
return ModInfo(file = modFile, name = modFile.nameWithoutExtension, description = description)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,11 +46,13 @@ enum class OS {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val TOTAL_MEMORY: Long by lazy {
|
val TOTAL_MEMORY: Long by lazy {
|
||||||
ReflectionHelper.get<Long>(ManagementFactory.getOperatingSystemMXBean(), "getTotalPhysicalMemorySize") ?: 1024L
|
val bytes = ReflectionHelper.invoke<Long>(ManagementFactory.getOperatingSystemMXBean(), "getTotalPhysicalMemorySize")
|
||||||
|
if (bytes == null) 1024
|
||||||
|
else bytes / 1024 / 1024
|
||||||
}
|
}
|
||||||
|
|
||||||
val SUGGESTED_MEMORY: Int by lazy {
|
val SUGGESTED_MEMORY: Int by lazy {
|
||||||
val memory = TOTAL_MEMORY / 1024 / 1024 / 4
|
val memory = TOTAL_MEMORY / 4
|
||||||
(Math.round(1.0 * memory / 128.0) * 128).toInt()
|
(Math.round(1.0 * memory / 128.0) * 128).toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,173 @@
|
|||||||
|
/*
|
||||||
|
* 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.property
|
||||||
|
|
||||||
|
import javafx.beans.property.*
|
||||||
|
import javafx.beans.value.ObservableValue
|
||||||
|
|
||||||
|
open class ImmediateStringProperty(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) {
|
||||||
|
super.set(newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bind(newObservable: ObservableValue<out String>) {
|
||||||
|
super.get()
|
||||||
|
super.bind(newObservable)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun unbind() {
|
||||||
|
super.get()
|
||||||
|
super.unbind()
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun fireValueChangedEvent() {
|
||||||
|
super.fireValueChangedEvent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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?) {
|
||||||
|
super.set(newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bind(newObservable: ObservableValue<out String?>) {
|
||||||
|
super.get()
|
||||||
|
super.bind(newObservable)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun unbind() {
|
||||||
|
super.get()
|
||||||
|
super.unbind()
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun fireValueChangedEvent() {
|
||||||
|
super.fireValueChangedEvent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open class ImmediateBooleanProperty(bean: Any, name: String, initialValue: Boolean): SimpleBooleanProperty(bean, name, initialValue) {
|
||||||
|
|
||||||
|
override fun set(newValue: Boolean) {
|
||||||
|
super.get()
|
||||||
|
super.set(newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun superSet(newValue: Boolean) {
|
||||||
|
super.set(newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bind(rawObservable: ObservableValue<out Boolean>?) {
|
||||||
|
super.get()
|
||||||
|
super.bind(rawObservable)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun unbind() {
|
||||||
|
super.get()
|
||||||
|
super.unbind()
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun fireValueChangedEvent() {
|
||||||
|
super.fireValueChangedEvent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open class ImmediateIntegerProperty(bean: Any, name: String, initialValue: Int): SimpleIntegerProperty(bean, name, initialValue) {
|
||||||
|
|
||||||
|
override fun set(newValue: Int) {
|
||||||
|
super.get()
|
||||||
|
super.set(newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun superSet(newValue: Int) {
|
||||||
|
super.set(newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bind(rawObservable: ObservableValue<out Number>) {
|
||||||
|
super.get()
|
||||||
|
super.bind(rawObservable)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun unbind() {
|
||||||
|
super.get()
|
||||||
|
super.unbind()
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun fireValueChangedEvent() {
|
||||||
|
super.fireValueChangedEvent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open class ImmediateDoubleProperty(bean: Any, name: String, initialValue: Double): SimpleDoubleProperty(bean, name, initialValue) {
|
||||||
|
|
||||||
|
override fun set(newValue: Double) {
|
||||||
|
super.get()
|
||||||
|
super.set(newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun superSet(newValue: Double) {
|
||||||
|
super.set(newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bind(rawObservable: ObservableValue<out Number>) {
|
||||||
|
super.get()
|
||||||
|
super.bind(rawObservable)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun unbind() {
|
||||||
|
super.get()
|
||||||
|
super.unbind()
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun fireValueChangedEvent() {
|
||||||
|
super.fireValueChangedEvent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open class ImmediateObjectProperty<T>(bean: Any, name: String, initialValue: T): SimpleObjectProperty<T>(bean, name, initialValue) {
|
||||||
|
|
||||||
|
override fun set(newValue: T) {
|
||||||
|
super.get()
|
||||||
|
super.set(newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bind(rawObservable: ObservableValue<out T>) {
|
||||||
|
super.get()
|
||||||
|
super.bind(rawObservable)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun unbind() {
|
||||||
|
super.get()
|
||||||
|
super.unbind()
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun fireValueChangedEvent() {
|
||||||
|
super.fireValueChangedEvent()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ import org.jackhuang.hmcl.download.MojangDownloadProvider
|
|||||||
import org.jackhuang.hmcl.game.DefaultGameRepository
|
import org.jackhuang.hmcl.game.DefaultGameRepository
|
||||||
import org.jackhuang.hmcl.launch.DefaultLauncher
|
import org.jackhuang.hmcl.launch.DefaultLauncher
|
||||||
import org.jackhuang.hmcl.game.LaunchOptions
|
import org.jackhuang.hmcl.game.LaunchOptions
|
||||||
|
import org.jackhuang.hmcl.game.minecraftVersion
|
||||||
import org.jackhuang.hmcl.launch.ProcessListener
|
import org.jackhuang.hmcl.launch.ProcessListener
|
||||||
import org.jackhuang.hmcl.util.makeCommand
|
import org.jackhuang.hmcl.util.makeCommand
|
||||||
import org.jackhuang.hmcl.task.Task
|
import org.jackhuang.hmcl.task.Task
|
||||||
@@ -115,10 +116,11 @@ class Test {
|
|||||||
fun installForge() {
|
fun installForge() {
|
||||||
val thread = Thread.currentThread()
|
val thread = Thread.currentThread()
|
||||||
val version = repository.getVersion("test").resolve(repository)
|
val version = repository.getVersion("test").resolve(repository)
|
||||||
|
val minecraftVersion = minecraftVersion(repository.getVersionJar(version)) ?: ""
|
||||||
// optifine HD_U_C4
|
// optifine HD_U_C4
|
||||||
// forge 14.21.1.2426
|
// forge 14.21.1.2426
|
||||||
// liteloader 1.12-SNAPSHOT-4
|
// liteloader 1.12-SNAPSHOT-4
|
||||||
dependency.installLibraryAsync(version, "liteloader", "1.12-SNAPSHOT-4").executor().apply {
|
dependency.installLibraryAsync(minecraftVersion, version, "liteloader", "1.12-SNAPSHOT-4").executor().apply {
|
||||||
taskListener = taskListener(thread)
|
taskListener = taskListener(thread)
|
||||||
}.start()
|
}.start()
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user