Load mod list asynchronously

This commit is contained in:
huangyuhui 2017-08-25 09:28:55 +08:00
parent 833247d133
commit cc2c186e7f
6 changed files with 64 additions and 40 deletions

View File

@ -17,7 +17,7 @@
*/
package org.jackhuang.hmcl.ui
import com.jfoenix.effects.JFXDepthManager
import com.jfoenix.controls.JFXTabPane
import javafx.fxml.FXML
import javafx.scene.control.ScrollPane
import javafx.scene.input.TransferMode
@ -29,11 +29,15 @@ import org.jackhuang.hmcl.mod.ModManager
import org.jackhuang.hmcl.task.Scheduler
import org.jackhuang.hmcl.task.task
import org.jackhuang.hmcl.util.onChange
import org.jackhuang.hmcl.util.onChangeAndOperateWeakly
import java.util.*
class ModController {
@FXML lateinit var scrollPane: ScrollPane
@FXML lateinit var rootPane: StackPane
@FXML lateinit var contentPane: VBox
@FXML lateinit var modPane: VBox
@FXML lateinit var contentPane: StackPane
lateinit var parentTab: JFXTabPane
private lateinit var modManager: ModManager
private lateinit var versionId: String
@ -61,23 +65,36 @@ class ModController {
this.modManager = modManager
this.versionId = versionId
task {
modManager.refreshMods(versionId)
}.subscribe(Scheduler.JAVAFX) {
contentPane.children.clear()
for (modInfo in modManager.getMods(versionId)) {
contentPane.children += ModItem(modInfo) {
modManager.removeMods(versionId, modInfo)
loadMods(modManager, versionId)
}.apply {
modInfo.activeProperty.onChange {
if (it)
styleClass -= "disabled"
else
synchronized(contentPane) {
runOnUiThread { rootPane.children -= contentPane }
modManager.refreshMods(versionId)
// Surprisingly, if there are a great number of mods, this processing will cause a UI pause.
// We must do this asynchronously.
val list = LinkedList<ModItem>()
for (modInfo in modManager.getMods(versionId)) {
list += ModItem(modInfo) {
modManager.removeMods(versionId, modInfo)
loadMods(modManager, versionId)
}.apply {
modInfo.activeProperty.onChange {
if (it)
styleClass -= "disabled"
else
styleClass += "disabled"
}
if (!modInfo.isActive)
styleClass += "disabled"
}
if (!modInfo.isActive)
styleClass += "disabled"
}
runOnUiThread { rootPane.children += contentPane }
it["list"] = list
}
}.subscribe(Scheduler.JAVAFX) { variables ->
parentTab.selectionModel.selectedItemProperty().onChangeAndOperateWeakly {
if (it?.userData == this) {
modPane.children.setAll(variables.get<List<ModItem>>("list"))
}
}
}
@ -88,11 +105,7 @@ class ModController {
chooser.title = i18n("mods.choose_mod")
chooser.extensionFilters.setAll(FileChooser.ExtensionFilter("Mod", "*.jar", "*.zip", "*.litemod"))
val res = chooser.showOpenDialog(Controllers.stage) ?: return
try {
modManager.addMod(versionId, res)
loadMods(modManager, versionId)
} catch (e: Exception) {
Controllers.dialog(i18n("mods.failed"))
}
task { modManager.addMod(versionId, res) }
.subscribe(task(Scheduler.JAVAFX) { loadMods(modManager, versionId) })
}
}

View File

@ -20,10 +20,12 @@ package org.jackhuang.hmcl.ui
import com.jfoenix.controls.JFXButton
import com.jfoenix.controls.JFXListView
import com.jfoenix.controls.JFXPopup
import com.jfoenix.controls.JFXTabPane
import javafx.beans.property.SimpleStringProperty
import javafx.beans.property.StringProperty
import javafx.fxml.FXML
import javafx.scene.control.Alert
import javafx.scene.control.Tab
import javafx.scene.control.Tooltip
import javafx.scene.layout.StackPane
import org.jackhuang.hmcl.download.game.GameAssetIndexDownloadTask
@ -36,6 +38,7 @@ class VersionPage : StackPane(), DecoratorPage {
override val titleProperty: StringProperty = SimpleStringProperty(this, "title", null)
@FXML lateinit var versionSettingsController: VersionSettingsController
@FXML lateinit var modTab: Tab
@FXML lateinit var modController: ModController
@FXML lateinit var installerController: InstallerController
@ -46,6 +49,7 @@ class VersionPage : StackPane(), DecoratorPage {
@FXML lateinit var btnExport: JFXButton
@FXML lateinit var rootPane: StackPane
@FXML lateinit var contentPane: StackPane
@FXML lateinit var tabPane: JFXTabPane
val browsePopup: JFXPopup
val managementPopup: JFXPopup
lateinit var profile: Profile
@ -71,6 +75,8 @@ class VersionPage : StackPane(), DecoratorPage {
titleProperty.set(i18n("launcher.title.game") + " - " + id)
versionSettingsController.loadVersionSetting(profile, id, profile.getVersionSetting(id))
modController.parentTab = tabPane
modTab.userData = modController
modController.loadMods(profile.modManager, id)
installerController.loadVersion(profile, id)
}

View File

@ -216,7 +216,7 @@ class VersionSettingsController {
private fun initJavaSubtitle(version: VersionSetting) {
task { it["java"] = version.javaVersion }
.then(task(Scheduler.JAVAFX) { componentJava.subtitle = it.get<JavaVersion?>("java")?.binary?.absolutePath ?: "Invalid Java Directory" })
.subscribe(task(Scheduler.JAVAFX) { componentJava.subtitle = it.get<JavaVersion?>("java")?.binary?.absolutePath ?: "Invalid Java Directory" })
}
fun onShowAdvanced() {

View File

@ -1,24 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import com.jfoenix.controls.JFXButton?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import com.jfoenix.controls.JFXSpinner?>
<StackPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:id="rootPane"
fx:controller="org.jackhuang.hmcl.ui.ModController">
<ScrollPane fx:id="scrollPane" fitToWidth="true" fitToHeight="true">
<VBox fx:id="contentPane" spacing="10" style="-fx-padding: 20;">
<JFXSpinner style="-fx-radius:16" styleClass="materialDesign-purple, first-spinner" />
<StackPane fx:id="contentPane">
<ScrollPane fx:id="scrollPane" fitToWidth="true" fitToHeight="true">
<VBox fx:id="modPane" spacing="10" style="-fx-padding: 20;">
</VBox>
</ScrollPane>
<VBox style="-fx-padding: 15;" spacing="15" pickOnBounds="false" alignment="BOTTOM_RIGHT">
<JFXButton prefWidth="40" prefHeight="40" buttonType="RAISED" onMouseClicked="#onAdd"
style="-fx-background-color:#5264AE;-fx-background-radius: 50px;">
<graphic>
<fx:include source="/assets/svg/plus.fxml"/>
</graphic>
</JFXButton>
</VBox>
</ScrollPane>
<VBox style="-fx-padding: 15;" spacing="15" pickOnBounds="false" alignment="BOTTOM_RIGHT">
<JFXButton prefWidth="40" prefHeight="40" buttonType="RAISED" onMouseClicked="#onAdd"
style="-fx-background-color:#5264AE;-fx-background-radius: 50px;">
<graphic>
<fx:include source="/assets/svg/plus.fxml" />
</graphic>
</JFXButton>
</VBox>
</StackPane>
</StackPane>

View File

@ -10,11 +10,11 @@
type="StackPane">
<JFXRippler />
<StackPane fx:id="contentPane">
<JFXTabPane>
<JFXTabPane fx:id="tabPane">
<Tab text="%settings">
<fx:include source="version-settings.fxml" fx:id="versionSettings"/>
</Tab>
<Tab text="%mods">
<Tab fx:id="modTab" text="%mods">
<fx:include source="mod.fxml" fx:id="mod"/>
</Tab>
<Tab text="%settings.tabs.installers">

View File

@ -23,6 +23,7 @@ import javafx.collections.ObservableList
fun <T> ObservableValue<T>.onChange(op: (T?) -> Unit) = apply { addListener { _, _, new -> op(new) } }
fun <T> ObservableValue<T>.onChangeAndOperate(op: (T?) -> Unit) = apply { addListener { _, _, new -> op(new) }; op(value) }
fun <T> ObservableValue<T>.onChangeAndOperateWeakly(op: (T?) -> Unit) = apply { addListener(WeakChangeListener { _, _, new -> op(new) }); op(value) }
fun ObservableBooleanValue.onChange(op: (Boolean) -> Unit) = apply { addListener { _, _, new -> op(new ?: false) } }
fun ObservableIntegerValue.onChange(op: (Int) -> Unit) = apply { addListener { _, _, new -> op((new ?: 0).toInt()) } }
fun ObservableLongValue.onChange(op: (Long) -> Unit) = apply { addListener { _, _, new -> op((new ?: 0L).toLong()) } }