emmmmm
This commit is contained in:
@@ -19,41 +19,44 @@ package org.jackhuang.hmcl.setting
|
||||
|
||||
import com.google.gson.*
|
||||
import javafx.beans.InvalidationListener
|
||||
import javafx.beans.property.*
|
||||
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager
|
||||
import org.jackhuang.hmcl.download.DependencyManager
|
||||
import org.jackhuang.hmcl.game.HMCLGameRepository
|
||||
import org.jackhuang.hmcl.mod.ModManager
|
||||
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.lang.reflect.Type
|
||||
|
||||
class Profile(var name: String = "Default", gameDir: File = File(".minecraft")) {
|
||||
val globalProperty = SimpleObjectProperty<VersionSetting>(this, "global", VersionSetting())
|
||||
class Profile(var name: String = "Default", initialGameDir: File = File(".minecraft"), initialSelectedVersion: String = "") {
|
||||
val globalProperty = ImmediateObjectProperty<VersionSetting>(this, "global", VersionSetting())
|
||||
var global: VersionSetting by globalProperty
|
||||
|
||||
val selectedVersionProperty = SimpleStringProperty(this, "selectedVersion", "")
|
||||
val selectedVersionProperty = ImmediateStringProperty(this, "selectedVersion", initialSelectedVersion)
|
||||
var selectedVersion: String by selectedVersionProperty
|
||||
|
||||
val gameDirProperty = SimpleObjectProperty<File>(this, "gameDir", gameDir)
|
||||
val gameDirProperty = ImmediateObjectProperty<File>(this, "gameDir", initialGameDir)
|
||||
var gameDir: File by gameDirProperty
|
||||
|
||||
val noCommonProperty = SimpleBooleanProperty(this, "noCommon", false)
|
||||
val noCommonProperty = ImmediateBooleanProperty(this, "noCommon", false)
|
||||
var noCommon: Boolean by noCommonProperty
|
||||
|
||||
var repository = HMCLGameRepository(gameDir)
|
||||
var repository = HMCLGameRepository(initialGameDir)
|
||||
var dependency = DefaultDependencyManager(repository, BMCLAPIDownloadProvider)
|
||||
var modManager = ModManager(repository)
|
||||
|
||||
init {
|
||||
gameDirProperty.addListener { _, _, newValue ->
|
||||
repository.baseDirectory = newValue
|
||||
gameDirProperty.addListener { _ ->
|
||||
repository.baseDirectory = gameDir
|
||||
repository.refreshVersions()
|
||||
}
|
||||
|
||||
selectedVersionProperty.addListener { _, _, newValue ->
|
||||
if (newValue.isNotBlank() && !repository.hasVersion(newValue)) {
|
||||
selectedVersionProperty.addListener { _ ->
|
||||
if (selectedVersion.isNotBlank() && !repository.hasVersion(selectedVersion)) {
|
||||
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 ?: ""
|
||||
}
|
||||
}
|
||||
@@ -110,9 +113,8 @@ class Profile(var name: String = "Default", gameDir: File = File(".minecraft"))
|
||||
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext): Profile? {
|
||||
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)
|
||||
selectedVersion = json["selectedVersion"]?.asString ?: ""
|
||||
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.DownloadProvider
|
||||
import org.jackhuang.hmcl.download.MojangDownloadProvider
|
||||
import org.jackhuang.hmcl.util.GSON
|
||||
import org.jackhuang.hmcl.util.LOG
|
||||
import java.io.File
|
||||
import java.util.logging.Level
|
||||
import org.jackhuang.hmcl.ProfileLoadingEvent
|
||||
import org.jackhuang.hmcl.ProfileChangedEvent
|
||||
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.yggdrasil.YggdrasilAccount
|
||||
import org.jackhuang.hmcl.event.EVENT_BUS
|
||||
import org.jackhuang.hmcl.util.FileTypeAdapter
|
||||
import org.jackhuang.hmcl.util.ignoreException
|
||||
import org.jackhuang.hmcl.util.property.ImmediateObjectProperty
|
||||
import java.net.Proxy
|
||||
import java.util.*
|
||||
|
||||
|
||||
object Settings {
|
||||
val GSON = GsonBuilder()
|
||||
.registerTypeAdapter(VersionSetting::class.java, VersionSetting)
|
||||
@@ -53,7 +50,7 @@ object Settings {
|
||||
|
||||
val SETTINGS_FILE = File("hmcl.json").absoluteFile
|
||||
|
||||
val SETTINGS: Config
|
||||
private val SETTINGS: Config
|
||||
|
||||
private val ACCOUNTS = mutableMapOf<String, Account>()
|
||||
|
||||
@@ -82,10 +79,10 @@ object Settings {
|
||||
|
||||
save()
|
||||
|
||||
if (!getProfiles().containsKey(DEFAULT_PROFILE))
|
||||
getProfiles().put(DEFAULT_PROFILE, Profile());
|
||||
if (!getProfileMap().containsKey(DEFAULT_PROFILE))
|
||||
getProfileMap().put(DEFAULT_PROFILE, Profile());
|
||||
|
||||
for ((name, profile) in getProfiles().entries) {
|
||||
for ((name, profile) in getProfileMap().entries) {
|
||||
profile.name = name
|
||||
profile.addPropertyChangedListener(InvalidationListener { save() })
|
||||
}
|
||||
@@ -151,23 +148,35 @@ object Settings {
|
||||
return getProfile(SETTINGS.selectedProfile)
|
||||
}
|
||||
|
||||
val selectedAccount: Account?
|
||||
get() {
|
||||
val a = getAccount(SETTINGS.selectedAccount)
|
||||
if (a == null && ACCOUNTS.isNotEmpty()) {
|
||||
val (key, acc) = ACCOUNTS.entries.first()
|
||||
SETTINGS.selectedAccount = key
|
||||
val selectedAccountProperty = object : ImmediateObjectProperty<Account?>(this, "selectedAccount", getAccount(SETTINGS.selectedAccount)) {
|
||||
override fun get(): Account? {
|
||||
val a = super.get()
|
||||
if (a == null || !ACCOUNTS.containsKey(a.username)) {
|
||||
val acc = if (ACCOUNTS.isEmpty()) null else ACCOUNTS.values.first()
|
||||
set(acc)
|
||||
return acc
|
||||
}
|
||||
return a
|
||||
} else return a
|
||||
}
|
||||
|
||||
fun setSelectedAccount(name: String) {
|
||||
if (ACCOUNTS.containsKey(name))
|
||||
SETTINGS.selectedAccount = name
|
||||
override fun set(newValue: Account?) {
|
||||
if (newValue == null || ACCOUNTS.containsKey(newValue.username)) {
|
||||
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_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) {
|
||||
ACCOUNTS[account.username] = account
|
||||
@@ -183,36 +192,38 @@ object Settings {
|
||||
|
||||
fun deleteAccount(name: String) {
|
||||
ACCOUNTS.remove(name)
|
||||
|
||||
selectedAccountProperty.get()
|
||||
}
|
||||
|
||||
fun getProfile(name: String?): Profile {
|
||||
var p: Profile? = getProfiles()[name ?: DEFAULT_PROFILE]
|
||||
var p: Profile? = getProfileMap()[name ?: DEFAULT_PROFILE]
|
||||
if (p == null)
|
||||
if (getProfiles().containsKey(DEFAULT_PROFILE))
|
||||
p = getProfiles()[DEFAULT_PROFILE]!!
|
||||
if (getProfileMap().containsKey(DEFAULT_PROFILE))
|
||||
p = getProfileMap()[DEFAULT_PROFILE]!!
|
||||
else {
|
||||
p = Profile()
|
||||
getProfiles().put(DEFAULT_PROFILE, p)
|
||||
getProfileMap().put(DEFAULT_PROFILE, p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
fun getProfilesFiltered(): Collection<Profile> {
|
||||
return getProfiles().values.filter { t -> t.name.isNotBlank() }
|
||||
fun getProfiles(): Collection<Profile> {
|
||||
return getProfileMap().values.filter { t -> t.name.isNotBlank() }
|
||||
}
|
||||
|
||||
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
|
||||
getProfiles().put(ver.name, ver)
|
||||
getProfileMap().put(ver.name, ver)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -227,15 +238,15 @@ object Settings {
|
||||
var notify = false
|
||||
if (selectedProfile.name == ver)
|
||||
notify = true
|
||||
val flag = getProfiles().remove(ver) != null
|
||||
val flag = getProfileMap().remove(ver) != null
|
||||
if (notify && flag)
|
||||
onProfileChanged()
|
||||
return flag
|
||||
}
|
||||
|
||||
internal fun onProfileChanged() {
|
||||
EVENT_BUS.fireEvent(ProfileChangedEvent(SETTINGS, selectedProfile))
|
||||
selectedProfile.repository.refreshVersions()
|
||||
EVENT_BUS.fireEvent(ProfileChangedEvent(SETTINGS, selectedProfile))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,10 +19,10 @@ package org.jackhuang.hmcl.setting
|
||||
|
||||
import com.google.gson.*
|
||||
import javafx.beans.InvalidationListener
|
||||
import javafx.beans.property.*
|
||||
import org.jackhuang.hmcl.MainApplication
|
||||
import org.jackhuang.hmcl.game.LaunchOptions
|
||||
import org.jackhuang.hmcl.util.*
|
||||
import org.jackhuang.hmcl.util.property.*
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
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.
|
||||
*/
|
||||
val usesGlobalProperty = SimpleBooleanProperty(this, "usesGlobal", false)
|
||||
val usesGlobalProperty = ImmediateBooleanProperty(this, "usesGlobal", false)
|
||||
var usesGlobal: Boolean by usesGlobalProperty
|
||||
|
||||
// java
|
||||
@@ -47,39 +47,39 @@ class VersionSetting() {
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* The command to launch java, i.e. optirun.
|
||||
*/
|
||||
val wrapperProperty = SimpleStringProperty(this, "wrapper", "")
|
||||
val wrapperProperty = ImmediateStringProperty(this, "wrapper", "")
|
||||
var wrapper: String by wrapperProperty
|
||||
|
||||
/**
|
||||
* The permanent generation size of JVM garbage collection.
|
||||
*/
|
||||
val permSizeProperty = SimpleStringProperty(this, "permSize", "")
|
||||
val permSizeProperty = ImmediateStringProperty(this, "permSize", "")
|
||||
var permSize: String by permSizeProperty
|
||||
|
||||
/**
|
||||
* The maximum memory that JVM can allocate.
|
||||
* 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
|
||||
|
||||
/**
|
||||
* The command that will be executed before launching the Minecraft.
|
||||
* Operating system relevant.
|
||||
*/
|
||||
val precalledCommandProperty = SimpleStringProperty(this, "precalledCommand", "")
|
||||
val precalledCommandProperty = ImmediateStringProperty(this, "precalledCommand", "")
|
||||
var precalledCommand: String by precalledCommandProperty
|
||||
|
||||
// options
|
||||
@@ -87,25 +87,25 @@ class VersionSetting() {
|
||||
/**
|
||||
* The user customized arguments passed to JVM.
|
||||
*/
|
||||
val javaArgsProperty = SimpleStringProperty(this, "javaArgs", "")
|
||||
val javaArgsProperty = ImmediateStringProperty(this, "javaArgs", "")
|
||||
var javaArgs: String by javaArgsProperty
|
||||
|
||||
/**
|
||||
* The user customized arguments passed to Minecraft.
|
||||
*/
|
||||
val minecraftArgsProperty = SimpleStringProperty(this, "minecraftArgs", "")
|
||||
val minecraftArgsProperty = ImmediateStringProperty(this, "minecraftArgs", "")
|
||||
var minecraftArgs: String by minecraftArgsProperty
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
// Minecraft settings.
|
||||
@@ -115,13 +115,13 @@ class VersionSetting() {
|
||||
*
|
||||
* Format: ip:port or without port.
|
||||
*/
|
||||
val serverIpProperty = SimpleStringProperty(this, "serverIp", "")
|
||||
val serverIpProperty = ImmediateStringProperty(this, "serverIp", "")
|
||||
var serverIp: String by serverIpProperty
|
||||
|
||||
/**
|
||||
* True if Minecraft started in fullscreen mode.
|
||||
*/
|
||||
val fullscreenProperty = SimpleBooleanProperty(this, "fullscreen", false)
|
||||
val fullscreenProperty = ImmediateBooleanProperty(this, "fullscreen", false)
|
||||
var fullscreen: Boolean by fullscreenProperty
|
||||
|
||||
/**
|
||||
@@ -131,7 +131,7 @@ class VersionSetting() {
|
||||
* String type prevents unexpected value from causing JsonSyntaxException.
|
||||
* 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
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ class VersionSetting() {
|
||||
* String type prevents unexpected value from causing JsonSyntaxException.
|
||||
* 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
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ class VersionSetting() {
|
||||
* 0 - .minecraft<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
|
||||
|
||||
// launcher settings
|
||||
@@ -160,7 +160,7 @@ class VersionSetting() {
|
||||
* 1 - Hide the launcher when the game starts.<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
|
||||
|
||||
fun addPropertyChangedListener(listener: InvalidationListener) {
|
||||
@@ -200,10 +200,10 @@ class VersionSetting() {
|
||||
fullscreen = fullscreen,
|
||||
serverIp = serverIp,
|
||||
wrapper = wrapper,
|
||||
proxyHost = Settings.SETTINGS.proxyHost,
|
||||
proxyPort = Settings.SETTINGS.proxyPort,
|
||||
proxyUser = Settings.SETTINGS.proxyUserName,
|
||||
proxyPass = Settings.SETTINGS.proxyPassword,
|
||||
proxyHost = Settings.PROXY_HOST,
|
||||
proxyPort = Settings.PROXY_PORT,
|
||||
proxyUser = Settings.PROXY_USER,
|
||||
proxyPass = Settings.PROXY_PASS,
|
||||
precalledCommand = precalledCommand,
|
||||
noGeneratedJVMArgs = noJVMArgs
|
||||
)
|
||||
@@ -217,7 +217,7 @@ class VersionSetting() {
|
||||
addProperty("usesGlobal", src.usesGlobal)
|
||||
addProperty("javaArgs", src.javaArgs)
|
||||
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("width", src.width)
|
||||
addProperty("height", src.height)
|
||||
@@ -239,11 +239,14 @@ class VersionSetting() {
|
||||
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): VersionSetting? {
|
||||
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 {
|
||||
usesGlobal = json["usesGlobal"]?.asBoolean ?: false
|
||||
javaArgs = json["javaArgs"]?.asString ?: ""
|
||||
minecraftArgs = json["minecraftArgs"]?.asString ?: ""
|
||||
maxMemory = parseJsonPrimitive(json["maxMemory"]?.asJsonPrimitive)
|
||||
maxMemory = maxMemoryN
|
||||
permSize = json["permSize"]?.asString ?: ""
|
||||
width = parseJsonPrimitive(json["width"]?.asJsonPrimitive)
|
||||
height = parseJsonPrimitive(json["height"]?.asJsonPrimitive)
|
||||
|
||||
@@ -18,38 +18,36 @@
|
||||
package org.jackhuang.hmcl.ui
|
||||
|
||||
import com.jfoenix.controls.JFXButton
|
||||
import com.jfoenix.controls.JFXCheckBox
|
||||
import com.jfoenix.controls.JFXRadioButton
|
||||
import com.jfoenix.effects.JFXDepthManager
|
||||
import javafx.beans.binding.Bindings
|
||||
import javafx.fxml.FXML
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.control.ToggleGroup
|
||||
import javafx.scene.layout.Pane
|
||||
import javafx.scene.layout.StackPane
|
||||
import javafx.scene.layout.VBox
|
||||
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 content: VBox
|
||||
@FXML lateinit var header: StackPane
|
||||
@FXML lateinit var body: StackPane
|
||||
@FXML lateinit var btnDelete: JFXButton
|
||||
@FXML lateinit var btnEdit: JFXButton
|
||||
@FXML lateinit var lblUser: Label
|
||||
@FXML lateinit var chkSelected: JFXRadioButton
|
||||
@FXML lateinit var lblType: Label
|
||||
|
||||
init {
|
||||
loadFXML("/assets/fxml/account-item.fxml")
|
||||
|
||||
minWidth = width
|
||||
maxWidth = width
|
||||
prefWidth = width
|
||||
|
||||
minHeight = height
|
||||
maxHeight = height
|
||||
prefHeight = height
|
||||
|
||||
JFXDepthManager.setDepth(this, 1)
|
||||
|
||||
chkSelected.toggleGroup = group
|
||||
btnDelete.graphic = SVG.delete("white", 15.0, 15.0)
|
||||
|
||||
// create content
|
||||
val headerColor = getDefaultColor(i % 12)
|
||||
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.beans.property.SimpleStringProperty
|
||||
import javafx.beans.property.StringProperty
|
||||
import javafx.beans.value.ChangeListener
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.control.ToggleGroup
|
||||
import org.jackhuang.hmcl.auth.Account
|
||||
import org.jackhuang.hmcl.auth.OfflineAccount
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount
|
||||
import org.jackhuang.hmcl.setting.Settings
|
||||
import org.jackhuang.hmcl.task.Scheduler
|
||||
import org.jackhuang.hmcl.task.with
|
||||
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
|
||||
|
||||
class AccountsPage : StackPane(), HasTitle {
|
||||
class AccountsPage() : StackPane(), DecoratorPage {
|
||||
override val titleProperty: StringProperty = SimpleStringProperty(this, "title", "Accounts")
|
||||
|
||||
@FXML lateinit var scrollPane: ScrollPane
|
||||
@@ -48,12 +49,30 @@ class AccountsPage : StackPane(), HasTitle {
|
||||
@FXML lateinit var lblCreationWarning: Label
|
||||
@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 {
|
||||
loadFXML("/assets/fxml/account.fxml")
|
||||
children.remove(dialog)
|
||||
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 ->
|
||||
val visible = newValue != 0
|
||||
@@ -62,27 +81,42 @@ class AccountsPage : StackPane(), HasTitle {
|
||||
}
|
||||
cboType.selectionModel.select(0)
|
||||
|
||||
Settings.selectedAccountProperty.addListener(listener)
|
||||
|
||||
loadAccounts()
|
||||
|
||||
if (Settings.getAccounts().isEmpty())
|
||||
addNewAccount()
|
||||
}
|
||||
|
||||
override fun onClose() {
|
||||
Settings.selectedAccountProperty.removeListener(listener)
|
||||
}
|
||||
|
||||
fun loadAccounts() {
|
||||
val children = mutableListOf<Node>()
|
||||
var i = 0
|
||||
val group = ToggleGroup()
|
||||
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)
|
||||
Platform.runLater { scrollPane.requestLayout() }
|
||||
Platform.runLater {
|
||||
masonryPane.requestLayout()
|
||||
scrollPane.requestLayout()
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildNode(i: Int, account: Account): Node {
|
||||
return AccountItem(i, Math.random() * 100 + 100, Math.random() * 100 + 100).apply {
|
||||
private fun buildNode(i: Int, account: Account, group: ToggleGroup): Node {
|
||||
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
|
||||
lblType.text = when(account) {
|
||||
is OfflineAccount -> "Offline Account"
|
||||
is YggdrasilAccount -> "Yggdrasil Account"
|
||||
else -> throw Error("Unsupported account: $account")
|
||||
}
|
||||
lblType.text = accountType(account)
|
||||
btnDelete.setOnMouseClicked {
|
||||
Settings.deleteAccount(account.username)
|
||||
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() {
|
||||
dialog.show()
|
||||
}
|
||||
@@ -149,4 +161,11 @@ class AccountsPage : StackPane(), HasTitle {
|
||||
fun onCreationCancel() {
|
||||
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) {
|
||||
this.stage = stage
|
||||
|
||||
decorator = Decorator(stage, max = false)
|
||||
decorator.mainPage = mainPane
|
||||
decorator = Decorator(stage, mainPane, max = false)
|
||||
decorator.showPage(null)
|
||||
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
|
||||
|
||||
scene = Scene(decorator, 800.0, 480.0)
|
||||
|
||||
@@ -37,12 +37,9 @@ import javafx.scene.control.Tooltip
|
||||
import javafx.scene.input.MouseEvent
|
||||
import javafx.scene.layout.*
|
||||
import javafx.scene.paint.Color
|
||||
import javafx.scene.shape.Rectangle
|
||||
import javafx.stage.Screen
|
||||
import javafx.stage.Stage
|
||||
import javafx.stage.StageStyle
|
||||
import javafx.scene.layout.BorderStrokeStyle
|
||||
import javafx.scene.layout.BorderStroke
|
||||
import org.jackhuang.hmcl.MainApplication
|
||||
import org.jackhuang.hmcl.ui.animation.AnimationProducer
|
||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations
|
||||
@@ -52,7 +49,7 @@ import org.jackhuang.hmcl.util.*
|
||||
import java.util.*
|
||||
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)
|
||||
|
||||
private var xOffset: Double = 0.0
|
||||
@@ -370,16 +367,28 @@ class Decorator @JvmOverloads constructor(private val primaryStage: Stage, priva
|
||||
if (content is WizardPage)
|
||||
titleLabel.text = prefix + content.title
|
||||
|
||||
if (content is HasTitle)
|
||||
if (content is DecoratorPage)
|
||||
titleLabel.textProperty().bind(content.titleProperty)
|
||||
}
|
||||
|
||||
lateinit var mainPage: Node
|
||||
var category: String? = null
|
||||
var nowPage: Node? = null
|
||||
|
||||
fun showPage(content: Node?) {
|
||||
val c = content ?: mainPage
|
||||
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) {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package org.jackhuang.hmcl.ui
|
||||
|
||||
import com.jfoenix.concurrency.JFXUtilities
|
||||
import com.jfoenix.controls.JFXScrollPane
|
||||
import javafx.animation.Animation
|
||||
import javafx.animation.KeyFrame
|
||||
import javafx.animation.Timeline
|
||||
@@ -29,10 +30,10 @@ import javafx.scene.Parent
|
||||
import javafx.scene.Scene
|
||||
import javafx.scene.control.ListView
|
||||
import javafx.scene.control.ScrollBar
|
||||
import javafx.scene.control.ScrollPane
|
||||
import javafx.scene.image.WritableImage
|
||||
import javafx.scene.input.MouseEvent
|
||||
import javafx.scene.input.ScrollEvent
|
||||
import javafx.scene.layout.Pane
|
||||
import javafx.scene.layout.Region
|
||||
import javafx.scene.shape.Rectangle
|
||||
import javafx.util.Duration
|
||||
@@ -90,6 +91,8 @@ fun ListView<*>.smoothScrolling() {
|
||||
}
|
||||
}
|
||||
|
||||
fun ScrollPane.smoothScrolling() = JFXScrollPane.smoothScrolling(this)
|
||||
|
||||
fun runOnUiThread(runnable: () -> Unit) = {
|
||||
JFXUtilities.runInFX(runnable)
|
||||
}
|
||||
@@ -109,6 +112,5 @@ fun setOverflowHidden(node: Region) {
|
||||
|
||||
val stylesheets = arrayOf(
|
||||
Controllers::class.java.getResource("/css/jfoenix-fonts.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("/css/jfoenix-design.css").toExternalForm(),
|
||||
Controllers::class.java.getResource("/assets/css/jfoenix-main-demo.css").toExternalForm())
|
||||
|
||||
@@ -18,11 +18,14 @@
|
||||
package org.jackhuang.hmcl.ui
|
||||
|
||||
import com.jfoenix.controls.JFXComboBox
|
||||
import javafx.beans.property.StringProperty
|
||||
import javafx.beans.value.ChangeListener
|
||||
import javafx.scene.Node
|
||||
import javafx.scene.layout.*
|
||||
import javafx.scene.paint.Paint
|
||||
import org.jackhuang.hmcl.ProfileChangedEvent
|
||||
import org.jackhuang.hmcl.ProfileLoadingEvent
|
||||
import org.jackhuang.hmcl.auth.Account
|
||||
import org.jackhuang.hmcl.event.EVENT_BUS
|
||||
import org.jackhuang.hmcl.event.RefreshedVersionsEvent
|
||||
import org.jackhuang.hmcl.game.LauncherHelper
|
||||
@@ -62,6 +65,21 @@ class LeftPaneController(val leftPane: VBox) {
|
||||
Settings.selectedProfile.repository.refreshVersions()
|
||||
}
|
||||
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) {
|
||||
@@ -81,13 +99,10 @@ class LeftPaneController(val leftPane: VBox) {
|
||||
|
||||
fun onProfileChanged(event: ProfileChangedEvent) {
|
||||
val profile = event.value
|
||||
profile.selectedVersionProperty.addListener { _, _, newValue ->
|
||||
versionChanged(newValue)
|
||||
profile.selectedVersionProperty.addListener { observable ->
|
||||
versionChanged(profile.selectedVersion)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadAccounts() {
|
||||
|
||||
profile.selectedVersionProperty.fireValueChangedEvent()
|
||||
}
|
||||
|
||||
private fun loadVersions() {
|
||||
@@ -98,7 +113,7 @@ class LeftPaneController(val leftPane: VBox) {
|
||||
val ripplerContainer = RipplerContainer(item)
|
||||
item.onSettingsButtonClicked {
|
||||
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.setOnMouseClicked {
|
||||
|
||||
@@ -18,34 +18,16 @@
|
||||
package org.jackhuang.hmcl.ui
|
||||
|
||||
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.StringProperty
|
||||
import javafx.collections.FXCollections
|
||||
import javafx.fxml.FXML
|
||||
import javafx.scene.Node
|
||||
import javafx.scene.layout.BorderPane
|
||||
import javafx.scene.layout.Pane
|
||||
import javafx.scene.layout.StackPane
|
||||
import org.jackhuang.hmcl.ProfileChangedEvent
|
||||
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
|
||||
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
||||
|
||||
/**
|
||||
* @see /assets/fxml/main.fxml
|
||||
*/
|
||||
class MainPage : BorderPane(), HasTitle {
|
||||
class MainPage : StackPane(), DecoratorPage {
|
||||
override val titleProperty: StringProperty = SimpleStringProperty(this, "title", "Main Page")
|
||||
|
||||
@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 {
|
||||
styleClass += "rippler-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)
|
||||
setOnMousePressed { e ->
|
||||
setOnMousePressed {
|
||||
if (this.clickedAnimation != null) {
|
||||
this.clickedAnimation!!.rate = 1.0
|
||||
this.clickedAnimation!!.play()
|
||||
}
|
||||
|
||||
}
|
||||
setOnMouseReleased { e ->
|
||||
setOnMouseReleased {
|
||||
if (this.clickedAnimation != null) {
|
||||
this.clickedAnimation!!.rate = -1.0
|
||||
this.clickedAnimation!!.play()
|
||||
}
|
||||
|
||||
}
|
||||
focusedProperty().addListener { o, oldVal, newVal ->
|
||||
focusedProperty().addListener { _, _, newVal ->
|
||||
if (newVal) {
|
||||
if (!isPressed) {
|
||||
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
|
||||
this.buttonContainer.isPickOnBounds = false
|
||||
this.buttonContainer.shapeProperty().bind(shapeProperty())
|
||||
@@ -157,8 +137,8 @@ open class RipplerContainer(@NamedArg("container") container: Node): StackPane()
|
||||
this.updateChildren()
|
||||
|
||||
containerProperty.addListener { _ -> updateChildren() }
|
||||
selectedProperty.addListener { _, _, newValue ->
|
||||
if (newValue) background = Background(BackgroundFill(ripplerFill, defaultRadii, null))
|
||||
selectedProperty.addListener { _ ->
|
||||
if (selected) background = Background(BackgroundFill(ripplerFill, defaultRadii, null))
|
||||
else background = Background(BackgroundFill(Color.TRANSPARENT, defaultRadii, null))
|
||||
}
|
||||
|
||||
|
||||
@@ -17,107 +17,38 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui
|
||||
|
||||
import com.jfoenix.controls.JFXScrollPane
|
||||
import com.jfoenix.controls.JFXTextField
|
||||
import com.jfoenix.controls.*
|
||||
import javafx.beans.InvalidationListener
|
||||
import javafx.beans.property.Property
|
||||
import javafx.beans.property.SimpleStringProperty
|
||||
import javafx.beans.property.StringProperty
|
||||
import javafx.beans.value.ChangeListener
|
||||
import javafx.fxml.FXML
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.control.ScrollPane
|
||||
import javafx.scene.layout.*
|
||||
import javafx.stage.DirectoryChooser
|
||||
import org.jackhuang.hmcl.setting.Profile
|
||||
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)
|
||||
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 versionSettingsController: VersionSettingsController
|
||||
@FXML lateinit var modController: ModController
|
||||
|
||||
init {
|
||||
loadFXML("/assets/fxml/version.fxml")
|
||||
}
|
||||
|
||||
fun initialize() {
|
||||
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) {
|
||||
fun load(id: String, profile: Profile) {
|
||||
titleProperty.set("Version settings - " + id)
|
||||
rootPane.children -= advancedSettingsPane
|
||||
|
||||
lastVersionSetting?.apply {
|
||||
widthProperty.unbind()
|
||||
heightProperty.unbind()
|
||||
maxMemoryProperty.unbind()
|
||||
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
|
||||
versionSettingsController.loadVersionSetting(id, profile.getVersionSetting(id))
|
||||
modController.modManager = profile.modManager
|
||||
modController.versionId = id
|
||||
modController.loadMods()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
interface HasTitle {
|
||||
interface DecoratorPage {
|
||||
val titleProperty: StringProperty
|
||||
|
||||
fun onClose() {}
|
||||
}
|
||||
@@ -3,11 +3,11 @@
|
||||
<?import javafx.scene.layout.*?>
|
||||
|
||||
<?import com.jfoenix.controls.JFXButton?>
|
||||
<?import javafx.scene.shape.SVGPath?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.image.Image?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import com.jfoenix.controls.JFXRadioButton?>
|
||||
<fx:root xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
type="StackPane">
|
||||
@@ -27,9 +27,11 @@
|
||||
</center>
|
||||
</BorderPane>
|
||||
</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>
|
||||
<StackPane fx:id="icon" StackPane.alignment="TOP_RIGHT">
|
||||
<StackPane fx:id="icon" StackPane.alignment="TOP_RIGHT" pickOnBounds="false">
|
||||
<ImageView StackPane.alignment="CENTER_RIGHT">
|
||||
<StackPane.margin>
|
||||
<Insets right="12" />
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<?import javafx.collections.FXCollections?>
|
||||
<?import java.lang.String?>
|
||||
<?import com.jfoenix.validation.RequiredFieldValidator?>
|
||||
<?import com.jfoenix.controls.JFXNodesList?>
|
||||
<fx:root xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
type="StackPane">
|
||||
@@ -34,10 +35,10 @@
|
||||
<Label>Create a new account</Label>
|
||||
</heading>
|
||||
<body>
|
||||
<GridPane vgap="30" hgap="30">
|
||||
<GridPane vgap="15" hgap="15" style="-fx-padding: 15 0 0 0;">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints maxWidth="100" />
|
||||
<ColumnConstraints />
|
||||
<ColumnConstraints hgrow="ALWAYS" />
|
||||
</columnConstraints>
|
||||
<Label text="Type" GridPane.halignment="RIGHT" GridPane.columnIndex="0" GridPane.rowIndex="0" />
|
||||
<Label text="Username" GridPane.halignment="RIGHT" GridPane.columnIndex="0" GridPane.rowIndex="1" />
|
||||
|
||||
@@ -39,13 +39,13 @@
|
||||
<bottom>
|
||||
<BorderPane fx:id="menuBottomBar">
|
||||
<left>
|
||||
<JFXButton fx:id="refreshMenuButton" styleClass="toggle-icon3">
|
||||
<JFXButton fx:id="refreshMenuButton" styleClass="toggle-icon4">
|
||||
<graphic>
|
||||
<fx:include source="/assets/svg/refresh-black.fxml"/>
|
||||
</graphic></JFXButton>
|
||||
</left>
|
||||
<right>
|
||||
<JFXButton fx:id="addMenuButton" styleClass="toggle-icon3">
|
||||
<JFXButton fx:id="addMenuButton" styleClass="toggle-icon4">
|
||||
<graphic>
|
||||
<fx:include source="/assets/svg/plus-black.fxml"/>
|
||||
</graphic></JFXButton>
|
||||
|
||||
@@ -4,13 +4,12 @@
|
||||
<?import com.jfoenix.controls.*?>
|
||||
<fx:root
|
||||
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">
|
||||
<BorderPane>
|
||||
<center>
|
||||
<StackPane fx:id="page" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER" />
|
||||
</center>
|
||||
<left>
|
||||
</left>
|
||||
<bottom>
|
||||
<BorderPane prefHeight="50.0" prefWidth="200.0" BorderPane.alignment="CENTER">
|
||||
<right>
|
||||
@@ -22,4 +21,5 @@
|
||||
</right>
|
||||
</BorderPane>
|
||||
</bottom>
|
||||
</BorderPane>
|
||||
</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"?>
|
||||
|
||||
<?import com.jfoenix.controls.*?>
|
||||
<?import javafx.collections.FXCollections?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<fx:root xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
type="StackPane">
|
||||
<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" 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>
|
||||
<JFXTabPane>
|
||||
<Tab text="Settings">
|
||||
<fx:include source="version-settings.fxml" fx:id="versionSettings" />
|
||||
</Tab>
|
||||
<Tab text="Mods">
|
||||
<fx:include source="mod.fxml" fx:id="mod" />
|
||||
</Tab>
|
||||
</JFXTabPane>
|
||||
</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
|
||||
|
||||
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 {
|
||||
if (username.isBlank() || uuid.isBlank())
|
||||
throw AuthenticationException("Username cannot be empty")
|
||||
|
||||
@@ -38,19 +38,19 @@ class DefaultDependencyManager(override val repository: DefaultGameRepository, o
|
||||
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")
|
||||
return ForgeInstallTask(this, version, libraryVersion) then { task ->
|
||||
return ForgeInstallTask(this, gameVersion, version, libraryVersion) then { task ->
|
||||
val newVersion = task.result!!
|
||||
VersionJSONSaveTask(this@DefaultDependencyManager, newVersion)
|
||||
}
|
||||
else if (libraryId == "liteloader")
|
||||
return LiteLoaderInstallTask(this, version, libraryVersion) then { task ->
|
||||
return LiteLoaderInstallTask(this, gameVersion, version, libraryVersion) then { task ->
|
||||
val newVersion = task.result!!
|
||||
VersionJSONSaveTask(this@DefaultDependencyManager, newVersion)
|
||||
}
|
||||
else if (libraryId == "optifine")
|
||||
return OptiFineInstallTask(this, version, libraryVersion) then { task ->
|
||||
return OptiFineInstallTask(this, gameVersion, version, libraryVersion) then { task ->
|
||||
val newVersion = task.result!!
|
||||
VersionJSONSaveTask(this@DefaultDependencyManager, newVersion)
|
||||
}
|
||||
|
||||
@@ -27,9 +27,10 @@ class DefaultGameBuilder(val dependencyManager: DefaultDependencyManager): GameB
|
||||
val downloadProvider = dependencyManager.downloadProvider
|
||||
|
||||
override fun buildAsync(): Task {
|
||||
val gameVersion = gameVersion
|
||||
return VersionJSONDownloadTask(gameVersion = gameVersion) then a@{ task ->
|
||||
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(
|
||||
GameAssetDownloadTask(dependencyManager, version),
|
||||
GameLoggingDownloadTask(dependencyManager, version),
|
||||
@@ -38,21 +39,21 @@ class DefaultGameBuilder(val dependencyManager: DefaultDependencyManager): GameB
|
||||
) then VersionJSONSaveTask(dependencyManager, version)
|
||||
|
||||
if (toolVersions.containsKey("forge"))
|
||||
result = result then libraryTaskHelper(version, "forge")
|
||||
result = result then libraryTaskHelper(gameVersion, version, "forge")
|
||||
if (toolVersions.containsKey("liteloader"))
|
||||
result = result then libraryTaskHelper(version, "liteloader")
|
||||
result = result then libraryTaskHelper(gameVersion, version, "liteloader")
|
||||
if (toolVersions.containsKey("optifine"))
|
||||
result = result then libraryTaskHelper(version, "optifine")
|
||||
result = result then libraryTaskHelper(gameVersion, version, "optifine")
|
||||
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
|
||||
if (prev is TaskResult<*> && prev.result is 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() {
|
||||
|
||||
@@ -36,7 +36,7 @@ abstract class DependencyManager(open val repository: GameRepository) {
|
||||
*/
|
||||
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.
|
||||
|
||||
@@ -30,8 +30,9 @@ import java.io.IOException
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
class ForgeInstallTask(private val dependencyManager: DefaultDependencyManager,
|
||||
private val version: Version,
|
||||
private val remoteVersion: String) : TaskResult<Version>() {
|
||||
private val gameVersion: String,
|
||||
private val version: Version,
|
||||
private val remoteVersion: String) : TaskResult<Version>() {
|
||||
private val forgeVersionList = dependencyManager.getVersionList("forge")
|
||||
private val installer: File = File("forge-installer.jar").absoluteFile
|
||||
lateinit var remote: RemoteVersion<*>
|
||||
@@ -39,15 +40,13 @@ class ForgeInstallTask(private val dependencyManager: DefaultDependencyManager,
|
||||
override val dependencies: MutableCollection<Task> = mutableListOf()
|
||||
|
||||
init {
|
||||
if (version.jar == null)
|
||||
throw IllegalArgumentException()
|
||||
if (!forgeVersionList.loaded)
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,23 +30,22 @@ import org.jackhuang.hmcl.util.merge
|
||||
* LiteLoader must be installed after Forge.
|
||||
*/
|
||||
class LiteLoaderInstallTask(private val dependencyManager: DefaultDependencyManager,
|
||||
private val version: Version,
|
||||
private val remoteVersion: String): TaskResult<Version>() {
|
||||
private val gameVersion: String,
|
||||
private val version: Version,
|
||||
private val remoteVersion: String): TaskResult<Version>() {
|
||||
private val liteLoaderVersionList = dependencyManager.getVersionList("liteloader") as LiteLoaderVersionList
|
||||
lateinit var remote: RemoteVersion<LiteLoaderRemoteVersionTag>
|
||||
override val dependents: MutableCollection<Task> = mutableListOf()
|
||||
override val dependencies: MutableCollection<Task> = mutableListOf()
|
||||
|
||||
init {
|
||||
if (version.jar == null)
|
||||
throw IllegalArgumentException()
|
||||
if (!liteLoaderVersionList.loaded)
|
||||
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
|
||||
}
|
||||
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
|
||||
|
||||
class OptiFineInstallTask(private val dependencyManager: DefaultDependencyManager,
|
||||
private val version: Version,
|
||||
private val remoteVersion: String): TaskResult<Version>() {
|
||||
private val gameVersion: String,
|
||||
private val version: Version,
|
||||
private val remoteVersion: String): TaskResult<Version>() {
|
||||
private val optiFineVersionList = dependencyManager.getVersionList("optifine")
|
||||
lateinit var remote: RemoteVersion<*>
|
||||
override val dependents: MutableCollection<Task> = mutableListOf()
|
||||
override val dependencies: MutableCollection<Task> = mutableListOf()
|
||||
|
||||
init {
|
||||
if (version.jar == null)
|
||||
throw IllegalArgumentException()
|
||||
if (!optiFineVersionList.loaded)
|
||||
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
|
||||
}
|
||||
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() {
|
||||
|
||||
@@ -41,7 +41,7 @@ open class DefaultGameRepository(var baseDirectory: File): GameRepository {
|
||||
override fun getRunDirectory(id: String) = baseDirectory
|
||||
override fun getVersionJar(version: Version): File {
|
||||
val v = version.resolve(this)
|
||||
val id = v.id
|
||||
val id = v.jar ?: v.id
|
||||
return getVersionRoot(id).resolve("$id.jar")
|
||||
}
|
||||
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())
|
||||
}
|
||||
|
||||
if (options.serverIp != null) {
|
||||
if (options.serverIp != null && options.serverIp.isNotBlank()) {
|
||||
val args = options.serverIp.split(":")
|
||||
res.add("--server")
|
||||
res.add(args[0])
|
||||
|
||||
@@ -18,19 +18,34 @@
|
||||
package org.jackhuang.hmcl.mod
|
||||
|
||||
import com.google.gson.JsonParseException
|
||||
import org.jackhuang.hmcl.util.property.ImmediateBooleanProperty
|
||||
import org.jackhuang.hmcl.util.*
|
||||
import java.io.File
|
||||
|
||||
class ModInfo (
|
||||
val file: File,
|
||||
var file: File,
|
||||
val name: String,
|
||||
val description: String = "",
|
||||
val authors: String = "",
|
||||
val version: String = "",
|
||||
val mcversion: String = "",
|
||||
val authors: String = "unknown",
|
||||
val version: String = "unknown",
|
||||
val mcversion: String = "unknown",
|
||||
val url: String = ""
|
||||
): Comparable<ModInfo> {
|
||||
val isActive: Boolean
|
||||
get() = file.extension != DISABLED_EXTENSION
|
||||
val activeProperty = object : ImmediateBooleanProperty(this, "active", 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(".")
|
||||
|
||||
@@ -53,22 +68,23 @@ class ModInfo (
|
||||
val file = if (modFile.extension == DISABLED_EXTENSION)
|
||||
modFile.absoluteFile.parentFile.resolve(modFile.nameWithoutExtension)
|
||||
else modFile
|
||||
var description = "Unrecognized mod file"
|
||||
if (file.extension == "zip" || file.extension == "jar")
|
||||
try {
|
||||
return ForgeModMetadata.fromFile(modFile)
|
||||
} catch (e: JsonParseException) {
|
||||
throw e
|
||||
} catch (ignore: Exception) {}
|
||||
} catch (ignore: Exception) {
|
||||
description = "May be Forge mod"
|
||||
}
|
||||
|
||||
else if (file.extension == "litemod")
|
||||
try {
|
||||
return LiteModMetadata.fromFile(modFile)
|
||||
} catch (e: JsonParseException) {
|
||||
throw e
|
||||
} catch (ignore: Exception) {}
|
||||
} catch (ignore: Exception) {
|
||||
description = "May be LiteLoader 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 {
|
||||
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 memory = TOTAL_MEMORY / 1024 / 1024 / 4
|
||||
val memory = TOTAL_MEMORY / 4
|
||||
(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.launch.DefaultLauncher
|
||||
import org.jackhuang.hmcl.game.LaunchOptions
|
||||
import org.jackhuang.hmcl.game.minecraftVersion
|
||||
import org.jackhuang.hmcl.launch.ProcessListener
|
||||
import org.jackhuang.hmcl.util.makeCommand
|
||||
import org.jackhuang.hmcl.task.Task
|
||||
@@ -115,10 +116,11 @@ class Test {
|
||||
fun installForge() {
|
||||
val thread = Thread.currentThread()
|
||||
val version = repository.getVersion("test").resolve(repository)
|
||||
val minecraftVersion = minecraftVersion(repository.getVersionJar(version)) ?: ""
|
||||
// optifine HD_U_C4
|
||||
// forge 14.21.1.2426
|
||||
// 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)
|
||||
}.start()
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user