aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/moe/nea/firmament/gui
diff options
context:
space:
mode:
authornea <nea@nea.moe>2023-05-24 02:29:20 +0200
committernea <nea@nea.moe>2023-05-24 02:29:20 +0200
commit5984383d2c48b4ae84bac1827bc6ca1891af8cf0 (patch)
tree90aaae93147ce7aad48777be488039626b70512a /src/main/kotlin/moe/nea/firmament/gui
parent5ff50799b6c8baf6dd87ecbf44860d19a4acceab (diff)
downloadfirmament-5984383d2c48b4ae84bac1827bc6ca1891af8cf0.tar.gz
firmament-5984383d2c48b4ae84bac1827bc6ca1891af8cf0.tar.bz2
firmament-5984383d2c48b4ae84bac1827bc6ca1891af8cf0.zip
Common config gui
Diffstat (limited to 'src/main/kotlin/moe/nea/firmament/gui')
-rw-r--r--src/main/kotlin/moe/nea/firmament/gui/ConfigGui.kt94
-rw-r--r--src/main/kotlin/moe/nea/firmament/gui/WGridPanelWithPadding.kt33
-rw-r--r--src/main/kotlin/moe/nea/firmament/gui/config/AllConfigsGui.kt42
-rw-r--r--src/main/kotlin/moe/nea/firmament/gui/config/BooleanHandler.kt33
-rw-r--r--src/main/kotlin/moe/nea/firmament/gui/config/ClickHandler.kt25
-rw-r--r--src/main/kotlin/moe/nea/firmament/gui/config/GuiAppender.kt44
-rw-r--r--src/main/kotlin/moe/nea/firmament/gui/config/ManagedConfig.kt153
-rw-r--r--src/main/kotlin/moe/nea/firmament/gui/config/StringHandler.kt32
-rw-r--r--src/main/kotlin/moe/nea/firmament/gui/repogui.kt36
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()
- }
- }
-}