mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-09-12 13:26:53 -04:00
Profiles editing and selecting
This commit is contained in:
parent
3b4359f7eb
commit
ff1a566bf4
@ -87,6 +87,14 @@ class HMCLGameRepository(val profile: Profile, baseDirectory: File)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun changeDirectory(newDir: File) {
|
||||||
|
baseDirectory = newDir
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
refreshVersions()
|
||||||
|
}
|
||||||
|
|
||||||
private fun checkModpack() {}
|
private fun checkModpack() {}
|
||||||
|
|
||||||
private fun getVersionSettingFile(id: String) = getVersionRoot(id).resolve("hmclversion.cfg")
|
private fun getVersionSettingFile(id: String) = getVersionRoot(id).resolve("hmclversion.cfg")
|
||||||
|
@ -41,11 +41,12 @@ object LauncherHelper {
|
|||||||
|
|
||||||
fun launch() {
|
fun launch() {
|
||||||
val profile = Settings.selectedProfile
|
val profile = Settings.selectedProfile
|
||||||
|
val selectedVersion = profile.selectedVersion ?: throw IllegalStateException("No version here")
|
||||||
val repository = profile.repository
|
val repository = profile.repository
|
||||||
val dependency = profile.dependency
|
val dependency = profile.dependency
|
||||||
val account = Settings.selectedAccount ?: throw IllegalStateException("No account here")
|
val account = Settings.selectedAccount ?: throw IllegalStateException("No account here")
|
||||||
val version = repository.getVersion(profile.selectedVersion)
|
val version = repository.getVersion(selectedVersion)
|
||||||
val setting = profile.getVersionSetting(profile.selectedVersion)
|
val setting = profile.getVersionSetting(selectedVersion)
|
||||||
var finished = 0
|
var finished = 0
|
||||||
|
|
||||||
Controllers.dialog(launchingStepsPane)
|
Controllers.dialog(launchingStepsPane)
|
||||||
@ -53,7 +54,7 @@ object LauncherHelper {
|
|||||||
.then(dependency.checkGameCompletionAsync(version))
|
.then(dependency.checkGameCompletionAsync(version))
|
||||||
|
|
||||||
.then(task(Scheduler.JAVAFX) { emitStatus(LoadingState.MODS) })
|
.then(task(Scheduler.JAVAFX) { emitStatus(LoadingState.MODS) })
|
||||||
.then(CurseForgeModpackCompletionTask(dependency, profile.selectedVersion))
|
.then(CurseForgeModpackCompletionTask(dependency, selectedVersion))
|
||||||
|
|
||||||
.then(task(Scheduler.JAVAFX) { emitStatus(LoadingState.LOGIN) })
|
.then(task(Scheduler.JAVAFX) { emitStatus(LoadingState.LOGIN) })
|
||||||
.then(task {
|
.then(task {
|
||||||
@ -69,7 +70,7 @@ object LauncherHelper {
|
|||||||
.then(task {
|
.then(task {
|
||||||
it["launcher"] = HMCLGameLauncher(
|
it["launcher"] = HMCLGameLauncher(
|
||||||
repository = repository,
|
repository = repository,
|
||||||
versionId = profile.selectedVersion,
|
versionId = selectedVersion,
|
||||||
options = setting.toLaunchOptions(profile.gameDir),
|
options = setting.toLaunchOptions(profile.gameDir),
|
||||||
listener = HMCLProcessListener(it["account"], setting),
|
listener = HMCLProcessListener(it["account"], setting),
|
||||||
account = it["account"]
|
account = it["account"]
|
||||||
|
@ -20,21 +20,24 @@ package org.jackhuang.hmcl.setting
|
|||||||
import com.google.gson.*
|
import com.google.gson.*
|
||||||
import javafx.beans.InvalidationListener
|
import javafx.beans.InvalidationListener
|
||||||
import org.jackhuang.hmcl.download.DefaultDependencyManager
|
import org.jackhuang.hmcl.download.DefaultDependencyManager
|
||||||
|
import org.jackhuang.hmcl.event.EVENT_BUS
|
||||||
|
import org.jackhuang.hmcl.event.RefreshedVersionsEvent
|
||||||
import org.jackhuang.hmcl.game.HMCLGameRepository
|
import org.jackhuang.hmcl.game.HMCLGameRepository
|
||||||
import org.jackhuang.hmcl.mod.ModManager
|
import org.jackhuang.hmcl.mod.ModManager
|
||||||
import org.jackhuang.hmcl.util.ImmediateObjectProperty
|
import org.jackhuang.hmcl.ui.runOnUiThread
|
||||||
import org.jackhuang.hmcl.util.ImmediateStringProperty
|
import org.jackhuang.hmcl.util.*
|
||||||
import org.jackhuang.hmcl.util.getValue
|
|
||||||
import org.jackhuang.hmcl.util.setValue
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.lang.reflect.Type
|
import java.lang.reflect.Type
|
||||||
|
|
||||||
class Profile(var name: String = "Default", initialGameDir: File = File(".minecraft"), initialSelectedVersion: String = "") {
|
class Profile(name: String = "Default", initialGameDir: File = File(".minecraft"), initialSelectedVersion: String = "") {
|
||||||
|
val nameProperty = ImmediateStringProperty(this, "name", name)
|
||||||
|
var name: String by nameProperty
|
||||||
|
|
||||||
val globalProperty = ImmediateObjectProperty<VersionSetting>(this, "global", VersionSetting())
|
val globalProperty = ImmediateObjectProperty<VersionSetting>(this, "global", VersionSetting())
|
||||||
var global: VersionSetting by globalProperty
|
var global: VersionSetting by globalProperty
|
||||||
|
|
||||||
val selectedVersionProperty = ImmediateStringProperty(this, "selectedVersion", initialSelectedVersion)
|
val selectedVersionProperty = ImmediateObjectProperty<String?>(this, "selectedVersion", initialSelectedVersion)
|
||||||
var selectedVersion: String by selectedVersionProperty
|
var selectedVersion: String? by selectedVersionProperty
|
||||||
|
|
||||||
val gameDirProperty = ImmediateObjectProperty<File>(this, "gameDir", initialGameDir)
|
val gameDirProperty = ImmediateObjectProperty<File>(this, "gameDir", initialGameDir)
|
||||||
var gameDir: File by gameDirProperty
|
var gameDir: File by gameDirProperty
|
||||||
@ -44,17 +47,21 @@ class Profile(var name: String = "Default", initialGameDir: File = File(".minecr
|
|||||||
var modManager = ModManager(repository)
|
var modManager = ModManager(repository)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
gameDirProperty.addListener { _ ->
|
gameDirProperty.onChange { newGameDir ->
|
||||||
repository.baseDirectory = gameDir
|
repository.baseDirectory = newGameDir!!
|
||||||
repository.refreshVersions()
|
repository.refreshVersions()
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedVersionProperty.addListener { _ ->
|
selectedVersionProperty.addListener { _ -> verifySelectedVersion() }
|
||||||
if (selectedVersion.isNotBlank() && !repository.hasVersion(selectedVersion)) {
|
EVENT_BUS.channel<RefreshedVersionsEvent>() += { event -> if (event.source == repository) verifySelectedVersion() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun verifySelectedVersion() = runOnUiThread {
|
||||||
|
// To prevent not loaded profile's selectedVersion being changed.
|
||||||
|
if (repository.isLoaded && ((selectedVersion == null && repository.getVersions().isNotEmpty()) || (selectedVersion != null && !repository.hasVersion(selectedVersion!!)))) {
|
||||||
val newVersion = repository.getVersions().firstOrNull()
|
val newVersion = repository.getVersions().firstOrNull()
|
||||||
// will cause anthor change event, we must ensure 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
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,10 +93,10 @@ class Profile(var name: String = "Default", initialGameDir: File = File(".minecr
|
|||||||
return vs
|
return vs
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSelectedVersionSetting(): VersionSetting =
|
fun getSelectedVersionSetting(): VersionSetting? = if (selectedVersion == null) null else getVersionSetting(selectedVersion!!)
|
||||||
getVersionSetting(selectedVersion)
|
|
||||||
|
|
||||||
fun addPropertyChangedListener(listener: InvalidationListener) {
|
fun addPropertyChangedListener(listener: InvalidationListener) {
|
||||||
|
nameProperty.addListener(listener)
|
||||||
globalProperty.addListener(listener)
|
globalProperty.addListener(listener)
|
||||||
selectedVersionProperty.addListener(listener)
|
selectedVersionProperty.addListener(listener)
|
||||||
gameDirProperty.addListener(listener)
|
gameDirProperty.addListener(listener)
|
||||||
|
@ -28,6 +28,7 @@ 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.event.EVENT_BUS
|
import org.jackhuang.hmcl.event.EVENT_BUS
|
||||||
|
import org.jackhuang.hmcl.task.Scheduler
|
||||||
import org.jackhuang.hmcl.util.*
|
import org.jackhuang.hmcl.util.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@ -270,12 +271,20 @@ object Settings {
|
|||||||
* PROFILES *
|
* PROFILES *
|
||||||
****************************************/
|
****************************************/
|
||||||
|
|
||||||
val selectedProfile: Profile
|
var selectedProfile: Profile
|
||||||
get() {
|
get() {
|
||||||
if (!hasProfile(SETTINGS.selectedProfile))
|
if (!hasProfile(SETTINGS.selectedProfile)) {
|
||||||
SETTINGS.selectedProfile = DEFAULT_PROFILE
|
SETTINGS.selectedProfile = DEFAULT_PROFILE
|
||||||
|
Scheduler.COMPUTATION.schedule { onProfileChanged() }
|
||||||
|
}
|
||||||
return getProfile(SETTINGS.selectedProfile)
|
return getProfile(SETTINGS.selectedProfile)
|
||||||
}
|
}
|
||||||
|
set(value) {
|
||||||
|
if (hasProfile(value.name) && value.name != SETTINGS.selectedProfile) {
|
||||||
|
SETTINGS.selectedProfile = value.name
|
||||||
|
Scheduler.COMPUTATION.schedule { onProfileChanged() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun getProfile(name: String?): Profile {
|
fun getProfile(name: String?): Profile {
|
||||||
var p: Profile? = getProfileMap()[name ?: DEFAULT_PROFILE]
|
var p: Profile? = getProfileMap()[name ?: DEFAULT_PROFILE]
|
||||||
@ -316,12 +325,10 @@ object Settings {
|
|||||||
if (DEFAULT_PROFILE == ver) {
|
if (DEFAULT_PROFILE == ver) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
var notify = false
|
|
||||||
if (selectedProfile.name == ver)
|
|
||||||
notify = true
|
|
||||||
val flag = getProfileMap().remove(ver) != null
|
val flag = getProfileMap().remove(ver) != null
|
||||||
if (notify && flag)
|
if (flag)
|
||||||
onProfileChanged()
|
Scheduler.COMPUTATION.schedule { onProfileLoading() }
|
||||||
|
|
||||||
return flag
|
return flag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ class AccountItem(i: Int, val account: Account, group: ToggleGroup) : StackPane(
|
|||||||
header.style = "-fx-background-radius: 2 2 0 0; -fx-background-color: " + headerColor
|
header.style = "-fx-background-radius: 2 2 0 0; -fx-background-color: " + headerColor
|
||||||
|
|
||||||
// create image view
|
// create image view
|
||||||
icon.translateYProperty().bind(Bindings.createDoubleBinding(Callable { header.boundsInParent.height - icon.height / 2 }, header.boundsInParentProperty(), icon.heightProperty()))
|
icon.translateYProperty().bind(Bindings.createDoubleBinding(Callable { header.boundsInParent.height - icon.height / 2 - 32.0 }, header.boundsInParentProperty(), icon.heightProperty()))
|
||||||
|
|
||||||
chkSelected.properties["account"] = account
|
chkSelected.properties["account"] = account
|
||||||
chkSelected.isSelected = Settings.selectedAccount == account
|
chkSelected.isSelected = Settings.selectedAccount == account
|
||||||
|
@ -24,6 +24,7 @@ import javafx.scene.image.Image
|
|||||||
import javafx.scene.layout.Region
|
import javafx.scene.layout.Region
|
||||||
import javafx.stage.Stage
|
import javafx.stage.Stage
|
||||||
import org.jackhuang.hmcl.Main
|
import org.jackhuang.hmcl.Main
|
||||||
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
|
|
||||||
object Controllers {
|
object Controllers {
|
||||||
lateinit var scene: Scene private set
|
lateinit var scene: Scene private set
|
||||||
@ -44,6 +45,8 @@ object Controllers {
|
|||||||
decorator.showPage(null)
|
decorator.showPage(null)
|
||||||
leftPaneController = LeftPaneController(decorator.leftPane)
|
leftPaneController = LeftPaneController(decorator.leftPane)
|
||||||
|
|
||||||
|
Settings.onProfileLoading()
|
||||||
|
|
||||||
decorator.isCustomMaximize = false
|
decorator.isCustomMaximize = false
|
||||||
|
|
||||||
scene = Scene(decorator, 800.0, 480.0)
|
scene = Scene(decorator, 800.0, 480.0)
|
||||||
|
@ -17,54 +17,51 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui
|
package org.jackhuang.hmcl.ui
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXComboBox
|
|
||||||
import javafx.scene.layout.VBox
|
import javafx.scene.layout.VBox
|
||||||
|
import javafx.scene.paint.Paint
|
||||||
|
import org.jackhuang.hmcl.ProfileChangedEvent
|
||||||
|
import org.jackhuang.hmcl.ProfileLoadingEvent
|
||||||
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.game.AccountHelper
|
import org.jackhuang.hmcl.game.AccountHelper
|
||||||
import org.jackhuang.hmcl.i18n
|
import org.jackhuang.hmcl.i18n
|
||||||
import org.jackhuang.hmcl.setting.Settings
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
import org.jackhuang.hmcl.ui.construct.IconedItem
|
import org.jackhuang.hmcl.ui.construct.IconedItem
|
||||||
import org.jackhuang.hmcl.ui.construct.RipplerContainer
|
import org.jackhuang.hmcl.ui.construct.RipplerContainer
|
||||||
import org.jackhuang.hmcl.util.onChangeAndOperate
|
import org.jackhuang.hmcl.util.onChangeAndOperate
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class LeftPaneController(private val leftPane: AdvancedListBox) {
|
class LeftPaneController(private val leftPane: AdvancedListBox) {
|
||||||
val versionsPane = VBox()
|
val profilePane = VBox()
|
||||||
val cboProfiles = JFXComboBox<String>().apply { items.add("Default"); prefWidthProperty().bind(leftPane.widthProperty()) }
|
|
||||||
val accountItem = VersionListItem("No Account", "unknown")
|
val accountItem = VersionListItem("No Account", "unknown")
|
||||||
|
|
||||||
init {
|
init {
|
||||||
leftPane
|
leftPane
|
||||||
.startCategory("ACCOUNTS")
|
.startCategory("ACCOUNTS")
|
||||||
.add(RipplerContainer(accountItem).apply {
|
.add(RipplerContainer(accountItem).apply {
|
||||||
|
setOnMouseClicked {
|
||||||
|
Controllers.navigate(AccountsPage())
|
||||||
|
}
|
||||||
accountItem.onSettingsButtonClicked {
|
accountItem.onSettingsButtonClicked {
|
||||||
Controllers.navigate(AccountsPage())
|
Controllers.navigate(AccountsPage())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.startCategory(i18n("ui.label.profile"))
|
|
||||||
.add(cboProfiles)
|
|
||||||
.startCategory("LAUNCHER")
|
.startCategory("LAUNCHER")
|
||||||
.add(IconedItem(SVG.gear("black"), "Settings").apply {
|
.add(IconedItem(SVG.gear("black"), i18n("launcher.title.launcher")).apply {
|
||||||
prefWidthProperty().bind(leftPane.widthProperty())
|
prefWidthProperty().bind(leftPane.widthProperty())
|
||||||
setOnMouseClicked {
|
setOnMouseClicked {
|
||||||
Controllers.navigate(Controllers.settingsPane)
|
Controllers.navigate(Controllers.settingsPane)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
/* .startCategory(i18n("ui.label.version"))
|
.startCategory(i18n("ui.label.profile"))
|
||||||
.add(versionsPane)
|
.add(profilePane)
|
||||||
|
|
||||||
EVENT_BUS.channel<RefreshedVersionsEvent>() += this::loadVersions
|
|
||||||
EVENT_BUS.channel<ProfileLoadingEvent>() += this::onProfilesLoading
|
EVENT_BUS.channel<ProfileLoadingEvent>() += this::onProfilesLoading
|
||||||
EVENT_BUS.channel<ProfileChangedEvent>() += this::onProfileChanged
|
EVENT_BUS.channel<ProfileChangedEvent>() += this::onProfileChanged
|
||||||
|
|
||||||
Settings.onProfileLoading()
|
|
||||||
|
|
||||||
Controllers.decorator.addMenuButton.setOnMouseClicked {
|
Controllers.decorator.addMenuButton.setOnMouseClicked {
|
||||||
Controllers.decorator.startWizard(DownloadWizardProvider(), "Install New Game")
|
Controllers.decorator.showPage(ProfilePage(null))
|
||||||
}
|
}
|
||||||
Controllers.decorator.refreshMenuButton.setOnMouseClicked {
|
|
||||||
Settings.selectedProfile.repository.refreshVersions()
|
|
||||||
}
|
|
||||||
Controllers.mainPane.buttonLaunch.setOnMouseClicked { LauncherHelper.launch() }*/
|
|
||||||
|
|
||||||
Settings.selectedAccountProperty.onChangeAndOperate {
|
Settings.selectedAccountProperty.onChangeAndOperate {
|
||||||
if (it == null) {
|
if (it == null) {
|
||||||
@ -86,46 +83,36 @@ class LeftPaneController(private val leftPane: AdvancedListBox) {
|
|||||||
if (Settings.getAccounts().isEmpty())
|
if (Settings.getAccounts().isEmpty())
|
||||||
Controllers.navigate(AccountsPage())
|
Controllers.navigate(AccountsPage())
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
fun onProfilesLoading() {
|
|
||||||
// TODO: Profiles
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onProfileChanged(event: ProfileChangedEvent) {
|
fun onProfileChanged(event: ProfileChangedEvent) {
|
||||||
val profile = event.value
|
val profile = event.value
|
||||||
profile.selectedVersionProperty.addListener { _ ->
|
|
||||||
versionChanged(profile.selectedVersion)
|
profilePane.children
|
||||||
}
|
.filter { it is RipplerContainer && it.properties["profile"] is Pair<*, *> }
|
||||||
profile.selectedVersionProperty.fireValueChangedEvent()
|
.forEach { (it as RipplerContainer).selected = (it.properties["profile"] as Pair<String, VersionListItem>).first == profile.name }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadVersions() {
|
fun onProfilesLoading() {
|
||||||
val profile = Settings.selectedProfile
|
val list = LinkedList<RipplerContainer>()
|
||||||
versionsPane.children.clear()
|
Settings.getProfiles().forEach { profile ->
|
||||||
profile.repository.getVersions().forEach { version ->
|
val item = VersionListItem(profile.name).apply {
|
||||||
val item = VersionListItem(version.id, minecraftVersion(profile.repository.getVersionJar(version.id)) ?: "Unknown")
|
lblGameVersion.textProperty().bind(profile.selectedVersionProperty)
|
||||||
|
}
|
||||||
val ripplerContainer = RipplerContainer(item)
|
val ripplerContainer = RipplerContainer(item)
|
||||||
item.onSettingsButtonClicked {
|
item.onSettingsButtonClicked {
|
||||||
Controllers.decorator.showPage(Controllers.versionPane)
|
Controllers.decorator.showPage(ProfilePage(profile))
|
||||||
Controllers.versionPane.load(item.versionName, profile)
|
|
||||||
}
|
}
|
||||||
ripplerContainer.ripplerFill = Paint.valueOf("#89E1F9")
|
ripplerContainer.ripplerFill = Paint.valueOf("#89E1F9")
|
||||||
ripplerContainer.setOnMouseClicked {
|
ripplerContainer.setOnMouseClicked {
|
||||||
// clean selected property
|
// clean selected property
|
||||||
versionsPane.children.forEach { if (it is RipplerContainer) it.selected = false }
|
profilePane.children.forEach { if (it is RipplerContainer) it.selected = false }
|
||||||
ripplerContainer.selected = true
|
ripplerContainer.selected = true
|
||||||
profile.selectedVersion = version.id
|
Settings.selectedProfile = profile
|
||||||
}
|
}
|
||||||
ripplerContainer.properties["version"] = version.id to item
|
ripplerContainer.properties["profile"] = profile.name to item
|
||||||
ripplerContainer.maxWidthProperty().bind(leftPane.widthProperty())
|
ripplerContainer.maxWidthProperty().bind(leftPane.widthProperty())
|
||||||
versionsPane.children += ripplerContainer
|
list += ripplerContainer
|
||||||
|
}
|
||||||
|
runOnUiThread { profilePane.children.setAll(list) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
fun versionChanged(selectedVersion: String) {
|
|
||||||
versionsPane.children
|
|
||||||
.filter { it is RipplerContainer && it.properties["version"] is Pair<*, *> }
|
|
||||||
.forEach { (it as RipplerContainer).selected = (it.properties["version"] as Pair<String, VersionListItem>).first == selectedVersion }
|
|
||||||
}*/
|
|
||||||
}
|
|
@ -45,7 +45,7 @@ import org.jackhuang.hmcl.util.onChange
|
|||||||
* @see /assets/fxml/main.fxml
|
* @see /assets/fxml/main.fxml
|
||||||
*/
|
*/
|
||||||
class MainPage : StackPane(), DecoratorPage {
|
class MainPage : StackPane(), DecoratorPage {
|
||||||
override val titleProperty: StringProperty = SimpleStringProperty(this, "title", i18n("launcher.title.main"))
|
override val titleProperty = SimpleStringProperty(this, "title", i18n("launcher.title.main"))
|
||||||
|
|
||||||
@FXML lateinit var btnLaunch: JFXButton
|
@FXML lateinit var btnLaunch: JFXButton
|
||||||
@FXML lateinit var btnRefresh: JFXButton
|
@FXML lateinit var btnRefresh: JFXButton
|
||||||
@ -59,15 +59,20 @@ class MainPage : StackPane(), DecoratorPage {
|
|||||||
btnLaunch.limitWidth(40.0)
|
btnLaunch.limitWidth(40.0)
|
||||||
btnLaunch.limitHeight(40.0)
|
btnLaunch.limitHeight(40.0)
|
||||||
|
|
||||||
EVENT_BUS.channel<RefreshedVersionsEvent>() += this::loadVersions
|
EVENT_BUS.channel<RefreshedVersionsEvent>() += { -> loadVersions() }
|
||||||
EVENT_BUS.channel<ProfileLoadingEvent>() += this::onProfilesLoading
|
EVENT_BUS.channel<ProfileLoadingEvent>() += this::onProfilesLoading
|
||||||
EVENT_BUS.channel<ProfileChangedEvent>() += this::onProfileChanged
|
EVENT_BUS.channel<ProfileChangedEvent>() += this::onProfileChanged
|
||||||
|
|
||||||
Settings.onProfileLoading()
|
|
||||||
|
|
||||||
btnAdd.setOnMouseClicked { Controllers.decorator.startWizard(DownloadWizardProvider(), "Install New Game") }
|
btnAdd.setOnMouseClicked { Controllers.decorator.startWizard(DownloadWizardProvider(), "Install New Game") }
|
||||||
btnRefresh.setOnMouseClicked { Settings.selectedProfile.repository.refreshVersions() }
|
btnRefresh.setOnMouseClicked { Settings.selectedProfile.repository.refreshVersions() }
|
||||||
btnLaunch.setOnMouseClicked { LauncherHelper.launch() }
|
btnLaunch.setOnMouseClicked {
|
||||||
|
if (Settings.selectedAccount == null) {
|
||||||
|
Controllers.dialog(i18n("login.no_Player007"))
|
||||||
|
} else if (Settings.selectedProfile.selectedVersion == null) {
|
||||||
|
Controllers.dialog(i18n("minecraft.no_selected_version"))
|
||||||
|
} else
|
||||||
|
LauncherHelper.launch()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildNode(i: Int, profile: Profile, version: String, game: String, group: ToggleGroup): Node {
|
private fun buildNode(i: Int, profile: Profile, version: String, game: String, group: ToggleGroup): Node {
|
||||||
@ -78,7 +83,7 @@ class MainPage : StackPane(), DecoratorPage {
|
|||||||
lblVersionName.text = version
|
lblVersionName.text = version
|
||||||
btnDelete.setOnMouseClicked {
|
btnDelete.setOnMouseClicked {
|
||||||
profile.repository.removeVersionFromDisk(version)
|
profile.repository.removeVersionFromDisk(version)
|
||||||
Platform.runLater(this@MainPage::loadVersions)
|
Platform.runLater { loadVersions() }
|
||||||
}
|
}
|
||||||
btnSettings.setOnMouseClicked {
|
btnSettings.setOnMouseClicked {
|
||||||
Controllers.decorator.showPage(Controllers.versionPane)
|
Controllers.decorator.showPage(Controllers.versionPane)
|
||||||
@ -94,15 +99,15 @@ class MainPage : StackPane(), DecoratorPage {
|
|||||||
// TODO: Profiles
|
// TODO: Profiles
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onProfileChanged(event: ProfileChangedEvent) {
|
fun onProfileChanged(event: ProfileChangedEvent) = runOnUiThread {
|
||||||
val profile = event.value
|
val profile = event.value
|
||||||
profile.selectedVersionProperty.setChangedListener {
|
profile.selectedVersionProperty.setChangedListener { t ->
|
||||||
versionChanged(profile.selectedVersion)
|
versionChanged(profile.selectedVersion)
|
||||||
}
|
}
|
||||||
|
loadVersions(profile)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadVersions() {
|
private fun loadVersions(profile: Profile = Settings.selectedProfile) {
|
||||||
val profile = Settings.selectedProfile
|
|
||||||
val group = ToggleGroup()
|
val group = ToggleGroup()
|
||||||
val children = mutableListOf<Node>()
|
val children = mutableListOf<Node>()
|
||||||
var i = 0
|
var i = 0
|
||||||
@ -117,7 +122,7 @@ class MainPage : StackPane(), DecoratorPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
fun versionChanged(selectedVersion: String) {
|
fun versionChanged(selectedVersion: String?) {
|
||||||
masonryPane.children
|
masonryPane.children
|
||||||
.filter { it is RipplerContainer && it.properties["version"] is Pair<*, *> }
|
.filter { it is RipplerContainer && it.properties["version"] is Pair<*, *> }
|
||||||
.forEach { (it as RipplerContainer).selected = (it.properties["version"] as Pair<String, VersionListItem>).first == selectedVersion }
|
.forEach { (it as RipplerContainer).selected = (it.properties["version"] as Pair<String, VersionListItem>).first == selectedVersion }
|
||||||
|
84
HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/ProfilePage.kt
Normal file
84
HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/ProfilePage.kt
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* 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.JFXButton
|
||||||
|
import com.jfoenix.controls.JFXTextField
|
||||||
|
import javafx.beans.property.SimpleStringProperty
|
||||||
|
import javafx.fxml.FXML
|
||||||
|
import javafx.scene.layout.StackPane
|
||||||
|
import org.jackhuang.hmcl.i18n
|
||||||
|
import org.jackhuang.hmcl.setting.Profile
|
||||||
|
import org.jackhuang.hmcl.setting.Settings
|
||||||
|
import org.jackhuang.hmcl.ui.construct.FileItem
|
||||||
|
import org.jackhuang.hmcl.ui.wizard.DecoratorPage
|
||||||
|
import org.jackhuang.hmcl.util.onChangeAndOperate
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param profile null if creating a new profile.
|
||||||
|
*/
|
||||||
|
class ProfilePage(private val profile: Profile?): StackPane(), DecoratorPage {
|
||||||
|
override val titleProperty = SimpleStringProperty(this, "title",
|
||||||
|
if (profile == null) i18n("ui.newProfileWindow.title") else i18n("ui.label.profile") + " - " + profile.name)
|
||||||
|
private val locationProperty = SimpleStringProperty(this, "location",
|
||||||
|
profile?.gameDir?.absolutePath ?: "")
|
||||||
|
@FXML lateinit var txtProfileName: JFXTextField
|
||||||
|
@FXML lateinit var gameDir: FileItem
|
||||||
|
@FXML lateinit var btnSave: JFXButton
|
||||||
|
@FXML lateinit var btnDelete: JFXButton
|
||||||
|
|
||||||
|
init {
|
||||||
|
loadFXML("/assets/fxml/profile.fxml")
|
||||||
|
|
||||||
|
txtProfileName.text = profile?.name ?: ""
|
||||||
|
txtProfileName.textProperty().onChangeAndOperate {
|
||||||
|
btnSave.isDisable = !txtProfileName.validate() || locationProperty.get().isNullOrBlank()
|
||||||
|
}
|
||||||
|
gameDir.setProperty(locationProperty)
|
||||||
|
locationProperty.onChangeAndOperate {
|
||||||
|
btnSave.isDisable = !txtProfileName.validate() || locationProperty.get().isNullOrBlank()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profile == null)
|
||||||
|
btnDelete.isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onDelete() {
|
||||||
|
if (profile != null) {
|
||||||
|
Settings.deleteProfile(profile)
|
||||||
|
Controllers.navigate(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onSave() {
|
||||||
|
if (profile != null) { // editing a profile
|
||||||
|
profile.name = txtProfileName.text
|
||||||
|
if (locationProperty.get() != null)
|
||||||
|
profile.gameDir = File(locationProperty.get())
|
||||||
|
} else {
|
||||||
|
if (locationProperty.get().isNullOrBlank()) {
|
||||||
|
gameDir.onExplore()
|
||||||
|
}
|
||||||
|
Settings.putProfile(Profile(name = txtProfileName.text, initialGameDir = File(locationProperty.get())))
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings.onProfileLoading()
|
||||||
|
Controllers.navigate(null)
|
||||||
|
}
|
||||||
|
}
|
@ -61,7 +61,7 @@ class VersionItem(i: Int, group: ToggleGroup) : StackPane() {
|
|||||||
header.style = "-fx-background-radius: 2 2 0 0; -fx-background-color: " + headerColor
|
header.style = "-fx-background-radius: 2 2 0 0; -fx-background-color: " + headerColor
|
||||||
|
|
||||||
// create image view
|
// create image view
|
||||||
icon.translateYProperty().bind(Bindings.createDoubleBinding(Callable { header.boundsInParent.height - icon.height }, header.boundsInParentProperty(), icon.heightProperty()))
|
icon.translateYProperty().bind(Bindings.createDoubleBinding(Callable { header.boundsInParent.height - icon.height / 2 - 32.0 }, header.boundsInParentProperty(), icon.heightProperty()))
|
||||||
iconView.limitSize(32.0, 32.0)
|
iconView.limitSize(32.0, 32.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ import javafx.scene.control.Label
|
|||||||
import javafx.scene.image.ImageView
|
import javafx.scene.image.ImageView
|
||||||
import javafx.scene.layout.StackPane
|
import javafx.scene.layout.StackPane
|
||||||
|
|
||||||
class VersionListItem(versionName: String, gameVersion: String) : StackPane() {
|
class VersionListItem(versionName: String, gameVersion: String = "") : StackPane() {
|
||||||
|
|
||||||
@FXML lateinit var lblVersionName: Label
|
@FXML lateinit var lblVersionName: Label
|
||||||
@FXML lateinit var lblGameVersion: Label
|
@FXML lateinit var lblGameVersion: Label
|
||||||
|
@ -52,14 +52,7 @@ class FileItem : BorderPane() {
|
|||||||
right = JFXButton().apply {
|
right = JFXButton().apply {
|
||||||
graphic = SVG.pencil("black", 15.0, 15.0)
|
graphic = SVG.pencil("black", 15.0, 15.0)
|
||||||
styleClass += "toggle-icon4"
|
styleClass += "toggle-icon4"
|
||||||
setOnMouseClicked {
|
setOnMouseClicked { onExplore() }
|
||||||
val chooser = DirectoryChooser()
|
|
||||||
chooser.titleProperty().bind(titleProperty)
|
|
||||||
val selectedDir = chooser.showDialog(Controllers.stage)
|
|
||||||
if (selectedDir != null)
|
|
||||||
property.value = selectedDir.absolutePath
|
|
||||||
chooser.titleProperty().unbind()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Tooltip.install(this, Tooltip().apply {
|
Tooltip.install(this, Tooltip().apply {
|
||||||
@ -67,6 +60,15 @@ class FileItem : BorderPane() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onExplore() {
|
||||||
|
val chooser = DirectoryChooser()
|
||||||
|
chooser.titleProperty().bind(titleProperty)
|
||||||
|
val selectedDir = chooser.showDialog(Controllers.stage)
|
||||||
|
if (selectedDir != null)
|
||||||
|
property.value = selectedDir.absolutePath
|
||||||
|
chooser.titleProperty().unbind()
|
||||||
|
}
|
||||||
|
|
||||||
fun setProperty(property: Property<String>) {
|
fun setProperty(property: Property<String>) {
|
||||||
this.property = property
|
this.property = property
|
||||||
x.textProperty().bind(property)
|
x.textProperty().bind(property)
|
||||||
|
@ -27,6 +27,17 @@
|
|||||||
<center>
|
<center>
|
||||||
<AdvancedListBox fx:id="leftPane"/>
|
<AdvancedListBox fx:id="leftPane"/>
|
||||||
</center>
|
</center>
|
||||||
|
<bottom>
|
||||||
|
<BorderPane fx:id="menuBottomBar">
|
||||||
|
<right>
|
||||||
|
<JFXButton fx:id="addMenuButton" styleClass="toggle-icon4">
|
||||||
|
<graphic>
|
||||||
|
<fx:include source="/assets/svg/plus-black.fxml"/>
|
||||||
|
</graphic>
|
||||||
|
</JFXButton>
|
||||||
|
</right>
|
||||||
|
</BorderPane>
|
||||||
|
</bottom>
|
||||||
</BorderPane>
|
</BorderPane>
|
||||||
</center>
|
</center>
|
||||||
<right>
|
<right>
|
||||||
|
49
HMCL/src/main/resources/assets/fxml/profile.fxml
Normal file
49
HMCL/src/main/resources/assets/fxml/profile.fxml
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import com.jfoenix.controls.JFXButton?>
|
||||||
|
<?import com.jfoenix.controls.JFXTextField?>
|
||||||
|
<?import com.jfoenix.validation.RequiredFieldValidator?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.ScrollPane?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
<?import org.jackhuang.hmcl.ui.construct.ComponentList?>
|
||||||
|
<?import org.jackhuang.hmcl.ui.construct.FileItem?>
|
||||||
|
<fx:root xmlns="http://javafx.com/javafx"
|
||||||
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
|
type="StackPane">
|
||||||
|
<ScrollPane fx:id="scroll" fitToHeight="true" fitToWidth="true">
|
||||||
|
<VBox fx:id="rootPane" style="-fx-padding: 20;">
|
||||||
|
|
||||||
|
<ComponentList depth="1">
|
||||||
|
|
||||||
|
<BorderPane> <!-- Name -->
|
||||||
|
<left>
|
||||||
|
<VBox>
|
||||||
|
<Label text="%ui.label.profile" BorderPane.alignment="CENTER_LEFT"/>
|
||||||
|
</VBox>
|
||||||
|
</left>
|
||||||
|
<right>
|
||||||
|
<JFXTextField fx:id="txtProfileName" BorderPane.alignment="CENTER_RIGHT">
|
||||||
|
<validators>
|
||||||
|
<RequiredFieldValidator/>
|
||||||
|
</validators>
|
||||||
|
</JFXTextField>
|
||||||
|
</right>
|
||||||
|
</BorderPane>
|
||||||
|
|
||||||
|
<FileItem fx:id="gameDir" name="%settings.game_directory" title="%settings.choose_gamedir"/>
|
||||||
|
</ComponentList>
|
||||||
|
|
||||||
|
</VBox>
|
||||||
|
</ScrollPane>
|
||||||
|
<BorderPane pickOnBounds="false" style="-fx-padding: 20;">
|
||||||
|
<left>
|
||||||
|
<JFXButton BorderPane.alignment="BOTTOM_LEFT" fx:id="btnDelete" onMouseClicked="#onDelete" prefWidth="100" prefHeight="40"
|
||||||
|
buttonType="RAISED" text="%ui.button.delete" styleClass="jfx-button-raised" />
|
||||||
|
</left>
|
||||||
|
<right>
|
||||||
|
<JFXButton BorderPane.alignment="BOTTOM_RIGHT" fx:id="btnSave" onMouseClicked="#onSave" prefWidth="100" prefHeight="40"
|
||||||
|
buttonType="RAISED" text="%ui.button.save" styleClass="jfx-button-raised"/>
|
||||||
|
</right>
|
||||||
|
</BorderPane>
|
||||||
|
</fx:root>
|
@ -16,7 +16,7 @@
|
|||||||
<HBox alignment="CENTER" mouseTransparent="true">
|
<HBox alignment="CENTER" mouseTransparent="true">
|
||||||
<StackPane fx:id="imageViewContainer">
|
<StackPane fx:id="imageViewContainer">
|
||||||
<ImageView preserveRatio="true" fx:id="imageView" smooth="false">
|
<ImageView preserveRatio="true" fx:id="imageView" smooth="false">
|
||||||
<Image url="/assets/img/icon.png" requestedWidth="25" requestedHeight="25" />
|
<Image url="/assets/img/icon.png" />
|
||||||
</ImageView>
|
</ImageView>
|
||||||
</StackPane>
|
</StackPane>
|
||||||
<BorderPane style="-fx-padding: 0 0 0 10;">
|
<BorderPane style="-fx-padding: 0 0 0 10;">
|
||||||
|
@ -36,6 +36,8 @@ import java.util.logging.Level
|
|||||||
*/
|
*/
|
||||||
open class DefaultGameRepository(var baseDirectory: File): GameRepository {
|
open class DefaultGameRepository(var baseDirectory: File): GameRepository {
|
||||||
protected val versions: MutableMap<String, Version> = TreeMap<String, Version>()
|
protected val versions: MutableMap<String, Version> = TreeMap<String, Version>()
|
||||||
|
var isLoaded: Boolean = false
|
||||||
|
protected set
|
||||||
|
|
||||||
override fun hasVersion(id: String) = versions.containsKey(id)
|
override fun hasVersion(id: String) = versions.containsKey(id)
|
||||||
override fun getVersion(id: String): Version {
|
override fun getVersion(id: String): Version {
|
||||||
@ -135,7 +137,7 @@ open class DefaultGameRepository(var baseDirectory: File): GameRepository {
|
|||||||
versions[id] = version
|
versions[id] = version
|
||||||
EVENT_BUS.fireEvent(LoadedOneVersionEvent(this, id))
|
EVENT_BUS.fireEvent(LoadedOneVersionEvent(this, id))
|
||||||
}
|
}
|
||||||
|
isLoaded = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
|
@ -21,6 +21,9 @@ import javafx.beans.property.*
|
|||||||
import javafx.beans.value.ChangeListener
|
import javafx.beans.value.ChangeListener
|
||||||
import javafx.beans.value.ObservableValue
|
import javafx.beans.value.ObservableValue
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Any operation of properties should run on JavaFX thread.
|
||||||
|
*/
|
||||||
open class ImmediateStringProperty(bean: Any, name: String, initialValue: String): SimpleStringProperty(bean, name, initialValue) {
|
open class ImmediateStringProperty(bean: Any, name: String, initialValue: String): SimpleStringProperty(bean, name, initialValue) {
|
||||||
|
|
||||||
override fun set(newValue: String) {
|
override fun set(newValue: String) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user