Profiles editing and selecting

This commit is contained in:
huangyuhui 2017-08-25 20:18:27 +08:00
parent 3b4359f7eb
commit ff1a566bf4
17 changed files with 263 additions and 94 deletions

View File

@ -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")

View File

@ -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"]

View File

@ -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)

View File

@ -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
}

View File

@ -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

View File

@ -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)

View File

@ -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 }
}*/
}

View File

@ -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 }

View 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)
}
}

View File

@ -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)
}

View File

@ -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

View File

@ -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)

View File

@ -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>

View 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>

View File

@ -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;">

View File

@ -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

View File

@ -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) {