diff options
author | Linnea Gräf <nea@nea.moe> | 2024-10-13 19:53:10 +0200 |
---|---|---|
committer | Linnea Gräf <nea@nea.moe> | 2024-10-13 19:53:10 +0200 |
commit | 87b851373071490bb371ba1160f85f0341579cb2 (patch) | |
tree | 45d267d83baad2e3cb6caad6127f6a4d40e7fcbf | |
parent | e6142bb93619dee768fc18b87ffdd28558d4bcab (diff) | |
download | firmament-87b851373071490bb371ba1160f85f0341579cb2.tar.gz firmament-87b851373071490bb371ba1160f85f0341579cb2.tar.bz2 firmament-87b851373071490bb371ba1160f85f0341579cb2.zip |
Add YACL config menu
-rw-r--r-- | build.gradle.kts | 5 | ||||
-rw-r--r-- | gradle/libs.versions.toml | 26 | ||||
-rw-r--r-- | src/compat/yacl/java/KeybindingBuilder.kt | 16 | ||||
-rw-r--r-- | src/compat/yacl/java/KeybindingController.kt | 71 | ||||
-rw-r--r-- | src/compat/yacl/java/YaclIntegration.kt | 119 | ||||
-rw-r--r-- | src/main/kotlin/gui/config/AllConfigsGui.kt | 71 | ||||
-rw-r--r-- | src/main/kotlin/gui/config/FirmamentConfigScreenProvider.kt | 37 | ||||
-rw-r--r-- | src/main/kotlin/gui/config/HudMetaHandler.kt | 47 | ||||
-rw-r--r-- | src/main/kotlin/gui/config/KeyBindingHandler.kt | 168 | ||||
-rw-r--r-- | src/main/kotlin/gui/config/KeyBindingStateManager.kt | 108 | ||||
-rw-r--r-- | src/main/kotlin/keybindings/SavedKeyBinding.kt | 2 | ||||
-rw-r--r-- | src/main/kotlin/repo/RepoManager.kt | 246 | ||||
-rw-r--r-- | src/main/resources/assets/firmament/lang/en_us.json | 1 |
13 files changed, 589 insertions, 328 deletions
diff --git a/build.gradle.kts b/build.gradle.kts index bb15a1f..20b077c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -85,6 +85,9 @@ allprojects { maven("https://maven.azureaaron.net/snapshots") maven("https://maven.azureaaron.net/releases") maven("https://www.cursemaven.com") + maven("https://maven.isxander.dev/releases") { + name = "Xander Maven" + } mavenLocal() } } @@ -154,6 +157,7 @@ val SourceSet.modImplementationConfigurationName val configuredSourceSet = createIsolatedSourceSet("configured") val sodiumSourceSet = createIsolatedSourceSet("sodium") val citResewnSourceSet = createIsolatedSourceSet("citresewn") +val yaclSourceSet = createIsolatedSourceSet("yacl") val shadowMe by configurations.creating { exclude(group = "org.jetbrains.kotlin") @@ -223,6 +227,7 @@ dependencies { (citResewnSourceSet.modImplementationConfigurationName)( innerJarsOf("citresewn", dependencies.create(libs.citresewn.get())).asFileTree) (citResewnSourceSet.modImplementationConfigurationName)(libs.citresewn) + (yaclSourceSet.modImplementationConfigurationName)(libs.yacl) // Actual dependencies modCompileOnly(libs.rei.api) { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2f3c7cc..efb7c94 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -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/ manninghamMills = "2.4.1" +# Update from https://docs.isxander.dev/yet-another-config-lib/installing-yacl +yacl = "3.5.0+1.21-fabric" + [libraries] minecraft = { module = "com.mojang:minecraft", version.ref = "minecraft" } fabric_loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric_loader" } @@ -109,22 +112,23 @@ sodium = { module = "maven.modrinth:sodium", version.ref = "sodium" } freecammod = { module = "maven.modrinth:freecam", version.ref = "freecammod" } citresewn = { module = "maven.modrinth:cit-resewn", version.ref = "citresewn" } femalegender = { module = "maven.modrinth:female-gender", version.ref = "femalegender" } +yacl = { module = "dev.isxander:yet-another-config-lib", version.ref = "yacl" } [bundles] runtime_required = [ - "architectury_fabric", - "rei_fabric", - "notenoughanimations", - "femalegender", - "hypixelmodapi_fabric", + "architectury_fabric", + "rei_fabric", + "notenoughanimations", + "femalegender", + "hypixelmodapi_fabric", ] runtime_optional = [ - "devauth", - "freecammod", - "sodium", -# "qolify", - "ncr", - "citresewn", + "devauth", + "freecammod", + "sodium", + # "qolify", + "ncr", + "citresewn", ] [plugins] diff --git a/src/compat/yacl/java/KeybindingBuilder.kt b/src/compat/yacl/java/KeybindingBuilder.kt new file mode 100644 index 0000000..322ddff --- /dev/null +++ b/src/compat/yacl/java/KeybindingBuilder.kt @@ -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) + } +} diff --git a/src/compat/yacl/java/KeybindingController.kt b/src/compat/yacl/java/KeybindingController.kt new file mode 100644 index 0000000..6be9dd1 --- /dev/null +++ b/src/compat/yacl/java/KeybindingController.kt @@ -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 + } +} diff --git a/src/compat/yacl/java/YaclIntegration.kt b/src/compat/yacl/java/YaclIntegration.kt new file mode 100644 index 0000000..6c9354a --- /dev/null +++ b/src/compat/yacl/java/YaclIntegration.kt @@ -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) + } + +} diff --git a/src/main/kotlin/gui/config/AllConfigsGui.kt b/src/main/kotlin/gui/config/AllConfigsGui.kt index 3091b2a..c72b8b0 100644 --- a/src/main/kotlin/gui/config/AllConfigsGui.kt +++ b/src/main/kotlin/gui/config/AllConfigsGui.kt @@ -12,39 +12,40 @@ import moe.nea.firmament.util.ScreenUtil.setScreenLater object AllConfigsGui { - val allConfigs - get() = listOf( - RepoManager.Config - ) + FeatureManager.allFeatures.mapNotNull { it.config } - - fun <T> List<T>.toObservableList(): ObservableList<T> = ObservableList(this) - - class MainMapping(val allConfigs: List<ManagedConfig>) { - @get:Bind("configs") - val configs = allConfigs.map { EntryMapping(it) }.toObservableList() - - class EntryMapping(val config: ManagedConfig) { - @Bind - fun name() = Text.translatable("firmament.config.${config.name}").string - - @Bind - fun openEditor() { - config.showConfigEditor(MC.screen) - } - } - } - - fun makeBuiltInScreen(parent: Screen? = null): Screen { - return MoulConfigUtils.loadScreen("config/main", MainMapping(allConfigs), parent) - } - - fun makeScreen(parent: Screen? = null): Screen { - val provider = FirmamentConfigScreenProvider.providers.find { it.key == "builtin" } - ?: FirmamentConfigScreenProvider.providers.first() - return provider.open(parent) - } - - fun showAllGuis() { - setScreenLater(makeScreen()) - } + val allConfigs + get() = listOf( + RepoManager.Config + ) + FeatureManager.allFeatures.mapNotNull { it.config } + + fun <T> List<T>.toObservableList(): ObservableList<T> = ObservableList(this) + + class MainMapping(val allConfigs: List<ManagedConfig>) { + @get:Bind("configs") + val configs = allConfigs.map { EntryMapping(it) }.toObservableList() + + class EntryMapping(val config: ManagedConfig) { + @Bind + fun name() = Text.translatable("firmament.config.${config.name}").string + + @Bind + fun openEditor() { + config.showConfigEditor(MC.screen) + } + } + } + + fun makeBuiltInScreen(parent: Screen? = null): Screen { + return MoulConfigUtils.loadScreen("config/main", MainMapping(allConfigs), parent) + } + + fun makeScreen(parent: Screen? = null): Screen { + val wantedKey = if (RepoManager.Config.enableYacl) "yacl" else "builtin" + val provider = FirmamentConfigScreenProvider.providers.find { it.key == wantedKey } + ?: FirmamentConfigScreenProvider.providers.first() + return provider.open(parent) + } + + fun showAllGuis() { + setScreenLater(makeScreen()) + } } diff --git a/src/main/kotlin/gui/config/FirmamentConfigScreenProvider.kt b/src/main/kotlin/gui/config/FirmamentConfigScreenProvider.kt index 582f701..82e151e 100644 --- a/src/main/kotlin/gui/config/FirmamentConfigScreenProvider.kt +++ b/src/main/kotlin/gui/config/FirmamentConfigScreenProvider.kt @@ -6,22 +6,29 @@ import net.minecraft.client.gui.screen.Screen import moe.nea.firmament.Firmament interface FirmamentConfigScreenProvider { - val key: String - val isEnabled: Boolean get() = true + val key: String + val isEnabled: Boolean get() = true - fun open(parent: Screen?): Screen + fun open(parent: Screen?): Screen - companion object { - private val loader = ServiceLoader.load(FirmamentConfigScreenProvider::class.java) + companion object { + private val loader = ServiceLoader.load(FirmamentConfigScreenProvider::class.java) - val providers by lazy { - loader.stream().asSequence().mapNotNull { service -> - kotlin.runCatching { service.get() } - .getOrElse { - Firmament.logger.warn("Could not load config provider ${service.type()}", it) - null - } - }.filter { it.isEnabled }.toList() - } - } + val providers by lazy { + loader.stream().asSequence().mapNotNull { service -> + kotlin.runCatching { service.get() } + .getOrElse { + Firmament.logger.warn("Could not load config provider ${service.type()}", it) + null + } + }.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() + } + } } diff --git a/src/main/kotlin/gui/config/HudMetaHandler.kt b/src/main/kotlin/gui/config/HudMetaHandler.kt index 35c9d51..a9659ee 100644 --- a/src/main/kotlin/gui/config/HudMetaHandler.kt +++ b/src/main/kotlin/gui/config/HudMetaHandler.kt @@ -1,5 +1,3 @@ - - package moe.nea.firmament.gui.config 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.decodeFromJsonElement import kotlinx.serialization.json.encodeToJsonElement +import net.minecraft.client.gui.screen.Screen import net.minecraft.text.MutableText import net.minecraft.text.Text import moe.nea.firmament.gui.FirmButtonComponent @@ -14,26 +13,30 @@ import moe.nea.firmament.jarvis.JarvisIntegration import moe.nea.firmament.util.MC class HudMetaHandler(val config: ManagedConfig, val label: MutableText, val width: Int, val height: Int) : - ManagedConfig.OptionHandler<HudMeta> { - override fun toJson(element: HudMeta): JsonElement? { - return Json.encodeToJsonElement(element.position) - } + ManagedConfig.OptionHandler<HudMeta> { + override fun toJson(element: HudMeta): JsonElement? { + return Json.encodeToJsonElement(element.position) + } + + override fun fromJson(element: JsonElement): HudMeta { + return HudMeta(Json.decodeFromJsonElement(element), label, width, height) + } - override fun fromJson(element: JsonElement): HudMeta { - 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) { - guiAppender.appendLabeledRow( - opt.labelText, - FirmButtonComponent( - TextComponent( - Text.stringifiedTranslatable("firmament.hud.edit", label).string), - ) { - MC.screen = JarvisIntegration.jarvis.getHudEditor( - guiAppender.screenAccessor.invoke(), - listOf(opt.value) - ) - }) - } + override fun emitGuiElements(opt: ManagedOption<HudMeta>, guiAppender: GuiAppender) { + guiAppender.appendLabeledRow( + opt.labelText, + FirmButtonComponent( + TextComponent( + Text.stringifiedTranslatable("firmament.hud.edit", label).string), + ) { + openEditor(opt, guiAppender.screenAccessor()) + }) + } } diff --git a/src/main/kotlin/gui/config/KeyBindingHandler.kt b/src/main/kotlin/gui/config/KeyBindingHandler.kt index c389cc9..7ec7e81 100644 --- a/src/main/kotlin/gui/config/KeyBindingHandler.kt +++ b/src/main/kotlin/gui/config/KeyBindingHandler.kt @@ -1,5 +1,3 @@ - - package moe.nea.firmament.gui.config import io.github.notenoughupdates.moulconfig.common.IMinecraft @@ -13,137 +11,63 @@ import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.decodeFromJsonElement 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.keybindings.FirmamentKeyBindings import moe.nea.firmament.keybindings.SavedKeyBinding class KeyBindingHandler(val name: String, val managedConfig: ManagedConfig) : - ManagedConfig.OptionHandler<SavedKeyBinding> { - - override fun initOption(opt: ManagedOption<SavedKeyBinding>) { - FirmamentKeyBindings.registerKeyBinding(name, opt) - } + ManagedConfig.OptionHandler<SavedKeyBinding> { - override fun toJson(element: SavedKeyBinding): JsonElement? { - return Json.encodeToJsonElement(element) - } + override fun initOption(opt: ManagedOption<SavedKeyBinding>) { + FirmamentKeyBindings.registerKeyBinding(name, opt) + } - override fun fromJson(element: JsonElement): SavedKeyBinding { - return Json.decodeFromJsonElement(element) - } + override fun toJson(element: SavedKeyBinding): JsonElement? { + return Json.encodeToJsonElement(element) + } - override fun emitGuiElements(opt: ManagedOption<SavedKeyBinding>, guiAppender: GuiAppender) { - var editing = false - var lastPressed = 0 - var lastPressedNonModifier = 0 - var label: String = "" - var button: FirmButtonComponent? = null - fun updateLabel() { - 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( - TextComponent( - IMinecraft.instance.defaultFontRenderer, - { label }, - 130, - TextComponent.TextAlignment.LEFT, - false, - false - ), action = { - if (editing) { - button!!.blur() - } else { - editing = true - button!!.requestFocus() - updateLabel() - } - }) { - override fun keyboardEvent(event: KeyboardEvent, context: GuiImmediateContext): Boolean { - if (event is KeyboardEvent.KeyPressed) { - return if (event.pressed) onKeyPressed(event.keycode, SavedKeyBinding.getModInt()) - else onKeyReleased(event.keycode, SavedKeyBinding.getModInt()) - } - return super.keyboardEvent(event, context) - } + override fun fromJson(element: JsonElement): SavedKeyBinding { + return Json.decodeFromJsonElement(element) + } - override fun getBackground(context: GuiImmediateContext): NinePatch<MyResourceLocation> { - if (editing) return activeBg - return super.getBackground(context) - } + override fun emitGuiElements(opt: ManagedOption<SavedKeyBinding>, guiAppender: GuiAppender) { + lateinit var button: FirmButtonComponent + val sm = KeyBindingStateManager( + { opt.value }, + { opt.value = it }, + { button.blur() }, + { button.requestFocus() } + ) + button = object : FirmButtonComponent( + TextComponent( + IMinecraft.instance.defaultFontRenderer, + { sm.label.string }, + 130, + TextComponent.TextAlignment.LEFT, + false, + false + ), action = { + sm.onClick() + }) { + override fun keyboardEvent(event: KeyboardEvent, context: GuiImmediateContext): Boolean { + if (event is KeyboardEvent.KeyPressed) { + return sm.keyboardEvent(event.keycode, event.pressed) + } + return super.keyboardEvent(event, 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 getBackground(context: GuiImmediateContext): NinePatch<MyResourceLocation> { + if (sm.editing) return activeBg + return super.getBackground(context) + } - override 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)) { - opt.value = SavedKeyBinding(ch, modifiers) - editing = false - blur() - lastPressed = 0 - lastPressedNonModifier = 0 - } - updateLabel() - return true - } - } - updateLabel() - guiAppender.appendLabeledRow(opt.labelText, button) - } + override fun onLostFocus() { + sm.onLostFocus() + } + } + sm.updateLabel() + guiAppender.appendLabeledRow(opt.labelText, button) + } } diff --git a/src/main/kotlin/gui/config/KeyBindingStateManager.kt b/src/main/kotlin/gui/config/KeyBindingStateManager.kt new file mode 100644 index 0000000..cc8178d --- /dev/null +++ b/src/main/kotlin/gui/config/KeyBindingStateManager.kt @@ -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 + } + + +} diff --git a/src/main/kotlin/keybindings/SavedKeyBinding.kt b/src/main/kotlin/keybindings/SavedKeyBinding.kt index 8607fd0..5bca87e 100644 --- a/src/main/kotlin/keybindings/SavedKeyBinding.kt +++ b/src/main/kotlin/keybindings/SavedKeyBinding.kt @@ -32,7 +32,7 @@ data class SavedKeyBinding( return Triple( modifiers and GLFW.GLFW_MOD_SHIFT != 0, modifiers and GLFW.GLFW_MOD_CONTROL != 0, - modifiers and GLFW.GLFW_MOD_ALT != 0 + modifiers and GLFW.GLFW_MOD_ALT != 0, ) } diff --git a/src/main/kotlin/repo/RepoManager.kt b/src/main/kotlin/repo/RepoManager.kt index f0da397..4c25daa 100644 --- a/src/main/kotlin/repo/RepoManager.kt +++ b/src/main/kotlin/repo/RepoManager.kt @@ -19,127 +19,129 @@ import moe.nea.firmament.util.MinecraftDispatcher import moe.nea.firmament.util.SkyblockId object RepoManager { - object Config : ManagedConfig("repo") { - var username by string("username") { "NotEnoughUpdates" } - var reponame by string("reponame") { "NotEnoughUpdates-REPO" } - var branch by string("branch") { "master" } - val autoUpdate by toggle("autoUpdate") { true } - val reset by button("reset") { - username = "NotEnoughUpdates" - reponame = "NotEnoughUpdates-REPO" - branch = "master" - save() - } - - val disableItemGroups by toggle("disable-item-groups") { true } - val reload by button("reload") { - save() - RepoManager.reload() - } - val redownload by button("redownload") { - save() - RepoManager.launchAsyncUpdate(true) - } - } - - val currentDownloadedSha by RepoDownloadManager::latestSavedVersionHash - - var recentlyFailedToUpdateItemList = false - - val neuRepo: NEURepository = NEURepository.of(RepoDownloadManager.repoSavedLocation).apply { - registerReloadListener(ItemCache) - registerReloadListener(ExpLadders) - registerReloadListener(ItemNameLookup) - ReloadRegistrationEvent.publish(ReloadRegistrationEvent(this)) - registerReloadListener { - Firmament.coroutineScope.launch(MinecraftDispatcher) { - if (!trySendClientboundUpdateRecipesPacket()) { - logger.warn("Failed to issue a ClientboundUpdateRecipesPacket (to reload REI). This may lead to an outdated item list.") - recentlyFailedToUpdateItemList = true - } - } - } - } - - val essenceRecipeProvider = EssenceRecipeProvider() - val recipeCache = BetterRepoRecipeCache(essenceRecipeProvider) - - init { - neuRepo.registerReloadListener(essenceRecipeProvider) - neuRepo.registerReloadListener(recipeCache) - } - - fun getAllRecipes() = neuRepo.items.items.values.asSequence().flatMap { it.recipes } - - fun getRecipesFor(skyblockId: SkyblockId): Set<NEURecipe> = recipeCache.recipes[skyblockId] ?: setOf() - fun getUsagesFor(skyblockId: SkyblockId): Set<NEURecipe> = recipeCache.usages[skyblockId] ?: setOf() - - private fun trySendClientboundUpdateRecipesPacket(): Boolean { - return MinecraftClient.getInstance().world != null && MinecraftClient.getInstance().networkHandler?.onSynchronizeRecipes( - SynchronizeRecipesS2CPacket(mutableListOf()) - ) != null - } - - init { - ClientTickEvents.START_WORLD_TICK.register(ClientTickEvents.StartWorldTick { - if (recentlyFailedToUpdateItemList && trySendClientboundUpdateRecipesPacket()) - recentlyFailedToUpdateItemList = false - }) - } - - fun getNEUItem(skyblockId: SkyblockId): NEUItem? = neuRepo.items.getItemBySkyblockId(skyblockId.neuItem) - - fun launchAsyncUpdate(force: Boolean = false) { - Firmament.coroutineScope.launch { - ItemCache.ReloadProgressHud.reportProgress("Downloading", 0, -1) // TODO: replace with a proper boundy bar - ItemCache.ReloadProgressHud.isEnabled = true - try { - RepoDownloadManager.downloadUpdate(force) - ItemCache.ReloadProgressHud.reportProgress("Download complete", 1, 1) - } finally { - ItemCache.ReloadProgressHud.isEnabled = false - } - reload() - } - } - - fun reload() { - try { - ItemCache.ReloadProgressHud.reportProgress("Reloading from Disk", - 0, - -1) // TODO: replace with a proper boundy bar - ItemCache.ReloadProgressHud.isEnabled = true - neuRepo.reload() - } catch (exc: NEURepositoryException) { - MinecraftClient.getInstance().player?.sendMessage( - Text.literal("Failed to reload repository. This will result in some mod features not working.") - ) - ItemCache.ReloadProgressHud.isEnabled = false - exc.printStackTrace() - } - } - - fun initialize() { - if (Config.autoUpdate) { - launchAsyncUpdate() - } else { - reload() - } - } - - fun getPotentialStubPetData(skyblockId: SkyblockId): PetData? { - val parts = skyblockId.neuItem.split(";") - if (parts.size != 2) { - return null - } - val (petId, rarityIndex) = parts - if (!rarityIndex.all { it.isDigit() }) { - return null - } - val intIndex = rarityIndex.toInt() - if (intIndex !in Rarity.values().indices) return null - if (petId !in neuRepo.constants.petNumbers) return null - return PetData(Rarity.values()[intIndex], petId, 0.0, true) - } + object Config : ManagedConfig("repo") { + var username by string("username") { "NotEnoughUpdates" } + var reponame by string("reponame") { "NotEnoughUpdates-REPO" } + var branch by string("branch") { "master" } + val autoUpdate by toggle("autoUpdate") { true } + val reset by button("reset") { + username = "NotEnoughUpdates" + reponame = "NotEnoughUpdates-REPO" + branch = "master" + save() + } + + val enableYacl by toggle("enable-yacl") { false } + + val disableItemGroups by toggle("disable-item-groups") { true } + val reload by button("reload") { + save() + RepoManager.reload() + } + val redownload by button("redownload") { + save() + RepoManager.launchAsyncUpdate(true) + } + } + + val currentDownloadedSha by RepoDownloadManager::latestSavedVersionHash + + var recentlyFailedToUpdateItemList = false + + val neuRepo: NEURepository = NEURepository.of(RepoDownloadManager.repoSavedLocation).apply { + registerReloadListener(ItemCache) + registerReloadListener(ExpLadders) + registerReloadListener(ItemNameLookup) + ReloadRegistrationEvent.publish(ReloadRegistrationEvent(this)) + registerReloadListener { + Firmament.coroutineScope.launch(MinecraftDispatcher) { + if (!trySendClientboundUpdateRecipesPacket()) { + logger.warn("Failed to issue a ClientboundUpdateRecipesPacket (to reload REI). This may lead to an outdated item list.") + recentlyFailedToUpdateItemList = true + } + } + } + } + + val essenceRecipeProvider = EssenceRecipeProvider() + val recipeCache = BetterRepoRecipeCache(essenceRecipeProvider) + + init { + neuRepo.registerReloadListener(essenceRecipeProvider) + neuRepo.registerReloadListener(recipeCache) + } + + fun getAllRecipes() = neuRepo.items.items.values.asSequence().flatMap { it.recipes } + + fun getRecipesFor(skyblockId: SkyblockId): Set<NEURecipe> = recipeCache.recipes[skyblockId] ?: setOf() + fun getUsagesFor(skyblockId: SkyblockId): Set<NEURecipe> = recipeCache.usages[skyblockId] ?: setOf() + + private fun trySendClientboundUpdateRecipesPacket(): Boolean { + return MinecraftClient.getInstance().world != null && MinecraftClient.getInstance().networkHandler?.onSynchronizeRecipes( + SynchronizeRecipesS2CPacket(mutableListOf()) + ) != null + } + + init { + ClientTickEvents.START_WORLD_TICK.register(ClientTickEvents.StartWorldTick { + if (recentlyFailedToUpdateItemList && trySendClientboundUpdateRecipesPacket()) + recentlyFailedToUpdateItemList = false + }) + } + + fun getNEUItem(skyblockId: SkyblockId): NEUItem? = neuRepo.items.getItemBySkyblockId(skyblockId.neuItem) + + fun launchAsyncUpdate(force: Boolean = false) { + Firmament.coroutineScope.launch { + ItemCache.ReloadProgressHud.reportProgress("Downloading", 0, -1) // TODO: replace with a proper boundy bar + ItemCache.ReloadProgressHud.isEnabled = true + try { + RepoDownloadManager.downloadUpdate(force) + ItemCache.ReloadProgressHud.reportProgress("Download complete", 1, 1) + } finally { + ItemCache.ReloadProgressHud.isEnabled = false + } + reload() + } + } + + fun reload() { + try { + ItemCache.ReloadProgressHud.reportProgress("Reloading from Disk", + 0, + -1) // TODO: replace with a proper boundy bar + ItemCache.ReloadProgressHud.isEnabled = true + neuRepo.reload() + } catch (exc: NEURepositoryException) { + MinecraftClient.getInstance().player?.sendMessage( + Text.literal("Failed to reload repository. This will result in some mod features not working.") + ) + ItemCache.ReloadProgressHud.isEnabled = false + exc.printStackTrace() + } + } + + fun initialize() { + if (Config.autoUpdate) { + launchAsyncUpdate() + } else { + reload() + } + } + + fun getPotentialStubPetData(skyblockId: SkyblockId): PetData? { + val parts = skyblockId.neuItem.split(";") + if (parts.size != 2) { + return null + } + val (petId, rarityIndex) = parts + if (!rarityIndex.all { it.isDigit() }) { + return null + } + val intIndex = rarityIndex.toInt() + if (intIndex !in Rarity.values().indices) return null + if (petId !in neuRepo.constants.petNumbers) return null + return PetData(Rarity.values()[intIndex], petId, 0.0, true) + } } diff --git a/src/main/resources/assets/firmament/lang/en_us.json b/src/main/resources/assets/firmament/lang/en_us.json index 131eae2..1512b9d 100644 --- a/src/main/resources/assets/firmament/lang/en_us.json +++ b/src/main/resources/assets/firmament/lang/en_us.json @@ -73,6 +73,7 @@ "firmament.config.repo.username.hint": "NotEnoughUpdates", "firmament.config.repo.reponame": "Repo Name", "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.hint": "dangerous", "firmament.config.repo.reset": "Reset", |