diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/ModController.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/ModController.kt index b6a13aa2b..2b64bde5c 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/ModController.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/ModController.kt @@ -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() + 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")) } } } @@ -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) }) } } \ No newline at end of file diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/VersionPage.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/VersionPage.kt index 1a51ec7fd..d57f79d82 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/VersionPage.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/VersionPage.kt @@ -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) } diff --git a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/VersionSettingsController.kt b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/VersionSettingsController.kt index 0c35613ca..2c082a4de 100644 --- a/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/VersionSettingsController.kt +++ b/HMCL/src/main/kotlin/org/jackhuang/hmcl/ui/VersionSettingsController.kt @@ -216,7 +216,7 @@ class VersionSettingsController { private fun initJavaSubtitle(version: VersionSetting) { task { it["java"] = version.javaVersion } - .then(task(Scheduler.JAVAFX) { componentJava.subtitle = it.get("java")?.binary?.absolutePath ?: "Invalid Java Directory" }) + .subscribe(task(Scheduler.JAVAFX) { componentJava.subtitle = it.get("java")?.binary?.absolutePath ?: "Invalid Java Directory" }) } fun onShowAdvanced() { diff --git a/HMCL/src/main/resources/assets/fxml/version/mod.fxml b/HMCL/src/main/resources/assets/fxml/version/mod.fxml index 724f89600..ea32b55c8 100644 --- a/HMCL/src/main/resources/assets/fxml/version/mod.fxml +++ b/HMCL/src/main/resources/assets/fxml/version/mod.fxml @@ -1,24 +1,28 @@ - - - + + + + - - + + + + + + + + + + + + - - - - - - - - + diff --git a/HMCL/src/main/resources/assets/fxml/version/version.fxml b/HMCL/src/main/resources/assets/fxml/version/version.fxml index 4686d011d..ca97f8682 100644 --- a/HMCL/src/main/resources/assets/fxml/version/version.fxml +++ b/HMCL/src/main/resources/assets/fxml/version/version.fxml @@ -10,11 +10,11 @@ type="StackPane"> - + - + diff --git a/HMCLCore/src/main/kotlin/org/jackhuang/hmcl/util/Lib.kt b/HMCLCore/src/main/kotlin/org/jackhuang/hmcl/util/Lib.kt index 2c312e0f4..36f9b6b27 100644 --- a/HMCLCore/src/main/kotlin/org/jackhuang/hmcl/util/Lib.kt +++ b/HMCLCore/src/main/kotlin/org/jackhuang/hmcl/util/Lib.kt @@ -23,6 +23,7 @@ import javafx.collections.ObservableList fun ObservableValue.onChange(op: (T?) -> Unit) = apply { addListener { _, _, new -> op(new) } } fun ObservableValue.onChangeAndOperate(op: (T?) -> Unit) = apply { addListener { _, _, new -> op(new) }; op(value) } +fun ObservableValue.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()) } }