From 1be0d68a5694eec0fc342310b7e28c2a54d3ef7d Mon Sep 17 00:00:00 2001 From: JFronny Date: Fri, 30 Jun 2023 14:02:31 +0200 Subject: [PATCH] Implement new instance window, bump java and GTK --- build.gradle.kts | 5 +- buildSrc/build.gradle.kts | 2 +- .../src/main/kotlin/inceptum.java.gradle.kts | 6 + launcher-gtk/build.gradle.kts | 26 +-- .../jfronny/inceptum/gtk/GtkEnvBackend.kt | 39 ++-- .../gitlab/jfronny/inceptum/gtk/GtkMenubar.kt | 48 +++-- .../jfronny/inceptum/gtk/control/ILabel.kt | 5 +- .../inceptum/gtk/control/InstanceThumbnail.kt | 4 +- .../jfronny/inceptum/gtk/control/KDropDown.kt | 32 ++++ .../jfronny/inceptum/gtk/control/KEntry.kt | 17 ++ .../gtk/control/assistant/AssistantPage.kt | 15 ++ .../gtk/control/assistant/KAssistant.kt | 33 ++++ .../inceptum/gtk/control/settings/IRow.kt | 17 +- .../gtk/control/settings/SettingsTab.kt | 2 - .../inceptum/gtk/window/NewInstanceWindow.kt | 15 -- .../gtk/window/create/NewInstanceWindow.kt | 179 ++++++++++++++++++ .../dialog/ProcessStateWatcherDialog.kt | 8 +- .../gtk/window/settings/instance/ExportTab.kt | 45 ++--- .../window/settings/instance/GeneralTab.kt | 6 +- .../window/settings/launcher/AccountsTab.kt | 2 +- .../model/mojang/VersionsListInfo.java | 6 + 21 files changed, 386 insertions(+), 126 deletions(-) create mode 100644 launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/KDropDown.kt create mode 100644 launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/KEntry.kt create mode 100644 launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/assistant/AssistantPage.kt create mode 100644 launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/assistant/KAssistant.kt delete mode 100644 launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/window/NewInstanceWindow.kt create mode 100644 launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/window/create/NewInstanceWindow.kt diff --git a/build.gradle.kts b/build.gradle.kts index 15d8457..53f536a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -17,10 +17,7 @@ val jbAnnotationsVersion by extra("24.0.1") val lwjglVersion by extra("3.3.2") val imguiVersion by extra("1.86.10") // launcher-gtk -val javagiVersion by extra("0.4") -val glibVersion by extra("1.2.10") -val gtkVersion by extra("4.8.3") -val adwaitaVersion by extra("1.2.0") +val javagiVersion by extra("v0.5.1") val flavorProp: String by extra(prop("flavor", "custom")) if (!setOf("custom", "maven", "fat", "windows", "linux", "macos").contains(flavorProp)) throw IllegalStateException("Unsupported flavor: $flavorProp") diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index fbd16f5..8e8b9f4 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -10,5 +10,5 @@ repositories { dependencies { implementation("gradle.plugin.com.github.johnrengelman:shadow:7.1.2") implementation("de.undercouch:gradle-download-task:5.1.2") - implementation("io.gitlab.jfronny:convention:1.3-SNAPSHOT") + implementation("io.gitlab.jfronny:convention:1.4-SNAPSHOT") } \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/inceptum.java.gradle.kts b/buildSrc/src/main/kotlin/inceptum.java.gradle.kts index 3e2813f..37f5cc8 100644 --- a/buildSrc/src/main/kotlin/inceptum.java.gradle.kts +++ b/buildSrc/src/main/kotlin/inceptum.java.gradle.kts @@ -3,6 +3,12 @@ plugins { `maven-publish` } +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(20)) + } +} + repositories { mavenCentral() maven("https://maven.frohnmeyer-wds.de/artifacts") diff --git a/launcher-gtk/build.gradle.kts b/launcher-gtk/build.gradle.kts index cd44268..e3158ce 100644 --- a/launcher-gtk/build.gradle.kts +++ b/launcher-gtk/build.gradle.kts @@ -3,7 +3,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { id("inceptum.application") id("com.github.johnrengelman.shadow") - kotlin("jvm") version "1.8.21" + kotlin("jvm") version "1.9.0-RC" } application { @@ -12,18 +12,19 @@ application { repositories { mavenLocal() - maven { url = uri("https://maven.frohnmeyer-wds.de/java-gi") } + maven("https://jitpack.io") { + content { + includeGroup("com.github.jwharm.java-gi") + } + } } dependencies { val javagiVersion: String by rootProject.extra - val glibVersion: String by rootProject.extra - val gtkVersion: String by rootProject.extra - val adwaitaVersion: String by rootProject.extra - implementation("io.github.jwharm.javagi:glib:$glibVersion-$javagiVersion") - implementation("io.github.jwharm.javagi:gtk:$gtkVersion-$javagiVersion") - implementation("io.github.jwharm.javagi:adwaita:$adwaitaVersion-$javagiVersion") + implementation("com.github.jwharm.java-gi:glib:$javagiVersion") + implementation("com.github.jwharm.java-gi:gtk:$javagiVersion") + implementation("com.github.jwharm.java-gi:adwaita:$javagiVersion") implementation(project(":launcher")) } @@ -51,13 +52,4 @@ tasks.runShadow { jvmArgs("--enable-preview", "--enable-native-access=ALL-UNNAMED") } -kotlin { - sourceSets.all { - languageSettings { - languageVersion = "2.0" - progressiveMode = true - } - } -} - tasks.withType(KotlinCompile::class) { compilerOptions.freeCompilerArgs.addAll("-Xlambdas=indy") } \ No newline at end of file diff --git a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/GtkEnvBackend.kt b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/GtkEnvBackend.kt index bcd90a8..e069d0b 100644 --- a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/GtkEnvBackend.kt +++ b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/GtkEnvBackend.kt @@ -2,11 +2,11 @@ package io.gitlab.jfronny.inceptum.gtk import io.gitlab.jfronny.commons.StringFormatter import io.gitlab.jfronny.inceptum.common.Utils -import io.gitlab.jfronny.inceptum.gtk.util.markup import io.gitlab.jfronny.inceptum.gtk.window.dialog.MicrosoftLoginDialog import io.gitlab.jfronny.inceptum.gtk.window.dialog.StringInputDialog import io.gitlab.jfronny.inceptum.launcher.LauncherEnv.EnvBackend import io.gitlab.jfronny.inceptum.launcher.api.account.MicrosoftAccount +import org.gnome.gio.Cancellable import org.gnome.gtk.* import java.util.function.Consumer @@ -16,21 +16,21 @@ object GtkEnvBackend : EnvBackend { override fun showError(message: String, title: String) { Utils.LOGGER.error(message) - simpleDialog(message, title, MessageType.ERROR, ButtonsType.CLOSE, null, null) + simpleDialog(message, title, null, null) } override fun showError(message: String, t: Throwable) { - simpleDialog(StringFormatter.toString(t), message, MessageType.ERROR, ButtonsType.CLOSE, null, null) + simpleDialog(StringFormatter.toString(t), message, null, null) } override fun showInfo(message: String, title: String) { Utils.LOGGER.info(message) - simpleDialog(message, title, MessageType.INFO, ButtonsType.CLOSE, null, null) + simpleDialog(message, title, null, null) } override fun showOkCancel(message: String, title: String, ok: Runnable, cancel: Runnable, defaultCancel: Boolean) { Utils.LOGGER.info(message) - simpleDialog(message, title, MessageType.QUESTION, ButtonsType.OK_CANCEL, ok, cancel) + simpleDialog(message, title, ok, cancel) } override fun getInput( @@ -52,37 +52,40 @@ object GtkEnvBackend : EnvBackend { ) dialog.title = prompt dialog.onResponse(processResponses(dialog, { ok.accept(dialog.input) }, cancel)) - dialog.show() + dialog.visible = true } override fun showLoginRefreshPrompt(account: MicrosoftAccount) = - schedule { MicrosoftLoginDialog(dialogParent, account).show() } + schedule { MicrosoftLoginDialog(dialogParent, account).visible = true } private fun simpleDialog( markup: String, title: String, - type: MessageType, - buttons: ButtonsType, ok: Runnable?, cancel: Runnable? - ) = schedule { simpleDialog(dialogParent, markup, title, type, buttons, ok, cancel) } + ) = schedule { simpleDialog(dialogParent, markup, title, ok, cancel) } @JvmStatic fun simpleDialog( parent: Window?, markup: String, title: String, - type: MessageType?, - buttons: ButtonsType?, ok: Runnable?, cancel: Runnable? ) { - val dialog = - MessageDialog(parent, DialogFlags.MODAL.or(DialogFlags.DESTROY_WITH_PARENT), type, buttons, null) - dialog.title = title - dialog.markup = markup - dialog.onResponse(processResponses(dialog, ok, cancel)) - dialog.show() + run { + val dialog = AlertDialog("") + dialog.message = title + dialog.detail = markup + dialog.modal = true + dialog.choose(parent, Cancellable()) { _, res, _ -> + val result = dialog.chooseFinish(res) + val cancelIdx = dialog.cancelButton + val defaultIdx = dialog.defaultButton + if (result == cancelIdx) cancel?.run() + if (result == defaultIdx) ok?.run() + } + } } private fun processResponses(dialog: Dialog, ok: Runnable?, cancel: Runnable?): Dialog.Response { diff --git a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/GtkMenubar.kt b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/GtkMenubar.kt index dfcc2b0..f7df6df 100644 --- a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/GtkMenubar.kt +++ b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/GtkMenubar.kt @@ -1,12 +1,13 @@ package io.gitlab.jfronny.inceptum.gtk +import io.gitlab.jfronny.commons.OSUtils import io.gitlab.jfronny.commons.io.JFiles import io.gitlab.jfronny.inceptum.common.MetaHolder import io.gitlab.jfronny.inceptum.common.Utils import io.gitlab.jfronny.inceptum.gtk.menu.MenuBuilder import io.gitlab.jfronny.inceptum.gtk.util.I18n import io.gitlab.jfronny.inceptum.gtk.window.AboutWindow -import io.gitlab.jfronny.inceptum.gtk.window.NewInstanceWindow +import io.gitlab.jfronny.inceptum.gtk.window.create.NewInstanceWindow import io.gitlab.jfronny.inceptum.gtk.window.dialog.MicrosoftLoginDialog import io.gitlab.jfronny.inceptum.gtk.window.dialog.ProcessStateWatcherDialog import io.gitlab.jfronny.inceptum.gtk.window.settings.launcher.LauncherSettingsWindow @@ -20,6 +21,8 @@ import io.gitlab.jfronny.inceptum.launcher.system.launch.InstanceLauncher import io.gitlab.jfronny.inceptum.launcher.system.launch.LaunchType import io.gitlab.jfronny.inceptum.launcher.system.setup.Steps import io.gitlab.jfronny.inceptum.launcher.util.ProcessState +import org.gnome.gdk.Clipboard +import org.gnome.gio.Cancellable import org.gnome.gtk.* import java.awt.Toolkit import java.awt.datatransfer.DataFlavor @@ -111,21 +114,34 @@ object GtkMenubar { dialog.show() } newMenu!!.button("url") { - LauncherEnv.getInput( - I18n["menu.file.new.url"], - I18n["menu.file.new.url.details"], - Toolkit.getDefaultToolkit().getSystemClipboard().getData(DataFlavor.stringFlavor) as String, - { s: String? -> - val state = ProcessState(Importers.MAX_STEPS, "Initializing") - ProcessStateWatcherDialog.show( - GtkEnvBackend.dialogParent, - I18n["menu.file.new.url"], - I18n["menu.file.new.url.error"], - state - ) { - Importers.importPack(s, state) - } - }, {}) + readClipboard { clipboard -> + LauncherEnv.getInput( + I18n["menu.file.new.url"], + I18n["menu.file.new.url.details"], + clipboard ?: "", + { s: String? -> + val state = ProcessState(Importers.MAX_STEPS, "Initializing") + ProcessStateWatcherDialog.show( + GtkEnvBackend.dialogParent, + I18n["menu.file.new.url"], + I18n["menu.file.new.url.error"], + state + ) { + Importers.importPack(s, state) + } + }, {}) + } + } + } + + private fun readClipboard(continuation: (String?) -> Unit) { + if (OSUtils.TYPE == OSUtils.Type.LINUX) { + val clipboard = GtkEnvBackend.dialogParent!!.display.clipboard + clipboard.readTextAsync(Cancellable()) { _, res, _ -> + continuation(clipboard.readTextFinish(res)) + } + } else { + continuation(Toolkit.getDefaultToolkit().systemClipboard.getData(DataFlavor.stringFlavor) as String?) } } diff --git a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/ILabel.kt b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/ILabel.kt index 5897c3a..fd9d8a2 100644 --- a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/ILabel.kt +++ b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/ILabel.kt @@ -26,7 +26,10 @@ class ILabel(str: @PropertyKey(resourceBundle = I18n.BUNDLE) String, mode: Mode, val provider = CssProvider() try { GtkMain::class.java.classLoader.getResourceAsStream("inceptum.css")!! - .use { provider.loadFromData(it.readAllBytes()) } + .use { + val bytes = it.readAllBytes() + provider.loadFromData(String(bytes), bytes.size.toLong()) + } } catch (t: Throwable) { throw RuntimeException(t) } diff --git a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/InstanceThumbnail.kt b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/InstanceThumbnail.kt index 7cb9cd9..7983b0f 100644 --- a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/InstanceThumbnail.kt +++ b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/InstanceThumbnail.kt @@ -4,10 +4,10 @@ import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance import org.gnome.gtk.Image import org.gnome.gtk.Spinner import org.gnome.gtk.Stack -import java.lang.foreign.Addressable +import java.lang.foreign.MemorySegment class InstanceThumbnail : Stack { - private constructor(address: Addressable) : super(address) + private constructor(address: MemorySegment) : super(address) constructor() : super() { val spinner = Spinner() diff --git a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/KDropDown.kt b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/KDropDown.kt new file mode 100644 index 0000000..2af8d4f --- /dev/null +++ b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/KDropDown.kt @@ -0,0 +1,32 @@ +package io.gitlab.jfronny.inceptum.gtk.control + +import org.gnome.gtk.DropDown +import org.gnome.gtk.PropertyExpression +import org.gnome.gtk.StringList +import org.gnome.gtk.StringObject +import java.util.ArrayList +import java.util.function.IntConsumer + +class KDropDown(options: Array, private val stringify: (T) -> String, selected: Int): DropDown(options.toModel(stringify), null) { + private val onChange = ArrayList() + + init { + this.selected = selected + onNotify("selected") { _ -> onChange.forEach { it.accept(this.selected) } } + expression = PropertyExpression(StringObject.getType(), null, "string") + } + + fun onChange(changed: IntConsumer) { + onChange.add(changed) + } + + fun updateOptions(newOptions: Array, newSelected: Int) { + selected = 0 + model = newOptions.toModel(stringify) + selected = newSelected + } + + companion object { + private fun Array.toModel(stringify: (T) -> String) = StringList(map(stringify).toTypedArray()) + } +} \ No newline at end of file diff --git a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/KEntry.kt b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/KEntry.kt new file mode 100644 index 0000000..defb4a3 --- /dev/null +++ b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/KEntry.kt @@ -0,0 +1,17 @@ +package io.gitlab.jfronny.inceptum.gtk.control + +import org.gnome.gtk.Entry +import java.util.function.Consumer + +class KEntry(value: String? = ""): Entry() { + private val onChange = ArrayList>() + + init { + text = value ?: "" + onChanged { onChange.forEach { it.accept(text) } } + } + + fun onChange(changed: Consumer) { + onChange.add(changed) + } +} \ No newline at end of file diff --git a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/assistant/AssistantPage.kt b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/assistant/AssistantPage.kt new file mode 100644 index 0000000..1658325 --- /dev/null +++ b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/assistant/AssistantPage.kt @@ -0,0 +1,15 @@ +package io.gitlab.jfronny.inceptum.gtk.control.assistant + +import org.gnome.gtk.AssistantPageType +import org.gnome.gtk.Box +import org.gnome.gtk.Orientation + +class AssistantPage(val title: String, val type: AssistantPageType): Box(Orientation.VERTICAL, 8) { + lateinit var setComplete: (Boolean) -> Unit + private val onOpen = ArrayList() + fun onOpen(action: Runnable) { + onOpen.add(action) + } + + fun emitOpen() = onOpen.forEach { it.run() } +} \ No newline at end of file diff --git a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/assistant/KAssistant.kt b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/assistant/KAssistant.kt new file mode 100644 index 0000000..e4292c6 --- /dev/null +++ b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/assistant/KAssistant.kt @@ -0,0 +1,33 @@ +package io.gitlab.jfronny.inceptum.gtk.control.assistant + +import io.gitlab.jfronny.inceptum.common.Utils +import org.gnome.gtk.Application +import org.gnome.gtk.Assistant +import org.gnome.gtk.AssistantPageType + +open class KAssistant(app: Application) : Assistant() { + private val pages = ArrayList() + + init { + application = app + onPrepare { next -> + val page = pages.firstOrNull { it.handle() == next.handle() } + if (page == null) { + Utils.LOGGER.error("Unknown page opened in assistant") + } else { + page.emitOpen() + } + } + } + + fun page(title: String, type: AssistantPageType, setup: AssistantPage.() -> Unit): Int { + val page = AssistantPage(title, type) + val idx = appendPage(page) + pages.add(page) + page.setComplete = { setPageComplete(page, it) } + page.setup() + setPageType(page, page.type) + setPageTitle(page, page.title) + return idx + } +} \ No newline at end of file diff --git a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/settings/IRow.kt b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/settings/IRow.kt index c70388c..bfb4504 100644 --- a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/settings/IRow.kt +++ b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/settings/IRow.kt @@ -1,6 +1,8 @@ package io.gitlab.jfronny.inceptum.gtk.control.settings import io.gitlab.jfronny.inceptum.gtk.control.ILabel +import io.gitlab.jfronny.inceptum.gtk.control.KDropDown +import io.gitlab.jfronny.inceptum.gtk.control.KEntry import io.gitlab.jfronny.inceptum.gtk.util.I18n import io.gitlab.jfronny.inceptum.gtk.util.margin import org.gnome.gtk.* @@ -41,12 +43,10 @@ class IRow( } } - fun setDropdown(options: Array, defaultIndex: Int, changed: IntConsumer): DropDown { - return DropDown(StringList(options), null).apply { + fun setDropdown(options: Array, defaultIndex: Int, changed: IntConsumer): KDropDown { + return KDropDown(options, { it } , defaultIndex).apply { + onChange(changed) packSmallEnd() - selected = defaultIndex - onNotify("selected") { _ -> changed.accept(selected) } - expression = PropertyExpression(StringObject.getType(), null, "string") } } @@ -69,13 +69,12 @@ class IRow( } } - fun setEntry(value: String?, changed: Consumer): Entry { - return Entry().apply { - text = value ?: "" + fun setEntry(value: String?, changed: Consumer): KEntry { + return KEntry(value).apply { hexpand = true valign = Align.CENTER halign = Align.FILL - onChanged { changed.accept(text) } + onChange(changed) append(this) } } diff --git a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/settings/SettingsTab.kt b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/settings/SettingsTab.kt index 475c087..a4bd221 100644 --- a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/settings/SettingsTab.kt +++ b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/settings/SettingsTab.kt @@ -81,8 +81,6 @@ open class SettingsTab(window: Window?) : Box(Orientation.VERTICAL, 8) { window, StringFormatter.toString(t), message, - MessageType.ERROR, - ButtonsType.CLOSE, null, null ) diff --git a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/window/NewInstanceWindow.kt b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/window/NewInstanceWindow.kt deleted file mode 100644 index 65d536f..0000000 --- a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/window/NewInstanceWindow.kt +++ /dev/null @@ -1,15 +0,0 @@ -package io.gitlab.jfronny.inceptum.gtk.window - -import org.gnome.gtk.* - -class NewInstanceWindow(app: Application) : Assistant() { - init { - application = app - run { - val initialPage = Box(Orientation.VERTICAL, 8) - initialPage.append(Label("Importing instances via this assistant is not yet supported, use the ImGUI")) - appendPage(initialPage) - setPageType(initialPage, AssistantPageType.INTRO) - } - } -} diff --git a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/window/create/NewInstanceWindow.kt b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/window/create/NewInstanceWindow.kt new file mode 100644 index 0000000..eefbdba --- /dev/null +++ b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/window/create/NewInstanceWindow.kt @@ -0,0 +1,179 @@ +package io.gitlab.jfronny.inceptum.gtk.window.create + +import io.gitlab.jfronny.commons.StringFormatter +import io.gitlab.jfronny.commons.io.JFiles +import io.gitlab.jfronny.inceptum.common.InceptumConfig +import io.gitlab.jfronny.inceptum.common.MetaHolder +import io.gitlab.jfronny.inceptum.common.Utils +import io.gitlab.jfronny.inceptum.gtk.control.KDropDown +import io.gitlab.jfronny.inceptum.gtk.control.KEntry +import io.gitlab.jfronny.inceptum.gtk.control.assistant.KAssistant +import io.gitlab.jfronny.inceptum.gtk.schedule +import io.gitlab.jfronny.inceptum.gtk.util.I18n +import io.gitlab.jfronny.inceptum.gtk.util.toTypedArray +import io.gitlab.jfronny.inceptum.gtk.window.dialog.ProcessStateWatcherDialog +import io.gitlab.jfronny.inceptum.launcher.api.FabricMetaApi +import io.gitlab.jfronny.inceptum.launcher.api.McApi +import io.gitlab.jfronny.inceptum.launcher.model.fabric.FabricVersionLoaderInfo +import io.gitlab.jfronny.inceptum.launcher.model.mojang.VersionsListInfo +import io.gitlab.jfronny.inceptum.launcher.system.instance.InstanceNameTool +import io.gitlab.jfronny.inceptum.launcher.system.instance.LoaderInfo +import io.gitlab.jfronny.inceptum.launcher.system.setup.SetupStepInfo +import io.gitlab.jfronny.inceptum.launcher.system.setup.Steps +import org.gnome.glib.GLib +import org.gnome.gtk.* +import java.io.IOException + + +class NewInstanceWindow(app: Application) : KAssistant(app) { + companion object { + private val VERSIONS = McApi.getVersions() + } + + init { + var gameVersion: VersionsListInfo? = null + var useFabric = false + var fabricVersion: FabricVersionLoaderInfo? = null + var name = "New Instance" + + var failureMessage = "Unknown error, please look at the log!" + var isFailure = false + + page("Welcome", AssistantPageType.INTRO) { + append(Label("This assistant will guide you through the process of setting up a Minecraft instance.\nTo begin, please choose the game version you want to use")) + + val versions = VERSIONS.versions.stream() + .filter { InceptumConfig.snapshots || it.type == "release" } + .toTypedArray() + val def = versions.withIndex().firstOrNull { it.value.id == VERSIONS.latest.release }?.index ?: 0 + gameVersion = versions[def] + + append(KDropDown(versions, { it.id }, def).apply { + onChange { gameVersion = versions[it] } + }) + + setComplete(true) + } + page("Loader", AssistantPageType.CONTENT) { + append(Label("Select a mod loader if you want to use mods in this instance. This can be changed later.")) + var lastGameVersion: VersionsListInfo? = null + var versions = arrayOf() + var def = 0 + + val none = CheckButton.newWithLabel("None") + val fabric = CheckButton.newWithLabel("Fabric") + none.onActivate { useFabric = false } + none.onActivate { useFabric = true } + append(none) + val fabricVersionDropdown = KDropDown(versions, { it.loader.version }, def) + fabricVersionDropdown.onChange { fabricVersion = versions[it] } + append(Box(Orientation.HORIZONTAL, 8).apply { + append(fabric) + append(fabricVersionDropdown) + }) + + onOpen { + if (lastGameVersion == null || lastGameVersion != gameVersion) { + versions = FabricMetaApi.getLoaderVersions(gameVersion!!).toTypedArray() + def = versions.withIndex().firstOrNull { it.value.loader.stable }?.index ?: 0 + fabricVersionDropdown.updateOptions(versions, def) + lastGameVersion = gameVersion + none.active = true + fabric.active = false + useFabric = false + } + + if (versions.isEmpty()) { + none.active = true + fabric.active = false + useFabric = false + fabric.sensitive = false + } + } + + setComplete(true) + } + page("Name", AssistantPageType.CONTENT) { + append(Label(I18n["instance.settings.general.name.placeholder"])) + val entry = KEntry(name) + entry.placeholderText = I18n["instance.settings.general.name.placeholder"] + entry.valign + entry.onChange { name = InstanceNameTool.getNextValid(it) } + append(entry) + onOpen { + name = InstanceNameTool.getDefaultName(gameVersion!!.id, useFabric) + entry.text = name + } + + setComplete(true) + } + page("Creating", AssistantPageType.PROGRESS) { + append(Label("Creating Instance")) + val progress = ProgressBar() + append(progress) + val stage = Label("") + append(stage) + onOpen { + commit() + val pState = Steps.createProcessState() + val state = SetupStepInfo( + McApi.getVersionInfo(gameVersion), + if (useFabric) LoaderInfo(fabricVersion!!.loader) else LoaderInfo.NONE, + name, + pState + ) + var finished = false + var cachedState: ProcessStateWatcherDialog.State? = null + addTickCallback { widget, _ -> + if (finished) return@addTickCallback GLib.SOURCE_REMOVE + val nc = ProcessStateWatcherDialog.State(pState) + if (nc != cachedState) { + cachedState = nc + stage.setMarkup(cachedState!!.msg) + progress.fraction = cachedState!!.progress.coerceAtMost(1f).toDouble() + widget.queueDraw() + } + GLib.SOURCE_CONTINUE + } + onClose { pState.cancel() } + onCancel { pState.cancel() } + pState.updateStep("Starting install process") + Thread { + try { + for (step in Steps.STEPS) { + if (state.isCancelled) { + try { + JFiles.deleteRecursive(MetaHolder.INSTANCE_DIR.resolve(state.name)) + } catch (e: IOException) { + Utils.LOGGER.error("Could not delete instance dir", e) + } + return@Thread + } + pState.incrementStep(step.name) + step.execute(state) + } + } catch (e: Throwable) { + pState.cancel() + Utils.LOGGER.error("Could not create instance") + failureMessage = StringFormatter.toString(e) + isFailure = true + } finally { + finished = true + schedule { setComplete(true) } + schedule { nextPage() } + } + }.start() + } + } + page("Done", AssistantPageType.SUMMARY) { + val status = Label("") + onOpen { + if (isFailure) { + status.setMarkup("Something went wrong while creating the instance.\n\n$failureMessage") + } else { + status.setMarkup("The instance was successfully created. You can now launch it using the main menu") + } + } + } + } +} diff --git a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/window/dialog/ProcessStateWatcherDialog.kt b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/window/dialog/ProcessStateWatcherDialog.kt index f6dd0df..8943f50 100644 --- a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/window/dialog/ProcessStateWatcherDialog.kt +++ b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/window/dialog/ProcessStateWatcherDialog.kt @@ -51,9 +51,7 @@ class ProcessStateWatcherDialog( (messageArea as Box).append(progress) addTickCallback { widget, _ -> if (finished) return@addTickCallback GLib.SOURCE_REMOVE - val nc = State( - state - ) + val nc = State(state) if (nc != cachedState) { cachedState = nc setMarkup(cachedState!!.msg) @@ -72,8 +70,6 @@ class ProcessStateWatcherDialog( parent, StringFormatter.toString(e), errorMessage, - MessageType.ERROR, - ButtonsType.CLOSE, null, null ) @@ -85,7 +81,7 @@ class ProcessStateWatcherDialog( } @JvmRecord - internal data class State(val msg: String, val progress: Float) { + data class State(val msg: String, val progress: Float) { constructor(source: ProcessState) : this(source.currentStep, source.progress) } diff --git a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/window/settings/instance/ExportTab.kt b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/window/settings/instance/ExportTab.kt index 8094a72..44e1ea7 100644 --- a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/window/settings/instance/ExportTab.kt +++ b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/window/settings/instance/ExportTab.kt @@ -1,7 +1,6 @@ package io.gitlab.jfronny.inceptum.gtk.window.settings.instance import io.gitlab.jfronny.inceptum.common.Utils -import io.gitlab.jfronny.inceptum.gtk.GtkEnvBackend import io.gitlab.jfronny.inceptum.gtk.control.settings.SettingsTab import io.gitlab.jfronny.inceptum.gtk.schedule import io.gitlab.jfronny.inceptum.gtk.util.I18n @@ -10,6 +9,7 @@ import io.gitlab.jfronny.inceptum.launcher.system.exporter.Exporter import io.gitlab.jfronny.inceptum.launcher.system.exporter.Exporters import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance import io.gitlab.jfronny.inceptum.launcher.util.ProcessState +import org.gnome.gio.Cancellable import org.gnome.gtk.* import java.nio.file.Path @@ -25,37 +25,20 @@ class ExportTab(private val instance: Instance, window: InstanceSettingsWindow?) for (exporter in Exporters.EXPORTERS) { row("instance.settings.export.title", "instance.settings.export.subtitle", exporter.name, exporter.fileExtension) { setButton("instance.settings.export") { - val dialog = FileChooserNative( - I18n["instance.settings.export.dialog.title", exporter.name], - window, - FileChooserAction.SAVE, - "_" + I18n["save"], - "_" + I18n["cancel"] - ) - val filter = FileFilter() - filter.name = exporter.name + " Pack" - filter.addPattern("*." + exporter.fileExtension) - dialog.addFilter(filter) - dialog.currentName = exporter.getDefaultFileName(instance) - dialog.onResponse { responseId: Int -> - if (responseId == ResponseType.ACCEPT.value) { - val file = dialog.file!!.path - if (file == null) { - GtkEnvBackend.simpleDialog( - window, - "The path returned by the file dialog is null", - "Could not export", - MessageType.ERROR, - ButtonsType.CLOSE, - null, - null - ) - return@onResponse - } - export(exporter, Path.of(file)) + run { + val dialog = FileDialog() + dialog.title = I18n["instance.settings.export.dialog.title", exporter.name] + val filters = AnyFilter() + val filter = FileFilter() + filter.name = exporter.name + " Pack" + filter.addPattern("*." + exporter.fileExtension) + filters.append(filter) + dialog.filters = filters + dialog.initialName = exporter.getDefaultFileName(instance) + dialog.save(window, Cancellable()) { _, res, _ -> + export(exporter, Path.of(dialog.saveFinish(res)?.path ?: return@save)) } } - dialog.show() } } } @@ -94,7 +77,7 @@ class ExportTab(private val instance: Instance, window: InstanceSettingsWindow?) else -> {} } } - success.show() + success.visible = true } } } diff --git a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/window/settings/instance/GeneralTab.kt b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/window/settings/instance/GeneralTab.kt index 92bd1df..668504e 100644 --- a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/window/settings/instance/GeneralTab.kt +++ b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/window/settings/instance/GeneralTab.kt @@ -12,6 +12,7 @@ import io.gitlab.jfronny.inceptum.gtk.util.I18n import io.gitlab.jfronny.inceptum.gtk.util.Memory import io.gitlab.jfronny.inceptum.gtk.util.markup import io.gitlab.jfronny.inceptum.gtk.util.toTypedArray +import io.gitlab.jfronny.inceptum.gtk.window.create.NewInstanceWindow import io.gitlab.jfronny.inceptum.launcher.api.FabricMetaApi import io.gitlab.jfronny.inceptum.launcher.api.McApi import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta @@ -65,8 +66,7 @@ class GeneralTab(instance: Instance, window: InstanceSettingsWindow) : SettingsT .filter { InceptumConfig.snapshots || it.type == "release" } .map { it.id } .toTypedArray() - var def = 0 - for (i in versions.indices) if (versions[i] == instance.gameVersion) def = i + val def = versions.withIndex().firstOrNull { it.value == instance.gameVersion }?.index ?: 0 row("instance.settings.general.game.version", "instance.settings.general.game.version.subtitle") { setDropdown(versions, def) { i -> @@ -111,7 +111,7 @@ class GeneralTab(instance: Instance, window: InstanceSettingsWindow) : SettingsT .map { it.loader.version } .orElse(null) fabricVersions = ver.map { Arrays.stream(it) } - .map{ it.map { l -> l.loader.version }.toTypedArray() } + .map { it.map { l -> l.loader.version }.toTypedArray() } .orElse(null) if (fabricVersions == null || fabricVersions!!.isEmpty()) { fabricEnabled.active = false diff --git a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/window/settings/launcher/AccountsTab.kt b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/window/settings/launcher/AccountsTab.kt index 8e3e564..ab28eeb 100644 --- a/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/window/settings/launcher/AccountsTab.kt +++ b/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/window/settings/launcher/AccountsTab.kt @@ -24,7 +24,7 @@ class AccountsTab(window: Window?) : SettingsTab(window) { clear() build() GtkMenubar.generateAccountsMenu(window!!.application!!) - }.show() + }.visible = true } } diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/model/mojang/VersionsListInfo.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/model/mojang/VersionsListInfo.java index 9ff2cd9..a11f4ea 100644 --- a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/model/mojang/VersionsListInfo.java +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/model/mojang/VersionsListInfo.java @@ -4,6 +4,7 @@ import io.gitlab.jfronny.gson.compile.annotations.GSerializable; import io.gitlab.jfronny.inceptum.common.GsonPreset; import java.util.Date; +import java.util.Objects; @GSerializable(configure = GsonPreset.Api.class) public class VersionsListInfo { @@ -24,4 +25,9 @@ public class VersionsListInfo { this.sha1 = vli.sha1; this.complianceLevel = vli.complianceLevel; } + + @Override + public boolean equals(Object obj) { + return obj instanceof VersionsListInfo li && Objects.equals(id, li.id); + } }