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

View File

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

View File

@ -216,7 +216,7 @@ class VersionSettingsController {
private fun initJavaSubtitle(version: VersionSetting) { private fun initJavaSubtitle(version: VersionSetting) {
task { it["java"] = version.javaVersion } 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() { fun onShowAdvanced() {

View File

@ -1,24 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import com.jfoenix.controls.JFXButton?> <?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" <StackPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml" xmlns:fx="http://javafx.com/fxml"
fx:id="rootPane" fx:id="rootPane"
fx:controller="org.jackhuang.hmcl.ui.ModController"> fx:controller="org.jackhuang.hmcl.ui.ModController">
<ScrollPane fx:id="scrollPane" fitToWidth="true" fitToHeight="true"> <JFXSpinner style="-fx-radius:16" styleClass="materialDesign-purple, first-spinner" />
<VBox fx:id="contentPane" spacing="10" style="-fx-padding: 20;"> <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> </VBox>
</ScrollPane> </StackPane>
<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"> type="StackPane">
<JFXRippler /> <JFXRippler />
<StackPane fx:id="contentPane"> <StackPane fx:id="contentPane">
<JFXTabPane> <JFXTabPane fx:id="tabPane">
<Tab text="%settings"> <Tab text="%settings">
<fx:include source="version-settings.fxml" fx:id="versionSettings"/> <fx:include source="version-settings.fxml" fx:id="versionSettings"/>
</Tab> </Tab>
<Tab text="%mods"> <Tab fx:id="modTab" text="%mods">
<fx:include source="mod.fxml" fx:id="mod"/> <fx:include source="mod.fxml" fx:id="mod"/>
</Tab> </Tab>
<Tab text="%settings.tabs.installers"> <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>.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>.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 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 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()) } } fun ObservableLongValue.onChange(op: (Long) -> Unit) = apply { addListener { _, _, new -> op((new ?: 0L).toLong()) } }