Add YACL config menu

This commit is contained in:
Linnea Gräf
2024-10-13 19:53:10 +02:00
parent e6142bb936
commit 87b8513730
13 changed files with 567 additions and 306 deletions

View File

@@ -85,6 +85,9 @@ allprojects {
maven("https://maven.azureaaron.net/snapshots") maven("https://maven.azureaaron.net/snapshots")
maven("https://maven.azureaaron.net/releases") maven("https://maven.azureaaron.net/releases")
maven("https://www.cursemaven.com") maven("https://www.cursemaven.com")
maven("https://maven.isxander.dev/releases") {
name = "Xander Maven"
}
mavenLocal() mavenLocal()
} }
} }
@@ -154,6 +157,7 @@ val SourceSet.modImplementationConfigurationName
val configuredSourceSet = createIsolatedSourceSet("configured") val configuredSourceSet = createIsolatedSourceSet("configured")
val sodiumSourceSet = createIsolatedSourceSet("sodium") val sodiumSourceSet = createIsolatedSourceSet("sodium")
val citResewnSourceSet = createIsolatedSourceSet("citresewn") val citResewnSourceSet = createIsolatedSourceSet("citresewn")
val yaclSourceSet = createIsolatedSourceSet("yacl")
val shadowMe by configurations.creating { val shadowMe by configurations.creating {
exclude(group = "org.jetbrains.kotlin") exclude(group = "org.jetbrains.kotlin")
@@ -223,6 +227,7 @@ dependencies {
(citResewnSourceSet.modImplementationConfigurationName)( (citResewnSourceSet.modImplementationConfigurationName)(
innerJarsOf("citresewn", dependencies.create(libs.citresewn.get())).asFileTree) innerJarsOf("citresewn", dependencies.create(libs.citresewn.get())).asFileTree)
(citResewnSourceSet.modImplementationConfigurationName)(libs.citresewn) (citResewnSourceSet.modImplementationConfigurationName)(libs.citresewn)
(yaclSourceSet.modImplementationConfigurationName)(libs.yacl)
// Actual dependencies // Actual dependencies
modCompileOnly(libs.rei.api) { modCompileOnly(libs.rei.api) {

View File

@@ -76,6 +76,9 @@ hypixelmodapi_fabric = "1.0.1+build.1+mc1.21"
# Update from https://github.com/shedaniel/fabric-asm or https://maven.shedaniel.me/me/shedaniel/mm/ # Update from https://github.com/shedaniel/fabric-asm or https://maven.shedaniel.me/me/shedaniel/mm/
manninghamMills = "2.4.1" manninghamMills = "2.4.1"
# Update from https://docs.isxander.dev/yet-another-config-lib/installing-yacl
yacl = "3.5.0+1.21-fabric"
[libraries] [libraries]
minecraft = { module = "com.mojang:minecraft", version.ref = "minecraft" } minecraft = { module = "com.mojang:minecraft", version.ref = "minecraft" }
fabric_loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric_loader" } fabric_loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric_loader" }
@@ -109,6 +112,7 @@ sodium = { module = "maven.modrinth:sodium", version.ref = "sodium" }
freecammod = { module = "maven.modrinth:freecam", version.ref = "freecammod" } freecammod = { module = "maven.modrinth:freecam", version.ref = "freecammod" }
citresewn = { module = "maven.modrinth:cit-resewn", version.ref = "citresewn" } citresewn = { module = "maven.modrinth:cit-resewn", version.ref = "citresewn" }
femalegender = { module = "maven.modrinth:female-gender", version.ref = "femalegender" } femalegender = { module = "maven.modrinth:female-gender", version.ref = "femalegender" }
yacl = { module = "dev.isxander:yet-another-config-lib", version.ref = "yacl" }
[bundles] [bundles]
runtime_required = [ runtime_required = [

View File

@@ -0,0 +1,16 @@
package moe.nea.firmament.compat.yacl
import dev.isxander.yacl3.api.Controller
import dev.isxander.yacl3.api.Option
import dev.isxander.yacl3.api.controller.ControllerBuilder
import moe.nea.firmament.gui.config.ManagedOption
import moe.nea.firmament.keybindings.SavedKeyBinding
class KeybindingBuilder(
val option: Option<SavedKeyBinding>,
val managedOption: ManagedOption<SavedKeyBinding>
) : ControllerBuilder<SavedKeyBinding> {
override fun build(): Controller<SavedKeyBinding> {
return KeybindingController(option, managedOption)
}
}

View File

@@ -0,0 +1,71 @@
package moe.nea.firmament.compat.yacl
import dev.isxander.yacl3.api.Controller
import dev.isxander.yacl3.api.Option
import dev.isxander.yacl3.api.utils.Dimension
import dev.isxander.yacl3.gui.AbstractWidget
import dev.isxander.yacl3.gui.YACLScreen
import dev.isxander.yacl3.gui.controllers.ControllerWidget
import net.minecraft.text.Text
import moe.nea.firmament.gui.config.KeyBindingHandler
import moe.nea.firmament.gui.config.KeyBindingStateManager
import moe.nea.firmament.gui.config.ManagedOption
import moe.nea.firmament.keybindings.SavedKeyBinding
class KeybindingController(
val option: Option<SavedKeyBinding>,
val managedOption: ManagedOption<SavedKeyBinding>,
) : Controller<SavedKeyBinding> {
val handler = managedOption.handler as KeyBindingHandler
override fun option(): Option<SavedKeyBinding> {
return option
}
override fun formatValue(): Text {
return option.pendingValue().format()
}
override fun provideWidget(screen: YACLScreen, widgetDimension: Dimension<Int>): AbstractWidget {
lateinit var button: ControllerWidget<KeybindingController>
val sm = KeyBindingStateManager(
{ option.pendingValue() },
{ option.requestSet(it) },
{ screen.focused = null },
{ screen.focused = button }
)
button = object : ControllerWidget<KeybindingController>(this, screen, widgetDimension) {
override fun getHoveredControlWidth(): Int {
return 130
}
override fun getValueText(): Text {
return sm.label
}
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
return sm.keyboardEvent(keyCode, true)
}
override fun keyReleased(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
return sm.keyboardEvent(keyCode, false)
}
override fun unfocus() {
sm.onLostFocus()
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
if (button == 0 && isHovered) {
sm.onClick()
return true
}
return super.mouseClicked(mouseX, mouseY, button)
}
}
option.addListener { t, u ->
sm.updateLabel()
}
sm.updateLabel()
return button
}
}

View File

@@ -0,0 +1,119 @@
package moe.nea.firmament.compat.yacl
import com.google.auto.service.AutoService
import dev.isxander.yacl3.api.Binding
import dev.isxander.yacl3.api.ButtonOption
import dev.isxander.yacl3.api.ConfigCategory
import dev.isxander.yacl3.api.LabelOption
import dev.isxander.yacl3.api.Option
import dev.isxander.yacl3.api.YetAnotherConfigLib
import dev.isxander.yacl3.api.controller.ControllerBuilder
import dev.isxander.yacl3.api.controller.DoubleSliderControllerBuilder
import dev.isxander.yacl3.api.controller.IntegerSliderControllerBuilder
import dev.isxander.yacl3.api.controller.StringControllerBuilder
import dev.isxander.yacl3.api.controller.TickBoxControllerBuilder
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import kotlin.time.DurationUnit
import net.minecraft.client.gui.screen.Screen
import net.minecraft.text.Text
import moe.nea.firmament.gui.config.AllConfigsGui
import moe.nea.firmament.gui.config.BooleanHandler
import moe.nea.firmament.gui.config.ClickHandler
import moe.nea.firmament.gui.config.DurationHandler
import moe.nea.firmament.gui.config.FirmamentConfigScreenProvider
import moe.nea.firmament.gui.config.HudMeta
import moe.nea.firmament.gui.config.HudMetaHandler
import moe.nea.firmament.gui.config.IntegerHandler
import moe.nea.firmament.gui.config.KeyBindingHandler
import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.gui.config.ManagedOption
import moe.nea.firmament.gui.config.StringHandler
import moe.nea.firmament.keybindings.SavedKeyBinding
import moe.nea.firmament.util.FirmFormatters
@AutoService(FirmamentConfigScreenProvider::class)
class YaclIntegration : FirmamentConfigScreenProvider {
fun buildCategories() =
AllConfigsGui.allConfigs
.map(::buildCategory)
private fun buildCategory(managedConfig: ManagedConfig): ConfigCategory {
return ConfigCategory.createBuilder()
.name(managedConfig.labelText)
.also {
it.rootGroupBuilder()
.options(buildOptions(managedConfig.sortedOptions))
}
.build()
}
fun buildOptions(options: List<ManagedOption<*>>): Collection<Option<*>> =
options.map { buildOption(it) }
private fun <T : Any> buildOption(managedOption: ManagedOption<T>): Option<*> {
val handler = managedOption.handler
val binding = Binding.generic(managedOption.default(), managedOption::value, managedOption::value.setter)
fun <T> createDefaultBinding(function: (Option<T>) -> ControllerBuilder<T>): Option.Builder<T> {
return Option.createBuilder<T>()
.name(managedOption.labelText)
.binding(binding as Binding<T>)
.controller { function(it) }
}
when (handler) {
is ClickHandler -> return ButtonOption.createBuilder()
.name(managedOption.labelText)
.action { t, u ->
handler.runnable()
}
.build()
is HudMetaHandler -> return ButtonOption.createBuilder()
.name(managedOption.labelText)
.action { t, u ->
handler.openEditor(managedOption as ManagedOption<HudMeta>, t)
}
.build()
is BooleanHandler -> return createDefaultBinding(TickBoxControllerBuilder::create).build()
is StringHandler -> return createDefaultBinding(StringControllerBuilder::create).build()
is IntegerHandler -> return createDefaultBinding {
IntegerSliderControllerBuilder.create(it).range(handler.min, handler.max).step(1)
}.build()
is DurationHandler -> return Option.createBuilder<Double>()
.name(managedOption.labelText)
.binding((binding as Binding<Duration>).xmap({ it.toDouble(DurationUnit.SECONDS) }, { it.seconds }))
.controller {
DoubleSliderControllerBuilder.create(it)
.formatValue { Text.literal(FirmFormatters.formatTimespan(it.seconds)) }
.step(0.1)
.range(handler.min.toDouble(DurationUnit.SECONDS), handler.max.toDouble(DurationUnit.SECONDS))
}
.build()
is KeyBindingHandler -> return createDefaultBinding {
KeybindingBuilder(it, managedOption as ManagedOption<SavedKeyBinding>)
}.build()
else -> return LabelOption.create(Text.literal("This option is currently unhandled for this config menu. Please report this as a bug."))
}
}
fun buildConfig(): YetAnotherConfigLib {
return YetAnotherConfigLib.createBuilder()
.title(Text.literal("Firmament"))
.categories(buildCategories())
.build()
}
override val key: String
get() = "yacl"
override fun open(parent: Screen?): Screen {
return buildConfig().generateScreen(parent)
}
}

View File

@@ -39,7 +39,8 @@ object AllConfigsGui {
} }
fun makeScreen(parent: Screen? = null): Screen { fun makeScreen(parent: Screen? = null): Screen {
val provider = FirmamentConfigScreenProvider.providers.find { it.key == "builtin" } val wantedKey = if (RepoManager.Config.enableYacl) "yacl" else "builtin"
val provider = FirmamentConfigScreenProvider.providers.find { it.key == wantedKey }
?: FirmamentConfigScreenProvider.providers.first() ?: FirmamentConfigScreenProvider.providers.first()
return provider.open(parent) return provider.open(parent)
} }

View File

@@ -21,7 +21,14 @@ interface FirmamentConfigScreenProvider {
Firmament.logger.warn("Could not load config provider ${service.type()}", it) Firmament.logger.warn("Could not load config provider ${service.type()}", it)
null null
} }
}.filter { it.isEnabled }.toList() }.filter { it.isEnabled }
.sortedWith(Comparator.comparing(
{ it.key },
Comparator<String> { left, right ->
if (left == "builtin") return@Comparator -1
if (right == "builtin") return@Comparator 1
return@Comparator left.compareTo(right)
})).toList()
} }
} }
} }

View File

@@ -1,5 +1,3 @@
package moe.nea.firmament.gui.config package moe.nea.firmament.gui.config
import io.github.notenoughupdates.moulconfig.gui.component.TextComponent import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
@@ -7,6 +5,7 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.decodeFromJsonElement import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.encodeToJsonElement import kotlinx.serialization.json.encodeToJsonElement
import net.minecraft.client.gui.screen.Screen
import net.minecraft.text.MutableText import net.minecraft.text.MutableText
import net.minecraft.text.Text import net.minecraft.text.Text
import moe.nea.firmament.gui.FirmButtonComponent import moe.nea.firmament.gui.FirmButtonComponent
@@ -23,6 +22,13 @@ class HudMetaHandler(val config: ManagedConfig, val label: MutableText, val widt
return HudMeta(Json.decodeFromJsonElement(element), label, width, height) return HudMeta(Json.decodeFromJsonElement(element), label, width, height)
} }
fun openEditor(option: ManagedOption<HudMeta>, oldScreen: Screen) {
MC.screen = JarvisIntegration.jarvis.getHudEditor(
oldScreen,
listOf(option.value)
)
}
override fun emitGuiElements(opt: ManagedOption<HudMeta>, guiAppender: GuiAppender) { override fun emitGuiElements(opt: ManagedOption<HudMeta>, guiAppender: GuiAppender) {
guiAppender.appendLabeledRow( guiAppender.appendLabeledRow(
opt.labelText, opt.labelText,
@@ -30,10 +36,7 @@ class HudMetaHandler(val config: ManagedConfig, val label: MutableText, val widt
TextComponent( TextComponent(
Text.stringifiedTranslatable("firmament.hud.edit", label).string), Text.stringifiedTranslatable("firmament.hud.edit", label).string),
) { ) {
MC.screen = JarvisIntegration.jarvis.getHudEditor( openEditor(opt, guiAppender.screenAccessor())
guiAppender.screenAccessor.invoke(),
listOf(opt.value)
)
}) })
} }
} }

View File

@@ -1,5 +1,3 @@
package moe.nea.firmament.gui.config package moe.nea.firmament.gui.config
import io.github.notenoughupdates.moulconfig.common.IMinecraft import io.github.notenoughupdates.moulconfig.common.IMinecraft
@@ -13,8 +11,6 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.decodeFromJsonElement import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.encodeToJsonElement import kotlinx.serialization.json.encodeToJsonElement
import net.minecraft.text.Text
import net.minecraft.util.Formatting
import moe.nea.firmament.gui.FirmButtonComponent import moe.nea.firmament.gui.FirmButtonComponent
import moe.nea.firmament.keybindings.FirmamentKeyBindings import moe.nea.firmament.keybindings.FirmamentKeyBindings
import moe.nea.firmament.keybindings.SavedKeyBinding import moe.nea.firmament.keybindings.SavedKeyBinding
@@ -35,114 +31,42 @@ class KeyBindingHandler(val name: String, val managedConfig: ManagedConfig) :
} }
override fun emitGuiElements(opt: ManagedOption<SavedKeyBinding>, guiAppender: GuiAppender) { override fun emitGuiElements(opt: ManagedOption<SavedKeyBinding>, guiAppender: GuiAppender) {
var editing = false lateinit var button: FirmButtonComponent
var lastPressed = 0 val sm = KeyBindingStateManager(
var lastPressedNonModifier = 0 { opt.value },
var label: String = "" { opt.value = it },
var button: FirmButtonComponent? = null { button.blur() },
fun updateLabel() { { button.requestFocus() }
var stroke = opt.value.format() )
if (editing) {
stroke = Text.literal("")
val (shift, alt, ctrl) = SavedKeyBinding.getMods(SavedKeyBinding.getModInt())
if (shift) {
stroke.append("SHIFT + ")
}
if (alt) {
stroke.append("ALT + ")
}
if (ctrl) {
stroke.append("CTRL + ")
}
stroke.append("???")
stroke.styled { it.withColor(Formatting.YELLOW) }
}
label = (stroke).string
managedConfig.save()
}
button = object : FirmButtonComponent( button = object : FirmButtonComponent(
TextComponent( TextComponent(
IMinecraft.instance.defaultFontRenderer, IMinecraft.instance.defaultFontRenderer,
{ label }, { sm.label.string },
130, 130,
TextComponent.TextAlignment.LEFT, TextComponent.TextAlignment.LEFT,
false, false,
false false
), action = { ), action = {
if (editing) { sm.onClick()
button!!.blur()
} else {
editing = true
button!!.requestFocus()
updateLabel()
}
}) { }) {
override fun keyboardEvent(event: KeyboardEvent, context: GuiImmediateContext): Boolean { override fun keyboardEvent(event: KeyboardEvent, context: GuiImmediateContext): Boolean {
if (event is KeyboardEvent.KeyPressed) { if (event is KeyboardEvent.KeyPressed) {
return if (event.pressed) onKeyPressed(event.keycode, SavedKeyBinding.getModInt()) return sm.keyboardEvent(event.keycode, event.pressed)
else onKeyReleased(event.keycode, SavedKeyBinding.getModInt())
} }
return super.keyboardEvent(event, context) return super.keyboardEvent(event, context)
} }
override fun getBackground(context: GuiImmediateContext): NinePatch<MyResourceLocation> { override fun getBackground(context: GuiImmediateContext): NinePatch<MyResourceLocation> {
if (editing) return activeBg if (sm.editing) return activeBg
return super.getBackground(context) return super.getBackground(context)
} }
fun onKeyPressed(ch: Int, modifiers: Int): Boolean {
if (!editing) {
return false
}
if (ch == GLFW.GLFW_KEY_ESCAPE) {
lastPressedNonModifier = 0
editing = false
lastPressed = 0
opt.value = SavedKeyBinding(GLFW.GLFW_KEY_UNKNOWN)
updateLabel()
blur()
return true
}
if (ch == GLFW.GLFW_KEY_LEFT_SHIFT || ch == GLFW.GLFW_KEY_RIGHT_SHIFT
|| ch == GLFW.GLFW_KEY_LEFT_ALT || ch == GLFW.GLFW_KEY_RIGHT_ALT
|| ch == GLFW.GLFW_KEY_LEFT_CONTROL || ch == GLFW.GLFW_KEY_RIGHT_CONTROL
) {
lastPressed = ch
} else {
opt.value = SavedKeyBinding(
ch, modifiers
)
editing = false
blur()
lastPressed = 0
lastPressedNonModifier = 0
}
updateLabel()
return true
}
override fun onLostFocus() { override fun onLostFocus() {
lastPressedNonModifier = 0 sm.onLostFocus()
editing = false
lastPressed = 0
updateLabel()
}
fun onKeyReleased(ch: Int, modifiers: Int): Boolean {
if (!editing)
return false
if (lastPressedNonModifier == ch || (lastPressedNonModifier == 0 && ch == lastPressed)) {
opt.value = SavedKeyBinding(ch, modifiers)
editing = false
blur()
lastPressed = 0
lastPressedNonModifier = 0
}
updateLabel()
return true
} }
} }
updateLabel() sm.updateLabel()
guiAppender.appendLabeledRow(opt.labelText, button) guiAppender.appendLabeledRow(opt.labelText, button)
} }

View File

@@ -0,0 +1,108 @@
package moe.nea.firmament.gui.config
import org.lwjgl.glfw.GLFW
import net.minecraft.text.Text
import net.minecraft.util.Formatting
import moe.nea.firmament.keybindings.SavedKeyBinding
class KeyBindingStateManager(
val value: () -> SavedKeyBinding,
val setValue: (key: SavedKeyBinding) -> Unit,
val blur: () -> Unit,
val requestFocus: () -> Unit,
) {
var editing = false
var lastPressed = 0
var lastPressedNonModifier = 0
var label: Text = Text.literal("")
fun onClick() {
if (editing) {
editing = false
blur()
} else {
editing = true
requestFocus()
}
updateLabel()
}
fun keyboardEvent(keyCode: Int, pressed: Boolean): Boolean {
return if (pressed) onKeyPressed(keyCode, SavedKeyBinding.getModInt())
else onKeyReleased(keyCode, SavedKeyBinding.getModInt())
}
fun onKeyPressed(ch: Int, modifiers: Int): Boolean {
if (!editing) {
return false
}
if (ch == GLFW.GLFW_KEY_ESCAPE) {
lastPressedNonModifier = 0
editing = false
lastPressed = 0
setValue(SavedKeyBinding(GLFW.GLFW_KEY_UNKNOWN))
updateLabel()
blur()
return true
}
if (ch == GLFW.GLFW_KEY_LEFT_SHIFT || ch == GLFW.GLFW_KEY_RIGHT_SHIFT
|| ch == GLFW.GLFW_KEY_LEFT_ALT || ch == GLFW.GLFW_KEY_RIGHT_ALT
|| ch == GLFW.GLFW_KEY_LEFT_CONTROL || ch == GLFW.GLFW_KEY_RIGHT_CONTROL
) {
lastPressed = ch
} else {
setValue(SavedKeyBinding(
ch, modifiers
))
editing = false
blur()
lastPressed = 0
lastPressedNonModifier = 0
}
updateLabel()
return true
}
fun onLostFocus() {
lastPressedNonModifier = 0
editing = false
lastPressed = 0
updateLabel()
}
fun onKeyReleased(ch: Int, modifiers: Int): Boolean {
if (!editing)
return false
if (lastPressedNonModifier == ch || (lastPressedNonModifier == 0 && ch == lastPressed)) {
setValue(SavedKeyBinding(ch, modifiers))
editing = false
blur()
lastPressed = 0
lastPressedNonModifier = 0
}
updateLabel()
return true
}
fun updateLabel() {
var stroke = value().format()
if (editing) {
stroke = Text.literal("")
val (shift, ctrl, alt) = SavedKeyBinding.getMods(SavedKeyBinding.getModInt())
if (shift) {
stroke.append("SHIFT + ")
}
if (alt) {
stroke.append("ALT + ")
}
if (ctrl) {
stroke.append("CTRL + ")
}
stroke.append("???")
stroke.styled { it.withColor(Formatting.YELLOW) }
}
label = stroke
}
}

View File

@@ -32,7 +32,7 @@ data class SavedKeyBinding(
return Triple( return Triple(
modifiers and GLFW.GLFW_MOD_SHIFT != 0, modifiers and GLFW.GLFW_MOD_SHIFT != 0,
modifiers and GLFW.GLFW_MOD_CONTROL != 0, modifiers and GLFW.GLFW_MOD_CONTROL != 0,
modifiers and GLFW.GLFW_MOD_ALT != 0 modifiers and GLFW.GLFW_MOD_ALT != 0,
) )
} }

View File

@@ -31,6 +31,8 @@ object RepoManager {
save() save()
} }
val enableYacl by toggle("enable-yacl") { false }
val disableItemGroups by toggle("disable-item-groups") { true } val disableItemGroups by toggle("disable-item-groups") { true }
val reload by button("reload") { val reload by button("reload") {
save() save()

View File

@@ -73,6 +73,7 @@
"firmament.config.repo.username.hint": "NotEnoughUpdates", "firmament.config.repo.username.hint": "NotEnoughUpdates",
"firmament.config.repo.reponame": "Repo Name", "firmament.config.repo.reponame": "Repo Name",
"firmament.config.repo.reponame.hint": "NotEnoughUpdates-REPO", "firmament.config.repo.reponame.hint": "NotEnoughUpdates-REPO",
"firmament.config.repo.enable-yacl": "Use YACL Config",
"firmament.config.repo.branch": "Repo Branch", "firmament.config.repo.branch": "Repo Branch",
"firmament.config.repo.branch.hint": "dangerous", "firmament.config.repo.branch.hint": "dangerous",
"firmament.config.repo.reset": "Reset", "firmament.config.repo.reset": "Reset",