mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-09-09 03:46:18 -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 getVersionSettingFile(id: String) = getVersionRoot(id).resolve("hmclversion.cfg")
|
||||
|
@ -41,11 +41,12 @@ object LauncherHelper {
|
||||
|
||||
fun launch() {
|
||||
val profile = Settings.selectedProfile
|
||||
val selectedVersion = profile.selectedVersion ?: throw IllegalStateException("No version here")
|
||||
val repository = profile.repository
|
||||
val dependency = profile.dependency
|
||||
val account = Settings.selectedAccount ?: throw IllegalStateException("No account here")
|
||||
val version = repository.getVersion(profile.selectedVersion)
|
||||
val setting = profile.getVersionSetting(profile.selectedVersion)
|
||||
val version = repository.getVersion(selectedVersion)
|
||||
val setting = profile.getVersionSetting(selectedVersion)
|
||||
var finished = 0
|
||||
|
||||
Controllers.dialog(launchingStepsPane)
|
||||
@ -53,7 +54,7 @@ object LauncherHelper {
|
||||
.then(dependency.checkGameCompletionAsync(version))
|
||||
|
||||
.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 {
|
||||
@ -69,7 +70,7 @@ object LauncherHelper {
|
||||
.then(task {
|
||||
it["launcher"] = HMCLGameLauncher(
|
||||
repository = repository,
|
||||
versionId = profile.selectedVersion,
|
||||
versionId = selectedVersion,
|
||||
options = setting.toLaunchOptions(profile.gameDir),
|
||||
listener = HMCLProcessListener(it["account"], setting),
|
||||
account = it["account"]
|
||||
|
@ -20,21 +20,24 @@ package org.jackhuang.hmcl.setting
|
||||
import com.google.gson.*
|
||||
import javafx.beans.InvalidationListener
|
||||
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.mod.ModManager
|
||||
import org.jackhuang.hmcl.util.ImmediateObjectProperty
|
||||
import org.jackhuang.hmcl.util.ImmediateStringProperty
|
||||
import org.jackhuang.hmcl.util.getValue
|
||||
import org.jackhuang.hmcl.util.setValue
|
||||
import org.jackhuang.hmcl.ui.runOnUiThread
|
||||
import org.jackhuang.hmcl.util.*
|
||||
import java.io.File
|
||||
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())
|
||||
var global: VersionSetting by globalProperty
|
||||
|
||||
val selectedVersionProperty = ImmediateStringProperty(this, "selectedVersion", initialSelectedVersion)
|
||||
var selectedVersion: String by selectedVersionProperty
|
||||
val selectedVersionProperty = ImmediateObjectProperty<String?>(this, "selectedVersion", initialSelectedVersion)
|
||||
var selectedVersion: String? by selectedVersionProperty
|
||||
|
||||
val gameDirProperty = ImmediateObjectProperty<File>(this, "gameDir", initialGameDir)
|
||||
var gameDir: File by gameDirProperty
|
||||
@ -44,17 +47,21 @@ class Profile(var name: String = "Default", initialGameDir: File = File(".minecr
|
||||
var modManager = ModManager(repository)
|
||||
|
||||
init {
|
||||
gameDirProperty.addListener { _ ->
|
||||
repository.baseDirectory = gameDir
|
||||
gameDirProperty.onChange { newGameDir ->
|
||||
repository.baseDirectory = newGameDir!!
|
||||
repository.refreshVersions()
|
||||
}
|
||||
|
||||
selectedVersionProperty.addListener { _ ->
|
||||
if (selectedVersion.isNotBlank() && !repository.hasVersion(selectedVersion)) {
|
||||
val newVersion = repository.getVersions().firstOrNull()
|
||||
// will cause anthor change event, we must ensure that there will not be dead recursion.
|
||||
selectedVersion = newVersion?.id ?: ""
|
||||
}
|
||||
selectedVersionProperty.addListener { _ -> verifySelectedVersion() }
|
||||
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()
|
||||
// will cause anthor change event, we must ensure that there will not be dead recursion.
|
||||
selectedVersion = newVersion?.id
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,10 +93,10 @@ class Profile(var name: String = "Default", initialGameDir: File = File(".minecr
|
||||
return vs
|
||||
}
|
||||
|
||||
fun getSelectedVersionSetting(): VersionSetting =
|
||||
getVersionSetting(selectedVersion)
|
||||
fun getSelectedVersionSetting(): VersionSetting? = if (selectedVersion == null) null else getVersionSetting(selectedVersion!!)
|
||||
|
||||
fun addPropertyChangedListener(listener: InvalidationListener) {
|
||||
nameProperty.addListener(listener)
|
||||
globalProperty.addListener(listener)
|
||||
selectedVersionProperty.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.MojangDownloadProvider
|
||||
import org.jackhuang.hmcl.event.EVENT_BUS
|
||||
import org.jackhuang.hmcl.task.Scheduler
|
||||
import org.jackhuang.hmcl.util.*
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
@ -270,12 +271,20 @@ object Settings {
|
||||
* PROFILES *
|
||||
****************************************/
|
||||
|
||||
val selectedProfile: Profile
|
||||
var selectedProfile: Profile
|
||||
get() {
|
||||
if (!hasProfile(SETTINGS.selectedProfile))
|
||||
if (!hasProfile(SETTINGS.selectedProfile)) {
|
||||
SETTINGS.selectedProfile = DEFAULT_PROFILE
|
||||
Scheduler.COMPUTATION.schedule { onProfileChanged() }
|
||||
}
|
||||
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 {
|
||||
var p: Profile? = getProfileMap()[name ?: DEFAULT_PROFILE]
|
||||
@ -316,12 +325,10 @@ object Settings {
|
||||
if (DEFAULT_PROFILE == ver) {
|
||||
return false
|
||||
}
|
||||
var notify = false
|
||||
if (selectedProfile.name == ver)
|
||||
notify = true
|
||||
val flag = getProfileMap().remove(ver) != null
|
||||
if (notify && flag)
|
||||
onProfileChanged()
|
||||
if (flag)
|
||||
Scheduler.COMPUTATION.schedule { onProfileLoading() }
|
||||
|
||||
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
|
||||
|
||||
// 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.isSelected = Settings.selectedAccount == account
|
||||
|
@ -24,6 +24,7 @@ import javafx.scene.image.Image
|
||||
import javafx.scene.layout.Region
|
||||
import javafx.stage.Stage
|
||||
import org.jackhuang.hmcl.Main
|
||||
import org.jackhuang.hmcl.setting.Settings
|
||||
|
||||
object Controllers {
|
||||
lateinit var scene: Scene private set
|
||||
@ -44,6 +45,8 @@ object Controllers {
|
||||
decorator.showPage(null)
|
||||
leftPaneController = LeftPaneController(decorator.leftPane)
|
||||
|
||||
Settings.onProfileLoading()
|
||||
|
||||
decorator.isCustomMaximize = false
|
||||
|
||||
scene = Scene(decorator, 800.0, 480.0)
|
||||
|
@ -17,54 +17,51 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui
|
||||
|
||||
import com.jfoenix.controls.JFXComboBox
|
||||
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.event.EVENT_BUS
|
||||
import org.jackhuang.hmcl.game.AccountHelper
|
||||
import org.jackhuang.hmcl.i18n
|
||||
import org.jackhuang.hmcl.setting.Settings
|
||||
import org.jackhuang.hmcl.ui.construct.IconedItem
|
||||
import org.jackhuang.hmcl.ui.construct.RipplerContainer
|
||||
import org.jackhuang.hmcl.util.onChangeAndOperate
|
||||
import java.util.*
|
||||
|
||||
class LeftPaneController(private val leftPane: AdvancedListBox) {
|
||||
val versionsPane = VBox()
|
||||
val cboProfiles = JFXComboBox<String>().apply { items.add("Default"); prefWidthProperty().bind(leftPane.widthProperty()) }
|
||||
val profilePane = VBox()
|
||||
val accountItem = VersionListItem("No Account", "unknown")
|
||||
|
||||
init {
|
||||
leftPane
|
||||
.startCategory("ACCOUNTS")
|
||||
.add(RipplerContainer(accountItem).apply {
|
||||
setOnMouseClicked {
|
||||
Controllers.navigate(AccountsPage())
|
||||
}
|
||||
accountItem.onSettingsButtonClicked {
|
||||
Controllers.navigate(AccountsPage())
|
||||
}
|
||||
})
|
||||
.startCategory(i18n("ui.label.profile"))
|
||||
.add(cboProfiles)
|
||||
.startCategory("LAUNCHER")
|
||||
.add(IconedItem(SVG.gear("black"), "Settings").apply {
|
||||
.add(IconedItem(SVG.gear("black"), i18n("launcher.title.launcher")).apply {
|
||||
prefWidthProperty().bind(leftPane.widthProperty())
|
||||
setOnMouseClicked {
|
||||
Controllers.navigate(Controllers.settingsPane)
|
||||
}
|
||||
})
|
||||
/* .startCategory(i18n("ui.label.version"))
|
||||
.add(versionsPane)
|
||||
.startCategory(i18n("ui.label.profile"))
|
||||
.add(profilePane)
|
||||
|
||||
EVENT_BUS.channel<RefreshedVersionsEvent>() += this::loadVersions
|
||||
EVENT_BUS.channel<ProfileLoadingEvent>() += this::onProfilesLoading
|
||||
EVENT_BUS.channel<ProfileChangedEvent>() += this::onProfileChanged
|
||||
|
||||
Settings.onProfileLoading()
|
||||
|
||||
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 {
|
||||
if (it == null) {
|
||||
@ -86,46 +83,36 @@ class LeftPaneController(private val leftPane: AdvancedListBox) {
|
||||
if (Settings.getAccounts().isEmpty())
|
||||
Controllers.navigate(AccountsPage())
|
||||
}
|
||||
/*
|
||||
fun onProfilesLoading() {
|
||||
// TODO: Profiles
|
||||
}
|
||||
|
||||
fun onProfileChanged(event: ProfileChangedEvent) {
|
||||
val profile = event.value
|
||||
profile.selectedVersionProperty.addListener { _ ->
|
||||
versionChanged(profile.selectedVersion)
|
||||
}
|
||||
profile.selectedVersionProperty.fireValueChangedEvent()
|
||||
|
||||
profilePane.children
|
||||
.filter { it is RipplerContainer && it.properties["profile"] is Pair<*, *> }
|
||||
.forEach { (it as RipplerContainer).selected = (it.properties["profile"] as Pair<String, VersionListItem>).first == profile.name }
|
||||
}
|
||||
|
||||
private fun loadVersions() {
|
||||
val profile = Settings.selectedProfile
|
||||
versionsPane.children.clear()
|
||||
profile.repository.getVersions().forEach { version ->
|
||||
val item = VersionListItem(version.id, minecraftVersion(profile.repository.getVersionJar(version.id)) ?: "Unknown")
|
||||
fun onProfilesLoading() {
|
||||
val list = LinkedList<RipplerContainer>()
|
||||
Settings.getProfiles().forEach { profile ->
|
||||
val item = VersionListItem(profile.name).apply {
|
||||
lblGameVersion.textProperty().bind(profile.selectedVersionProperty)
|
||||
}
|
||||
val ripplerContainer = RipplerContainer(item)
|
||||
item.onSettingsButtonClicked {
|
||||
Controllers.decorator.showPage(Controllers.versionPane)
|
||||
Controllers.versionPane.load(item.versionName, profile)
|
||||
Controllers.decorator.showPage(ProfilePage(profile))
|
||||
}
|
||||
ripplerContainer.ripplerFill = Paint.valueOf("#89E1F9")
|
||||
ripplerContainer.setOnMouseClicked {
|
||||
// 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
|
||||
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())
|
||||
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
|
||||
*/
|
||||
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 btnRefresh: JFXButton
|
||||
@ -59,15 +59,20 @@ class MainPage : StackPane(), DecoratorPage {
|
||||
btnLaunch.limitWidth(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<ProfileChangedEvent>() += this::onProfileChanged
|
||||
|
||||
Settings.onProfileLoading()
|
||||
|
||||
btnAdd.setOnMouseClicked { Controllers.decorator.startWizard(DownloadWizardProvider(), "Install New Game") }
|
||||
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 {
|
||||
@ -78,7 +83,7 @@ class MainPage : StackPane(), DecoratorPage {
|
||||
lblVersionName.text = version
|
||||
btnDelete.setOnMouseClicked {
|
||||
profile.repository.removeVersionFromDisk(version)
|
||||
Platform.runLater(this@MainPage::loadVersions)
|
||||
Platform.runLater { loadVersions() }
|
||||
}
|
||||
btnSettings.setOnMouseClicked {
|
||||
Controllers.decorator.showPage(Controllers.versionPane)
|
||||
@ -94,15 +99,15 @@ class MainPage : StackPane(), DecoratorPage {
|
||||
// TODO: Profiles
|
||||
}
|
||||
|
||||
fun onProfileChanged(event: ProfileChangedEvent) {
|
||||
fun onProfileChanged(event: ProfileChangedEvent) = runOnUiThread {
|
||||
val profile = event.value
|
||||
profile.selectedVersionProperty.setChangedListener {
|
||||
profile.selectedVersionProperty.setChangedListener { t ->
|
||||
versionChanged(profile.selectedVersion)
|
||||
}
|
||||
loadVersions(profile)
|
||||
}
|
||||
|
||||
private fun loadVersions() {
|
||||
val profile = Settings.selectedProfile
|
||||
private fun loadVersions(profile: Profile = Settings.selectedProfile) {
|
||||
val group = ToggleGroup()
|
||||
val children = mutableListOf<Node>()
|
||||
var i = 0
|
||||
@ -117,7 +122,7 @@ class MainPage : StackPane(), DecoratorPage {
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun versionChanged(selectedVersion: String) {
|
||||
fun versionChanged(selectedVersion: String?) {
|
||||
masonryPane.children
|
||||
.filter { it is RipplerContainer && it.properties["version"] is Pair<*, *> }
|
||||
.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
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ import javafx.scene.control.Label
|
||||
import javafx.scene.image.ImageView
|
||||
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 lblGameVersion: Label
|
||||
|
@ -52,14 +52,7 @@ class FileItem : BorderPane() {
|
||||
right = JFXButton().apply {
|
||||
graphic = SVG.pencil("black", 15.0, 15.0)
|
||||
styleClass += "toggle-icon4"
|
||||
setOnMouseClicked {
|
||||
val chooser = DirectoryChooser()
|
||||
chooser.titleProperty().bind(titleProperty)
|
||||
val selectedDir = chooser.showDialog(Controllers.stage)
|
||||
if (selectedDir != null)
|
||||
property.value = selectedDir.absolutePath
|
||||
chooser.titleProperty().unbind()
|
||||
}
|
||||
setOnMouseClicked { onExplore() }
|
||||
}
|
||||
|
||||
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>) {
|
||||
this.property = property
|
||||
x.textProperty().bind(property)
|
||||
|
@ -27,6 +27,17 @@
|
||||
<center>
|
||||
<AdvancedListBox fx:id="leftPane"/>
|
||||
</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>
|
||||
</center>
|
||||
<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">
|
||||
<StackPane fx:id="imageViewContainer">
|
||||
<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>
|
||||
</StackPane>
|
||||
<BorderPane style="-fx-padding: 0 0 0 10;">
|
||||
|
@ -36,6 +36,8 @@ import java.util.logging.Level
|
||||
*/
|
||||
open class DefaultGameRepository(var baseDirectory: File): GameRepository {
|
||||
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 getVersion(id: String): Version {
|
||||
@ -135,7 +137,7 @@ open class DefaultGameRepository(var baseDirectory: File): GameRepository {
|
||||
versions[id] = version
|
||||
EVENT_BUS.fireEvent(LoadedOneVersionEvent(this, id))
|
||||
}
|
||||
|
||||
isLoaded = true
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
|
@ -21,6 +21,9 @@ import javafx.beans.property.*
|
||||
import javafx.beans.value.ChangeListener
|
||||
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) {
|
||||
|
||||
override fun set(newValue: String) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user