diff options
author | nea <nea@nea.moe> | 2023-05-24 02:29:20 +0200 |
---|---|---|
committer | nea <nea@nea.moe> | 2023-05-24 02:29:20 +0200 |
commit | 5984383d2c48b4ae84bac1827bc6ca1891af8cf0 (patch) | |
tree | 90aaae93147ce7aad48777be488039626b70512a /src/main/kotlin/moe/nea/firmament/gui | |
parent | 5ff50799b6c8baf6dd87ecbf44860d19a4acceab (diff) | |
download | firmament-5984383d2c48b4ae84bac1827bc6ca1891af8cf0.tar.gz firmament-5984383d2c48b4ae84bac1827bc6ca1891af8cf0.tar.bz2 firmament-5984383d2c48b4ae84bac1827bc6ca1891af8cf0.zip |
Common config gui
Diffstat (limited to 'src/main/kotlin/moe/nea/firmament/gui')
9 files changed, 329 insertions, 163 deletions
diff --git a/src/main/kotlin/moe/nea/firmament/gui/ConfigGui.kt b/src/main/kotlin/moe/nea/firmament/gui/ConfigGui.kt deleted file mode 100644 index 6acf68a..0000000 --- a/src/main/kotlin/moe/nea/firmament/gui/ConfigGui.kt +++ /dev/null @@ -1,94 +0,0 @@ -package moe.nea.firmament.gui - -import io.github.cottonmc.cotton.gui.client.LightweightGuiDescription -import io.github.cottonmc.cotton.gui.widget.WButton -import io.github.cottonmc.cotton.gui.widget.WLabel -import io.github.cottonmc.cotton.gui.widget.WTextField -import io.github.cottonmc.cotton.gui.widget.WToggleButton -import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment -import io.github.cottonmc.cotton.gui.widget.data.Insets -import io.github.cottonmc.cotton.gui.widget.data.VerticalAlignment -import moe.nea.firmament.Firmament -import moe.nea.firmament.util.data.DataHolder -import net.minecraft.text.Text -import kotlin.reflect.KMutableProperty1 - -class ConfigGui<K>(val holder: DataHolder<K>, val build: ConfigGui<K>.() -> Unit) : LightweightGuiDescription() { - private val root = WGridPanelWithPadding(verticalPadding = 4) - private val reloadables = mutableListOf<(() -> Unit)>() - - init { - setRootPanel(root) - root.insets = Insets.ROOT_PANEL - build() - reload() - } - - fun title(text: Text) { - if (col != 0) { - Firmament.logger.warn("Set title not at the top of the ConfigGui") - } - val label = WLabel(text) - label.verticalAlignment = VerticalAlignment.TOP - label.horizontalAlignment = HorizontalAlignment.CENTER - root.add(label, 0, col, 11, 1) - col++ - } - - private fun label(text: Text) { - val label = WLabel(text) - label.verticalAlignment = VerticalAlignment.CENTER - root.add(label, 0, col, 5, 1) - } - - fun toggle(text: Text, prop: KMutableProperty1<K, Boolean>) { - val toggle = WToggleButton(text) - reloadables.add { toggle.toggle = prop.get(holder.data) } - toggle.setOnToggle { - prop.set(holder.data, true) - holder.markDirty() - } - root.add(toggle, 5, col, 6, 1) - label(text) - col++ - } - - fun button(text: Text, buttonText: Text, runnable: () -> Unit) { - val button = WButton(buttonText) - button.setOnClick { - runnable.invoke() - } - root.add(button, 5, col, 6, 1) - label(text) - col++ - } - - fun textfield( - text: Text, - background: Text, - prop: KMutableProperty1<K, String>, - maxLength: Int = 255 - ) { - val textfield = WTextField(background) - textfield.isEditable = true - reloadables.add { - textfield.text = prop.get(holder.data) - } - textfield.maxLength = maxLength - textfield.setChangedListener { - prop.set(holder.data, it) - holder.markDirty() - } - root.add(textfield, 5, col, 6, 11) - label(text) - col++ - } - - fun reload() { - reloadables.forEach { it.invoke() } - } - - private var col = 0 - - -} diff --git a/src/main/kotlin/moe/nea/firmament/gui/WGridPanelWithPadding.kt b/src/main/kotlin/moe/nea/firmament/gui/WGridPanelWithPadding.kt deleted file mode 100644 index 255b80d..0000000 --- a/src/main/kotlin/moe/nea/firmament/gui/WGridPanelWithPadding.kt +++ /dev/null @@ -1,33 +0,0 @@ -package moe.nea.firmament.gui - -import io.github.cottonmc.cotton.gui.widget.WPanelWithInsets -import io.github.cottonmc.cotton.gui.widget.WWidget -import io.github.cottonmc.cotton.gui.widget.data.Insets - -class WGridPanelWithPadding( - val grid: Int = 18, - val verticalPadding: Int = 0, - val horizontalPadding: Int = 0, -) : WPanelWithInsets() { - - private inline val vertOffset get() = grid + verticalPadding - private inline val horiOffset get() = grid + horizontalPadding - - fun add(w: WWidget, x: Int, y: Int, width: Int = 1, height: Int = 1) { - children.add(w) - w.parent = this - w.setLocation(x * horiOffset + insets.left, y * vertOffset + insets.top) - if (w.canResize()) - w.setSize( - grid + (horiOffset * (width - 1)), - grid + (vertOffset * (height - 1)), - ) - expandToFit(w, insets) - } - - override fun setInsets(insets: Insets): WGridPanelWithPadding { - super.setInsets(insets) - return this - } - -} diff --git a/src/main/kotlin/moe/nea/firmament/gui/config/AllConfigsGui.kt b/src/main/kotlin/moe/nea/firmament/gui/config/AllConfigsGui.kt new file mode 100644 index 0000000..687959d --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/gui/config/AllConfigsGui.kt @@ -0,0 +1,42 @@ +package moe.nea.firmament.gui.config + +import io.github.cottonmc.cotton.gui.client.BackgroundPainter +import io.github.cottonmc.cotton.gui.client.CottonClientScreen +import io.github.cottonmc.cotton.gui.client.LightweightGuiDescription +import io.github.cottonmc.cotton.gui.widget.WButton +import io.github.cottonmc.cotton.gui.widget.WGridPanel +import io.github.cottonmc.cotton.gui.widget.WLabel +import io.github.cottonmc.cotton.gui.widget.WListPanel +import io.github.cottonmc.cotton.gui.widget.data.Insets +import net.minecraft.text.Text +import moe.nea.firmament.features.FeatureManager +import moe.nea.firmament.repo.RepoManager +import moe.nea.firmament.util.ScreenUtil.setScreenLater + +object AllConfigsGui { + + fun showAllGuis() { + val lwgd = LightweightGuiDescription() + var screen: CottonClientScreen? = null + lwgd.setRootPanel(WListPanel( + listOf( + RepoManager.Config + ) + FeatureManager.allFeatures.mapNotNull { it.config }, ::WGridPanel + ) { config, panel -> + panel.insets = Insets.ROOT_PANEL + panel.backgroundPainter = BackgroundPainter.VANILLA + panel.add(WLabel(Text.translatable("firmament.config.${config.name}")), 0, 0, 10, 1) + panel.add(WButton(Text.translatable("firmanent.config.edit")).also { + it.setOnClick { + config.showConfigEditor(screen) + } + }, 0, 1, 10, 1) + println("Panel size: ${panel.width} ${panel.height}") + }.also { + it.setListItemHeight(52) + it.setSize(10 * 18 + 14 + 16, 300) + }) + screen = CottonClientScreen(lwgd) + setScreenLater(screen) + } +} diff --git a/src/main/kotlin/moe/nea/firmament/gui/config/BooleanHandler.kt b/src/main/kotlin/moe/nea/firmament/gui/config/BooleanHandler.kt new file mode 100644 index 0000000..fa56901 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/gui/config/BooleanHandler.kt @@ -0,0 +1,33 @@ +package moe.nea.firmament.gui.config + +import io.github.cottonmc.cotton.gui.widget.WLabel +import io.github.cottonmc.cotton.gui.widget.WToggleButton +import io.github.cottonmc.cotton.gui.widget.data.VerticalAlignment +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.boolean +import kotlinx.serialization.json.jsonPrimitive +import net.minecraft.text.Text + +class BooleanHandler(val config: ManagedConfig) : ManagedConfig.OptionHandler<Boolean> { + override fun toJson(element: Boolean): JsonElement? { + return JsonPrimitive(element) + } + + override fun fromJson(element: JsonElement): Boolean { + return element.jsonPrimitive.boolean + } + + override fun emitGuiElements(opt: ManagedConfig.Option<Boolean>, guiAppender: GuiAppender) { + guiAppender.appendLabeledRow( + opt.labelText, + WToggleButton(opt.labelText).apply { + guiAppender.onReload { toggle = opt.value } + setOnToggle { + opt.value = it + config.save() + } + } + ) + } +} diff --git a/src/main/kotlin/moe/nea/firmament/gui/config/ClickHandler.kt b/src/main/kotlin/moe/nea/firmament/gui/config/ClickHandler.kt new file mode 100644 index 0000000..4948ade --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/gui/config/ClickHandler.kt @@ -0,0 +1,25 @@ +package moe.nea.firmament.gui.config + +import io.github.cottonmc.cotton.gui.widget.WButton +import io.github.cottonmc.cotton.gui.widget.WLabel +import kotlinx.serialization.json.JsonElement +import net.minecraft.text.Text + +class ClickHandler(val config: ManagedConfig, val runnable: () -> Unit) : ManagedConfig.OptionHandler<Unit> { + override fun toJson(element: Unit): JsonElement? { + return null + } + + override fun fromJson(element: JsonElement) {} + + override fun emitGuiElements(opt: ManagedConfig.Option<Unit>, guiAppender: GuiAppender) { + guiAppender.appendLabeledRow( + Text.translatable("firmament.config.${config.name}.${opt.propertyName}"), + WButton(Text.translatable("firmament.config.${config.name}.${opt.propertyName}")).apply { + setOnClick { + runnable() + } + }, + ) + } +} diff --git a/src/main/kotlin/moe/nea/firmament/gui/config/GuiAppender.kt b/src/main/kotlin/moe/nea/firmament/gui/config/GuiAppender.kt new file mode 100644 index 0000000..fb0fb1d --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/gui/config/GuiAppender.kt @@ -0,0 +1,44 @@ +package moe.nea.firmament.gui.config + +import io.github.cottonmc.cotton.gui.widget.WGridPanel +import io.github.cottonmc.cotton.gui.widget.WLabel +import io.github.cottonmc.cotton.gui.widget.WWidget +import io.github.cottonmc.cotton.gui.widget.data.VerticalAlignment +import net.minecraft.text.Text + +class GuiAppender(val width: Int) { + private var row = 0 + internal val panel = WGridPanel().also { it.setGaps(4, 4) } + internal val reloadables = mutableListOf<(() -> Unit)>() + fun set(x: Int, y: Int, w: Int, h: Int, widget: WWidget) { + panel.add(widget, x, y, w, h) + } + + + fun onReload(reloadable: () -> Unit) { + reloadables.add(reloadable) + } + + fun skipRows(r: Int) { + row += r + } + + fun appendLabeledRow(label: Text, right: WWidget) { + appendSplitRow( + WLabel(label).setVerticalAlignment(VerticalAlignment.CENTER), + right + ) + } + + fun appendSplitRow(left: WWidget, right: WWidget) { + val lw = width / 2 + set(0, row, lw, 1, left) + set(lw, row, width - lw, 1, right) + skipRows(1) + } + + fun appendFullRow(widget: WWidget) { + set(0, row, width, 1, widget) + skipRows(1) + } +} diff --git a/src/main/kotlin/moe/nea/firmament/gui/config/ManagedConfig.kt b/src/main/kotlin/moe/nea/firmament/gui/config/ManagedConfig.kt new file mode 100644 index 0000000..b268be9 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/gui/config/ManagedConfig.kt @@ -0,0 +1,153 @@ +package moe.nea.firmament.gui.config + +import io.github.cottonmc.cotton.gui.client.CottonClientScreen +import io.github.cottonmc.cotton.gui.client.LightweightGuiDescription +import io.github.cottonmc.cotton.gui.widget.data.Insets +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonObject +import kotlin.io.path.createDirectories +import kotlin.io.path.readText +import kotlin.io.path.writeText +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty +import net.minecraft.client.gui.screen.Screen +import net.minecraft.text.Text +import moe.nea.firmament.Firmament +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.ScreenUtil.setScreenLater + +abstract class ManagedConfig(val name: String) { + + interface OptionHandler<T : Any> { + fun toJson(element: T): JsonElement? + fun fromJson(element: JsonElement): T + fun emitGuiElements(opt: Option<T>, guiAppender: GuiAppender) + } + + inner class Option<T : Any> internal constructor( + val config: ManagedConfig, + val propertyName: String, + val default: () -> T, + val handler: OptionHandler<T> + ) : ReadWriteProperty<Any?, T> { + + val rawLabelText = "firmament.config.${config.name}.${propertyName}" + val labelText = Text.translatable(rawLabelText) + + private lateinit var _value: T + private var loaded = false + var value: T + get() { + if (!loaded) + load() + return _value + } + set(value) { + loaded = true + _value = value + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { + this.value = value + } + + override fun getValue(thisRef: Any?, property: KProperty<*>): T { + return value + } + + private fun load() { + if (data.containsKey(propertyName)) { + try { + value = handler.fromJson(data[propertyName]!!) + } catch (e: Exception) { + Firmament.logger.error( + "Exception during loading of config file $name. This will reset this config.", + e + ) + } + } + value = default() + } + + internal fun toJson(): JsonElement? { + return handler.toJson(value) + } + + fun appendToGui(guiapp: GuiAppender) { + handler.emitGuiElements(this, guiapp) + } + } + + val file = Firmament.CONFIG_DIR.resolve("$name.json") + val data: JsonObject by lazy { + try { + Firmament.json.decodeFromString( + file.readText() + ) + } catch (e: Exception) { + Firmament.logger.info("Could not read config $name. Loading empty config.") + JsonObject(mutableMapOf()) + } + } + + fun save() { + val data = JsonObject(allOptions.mapNotNull { (key, value) -> + value.toJson()?.let { + key to it + } + }.toMap()) + file.parent.createDirectories() + file.writeText(Firmament.json.encodeToString(data)) + } + + + val allOptions = mutableMapOf<String, Option<*>>() + val sortedOptions = mutableListOf<Option<*>>() + + private var latestGuiAppender: GuiAppender? = null + + protected fun <T : Any> option(propertyName: String, default: () -> T, handler: OptionHandler<T>): Option<T> { + if (propertyName in allOptions) error("Cannot register the same name twice") + return Option(this, propertyName, default, handler).also { + allOptions[propertyName] = it + sortedOptions.add(it) + } + } + + protected fun toggle(propertyName: String, default: () -> Boolean): Option<Boolean> { + return option(propertyName, default, BooleanHandler(this)) + } + + protected fun button(propertyName: String, runnable: () -> Unit): Option<Unit> { + return option(propertyName, { }, ClickHandler(this, runnable)) + } + + protected fun string(propertyName: String, default: () -> String): Option<String> { + return option(propertyName, default, StringHandler(this)) + } + + + + fun reloadGui() { + latestGuiAppender?.reloadables?.forEach {it() } + } + + fun showConfigEditor(parent: Screen? = null) { + val lwgd = LightweightGuiDescription() + val guiapp = GuiAppender(20) + latestGuiAppender = guiapp + guiapp.panel.insets = Insets.ROOT_PANEL + sortedOptions.forEach { it.appendToGui(guiapp) } + guiapp.reloadables.forEach { it() } + lwgd.setRootPanel(guiapp.panel) + setScreenLater(object : CottonClientScreen(lwgd) { + override fun close() { + latestGuiAppender = null + MC.screen = parent + } + }) + } + +} diff --git a/src/main/kotlin/moe/nea/firmament/gui/config/StringHandler.kt b/src/main/kotlin/moe/nea/firmament/gui/config/StringHandler.kt new file mode 100644 index 0000000..b883e21 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/gui/config/StringHandler.kt @@ -0,0 +1,32 @@ +package moe.nea.firmament.gui.config + +import io.github.cottonmc.cotton.gui.widget.WLabel +import io.github.cottonmc.cotton.gui.widget.WTextField +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.jsonPrimitive +import net.minecraft.text.Text + +class StringHandler(val config: ManagedConfig) : ManagedConfig.OptionHandler<String> { + override fun toJson(element: String): JsonElement? { + return JsonPrimitive(element) + } + + override fun fromJson(element: JsonElement): String { + return element.jsonPrimitive.toString() + } + + override fun emitGuiElements(opt: ManagedConfig.Option<String>, guiAppender: GuiAppender) { + guiAppender.appendLabeledRow( + opt.labelText, + WTextField(opt.labelText).apply { + suggestion = Text.translatableWithFallback(opt.rawLabelText + ".hint", "") + guiAppender.onReload { text = opt.value } + setChangedListener { + opt.value = it + config.save() + } + } + ) + } +} diff --git a/src/main/kotlin/moe/nea/firmament/gui/repogui.kt b/src/main/kotlin/moe/nea/firmament/gui/repogui.kt deleted file mode 100644 index da681e4..0000000 --- a/src/main/kotlin/moe/nea/firmament/gui/repogui.kt +++ /dev/null @@ -1,36 +0,0 @@ -package moe.nea.firmament.gui - -import net.minecraft.text.Text -import moe.nea.firmament.repo.RepoManager - -fun repoGui(): ConfigGui<RepoManager.Config> { - return ConfigGui(RepoManager) { - title(Text.translatable("firmament.gui.repo.title")) - toggle(Text.translatable("firmament.gui.repo.autoupdate"), RepoManager.Config::autoUpdate) - textfield( - Text.translatable("firmament.gui.repo.username"), - Text.translatable("firmament.gui.repo.hint.username"), - RepoManager.Config::user, - maxLength = 255 - ) - textfield( - Text.translatable("firmament.gui.repo.reponame"), - Text.translatable("firmament.gui.repo.hint.reponame"), - RepoManager.Config::repo - ) - textfield( - Text.translatable("firmament.gui.repo.branch"), - Text.translatable("firmament.gui.repo.hint.branch"), - RepoManager.Config::branch - ) - button( - Text.translatable("firmament.gui.repo.reset.label"), - Text.translatable("firmament.gui.repo.reset"), - ) { - RepoManager.data.user = "NotEnoughUpdates" - RepoManager.data.repo = "NotEnoughUpdates-REPO" - RepoManager.data.branch = "dangerous" - reload() - } - } -} |