diff options
10 files changed, 262 insertions, 26 deletions
@@ -50,6 +50,7 @@ Priority 3: - bits? - collection? - pretty graphs! +- Maintain scroll percentage in vanilla guis when reinitializing - and much more that i will add as i go along diff --git a/src/main/java/moe/nea/firmament/mixins/MixinKeybindsScreen.java b/src/main/java/moe/nea/firmament/mixins/MixinKeybindsScreen.java new file mode 100644 index 0000000..23bc26c --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/MixinKeybindsScreen.java @@ -0,0 +1,53 @@ +package moe.nea.firmament.mixins; + +import moe.nea.firmament.gui.config.ManagedConfig; +import moe.nea.firmament.keybindings.FirmamentKeyBindings; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.option.ControlsListWidget; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.option.KeyBinding; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyArg; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ControlsListWidget.KeyBindingEntry.class) +public class MixinKeybindsScreen { + + @Mutable + @Shadow + @Final + private ButtonWidget editButton; + + @Shadow + @Final + private KeyBinding binding; + + @Shadow + @Final + private ButtonWidget resetButton; + + @ModifyArg(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/widget/ButtonWidget;builder(Lnet/minecraft/text/Text;Lnet/minecraft/client/gui/widget/ButtonWidget$PressAction;)Lnet/minecraft/client/gui/widget/ButtonWidget$Builder;")) + public ButtonWidget.PressAction onInit(ButtonWidget.PressAction action) { + ManagedConfig config = FirmamentKeyBindings.INSTANCE.getKeyBindings().get(binding); + if (config == null) return action; + return button -> { + config.showConfigEditor(MinecraftClient.getInstance().currentScreen); + }; + } + + @Inject(method = "update", at = @At("HEAD"), cancellable = true) + public void onUpdate(CallbackInfo ci) { + ManagedConfig config = FirmamentKeyBindings.INSTANCE.getKeyBindings().get(binding); + if (config == null) return; + resetButton.active = false; + editButton.setMessage(Text.translatable("firmament.keybinding.external")); + ci.cancel(); + } + +} diff --git a/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt b/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt index 0c8acb9..facb821 100644 --- a/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt +++ b/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt @@ -12,7 +12,6 @@ import moe.nea.firmament.Firmament import moe.nea.firmament.features.chat.ChatLinks import moe.nea.firmament.features.debug.DebugView import moe.nea.firmament.features.debug.DeveloperFeatures -import moe.nea.firmament.features.fishing.FishingWarning import moe.nea.firmament.features.fixes.Fixes import moe.nea.firmament.features.inventory.CraftingOverlay import moe.nea.firmament.features.inventory.SaveCursorPosition @@ -54,6 +53,7 @@ object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "feature loadFeature(DeveloperFeatures) loadFeature(DebugView) } + allFeatures.forEach { it.config } hasAutoloaded = true } } diff --git a/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt b/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt index f6a197c..ec86341 100644 --- a/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt +++ b/src/main/kotlin/moe/nea/firmament/features/inventory/SlotLocking.kt @@ -8,17 +8,17 @@ package moe.nea.firmament.features.inventory import kotlinx.serialization.Serializable import kotlinx.serialization.serializer -import net.minecraft.client.gui.DrawContext -import net.minecraft.entity.player.PlayerInventory import moe.nea.firmament.events.HandledScreenKeyPressedEvent import moe.nea.firmament.events.IsSlotProtectedEvent import moe.nea.firmament.events.SlotRenderEvents import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.keybindings.FirmamentKeyBindings +import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.mixins.accessor.AccessorHandledScreen import moe.nea.firmament.util.CommonSoundEffects import moe.nea.firmament.util.MC import moe.nea.firmament.util.data.ProfileSpecificDataHolder +import net.minecraft.entity.player.PlayerInventory +import org.lwjgl.glfw.GLFW object SlotLocking : FirmamentFeature { override val identifier: String @@ -29,13 +29,19 @@ object SlotLocking : FirmamentFeature { val lockedSlots: MutableSet<Int> = mutableSetOf(), ) + object TConfig : ManagedConfig(identifier) { + val lock by keyBinding("lock") { GLFW.GLFW_KEY_L } + } + + override val config: TConfig + get() = TConfig + object DConfig : ProfileSpecificDataHolder<Data>(serializer(), "locked-slots", ::Data) - val keyBinding by FirmamentKeyBindings::SLOT_LOCKING val lockedSlots get() = DConfig.data?.lockedSlots override fun onLoad() { HandledScreenKeyPressedEvent.subscribe { - if (!it.matches(keyBinding)) return@subscribe + if (!it.matches(TConfig.lock)) return@subscribe val inventory = MC.handledScreen ?: return@subscribe inventory as AccessorHandledScreen diff --git a/src/main/kotlin/moe/nea/firmament/gui/config/KeyBindingHandler.kt b/src/main/kotlin/moe/nea/firmament/gui/config/KeyBindingHandler.kt new file mode 100644 index 0000000..5a4e3af --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/gui/config/KeyBindingHandler.kt @@ -0,0 +1,113 @@ +package moe.nea.firmament.gui.config + +import io.github.cottonmc.cotton.gui.widget.WButton +import io.github.cottonmc.cotton.gui.widget.data.InputResult +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.decodeFromJsonElement +import kotlinx.serialization.json.encodeToJsonElement +import moe.nea.firmament.keybindings.FirmamentKeyBindings +import moe.nea.firmament.keybindings.SavedKeyBinding +import net.minecraft.client.util.InputUtil +import net.minecraft.text.Text +import net.minecraft.util.Formatting +import org.lwjgl.glfw.GLFW + +class KeyBindingHandler(name: String, managedConfig: ManagedConfig) : ManagedConfig.OptionHandler<SavedKeyBinding> { + init { + FirmamentKeyBindings.registerKeyBinding(name, managedConfig) + } + + override fun toJson(element: SavedKeyBinding): JsonElement? { + return Json.encodeToJsonElement(element) + } + + override fun fromJson(element: JsonElement): SavedKeyBinding { + return Json.decodeFromJsonElement(element) + } + + override fun emitGuiElements(opt: ManagedOption<SavedKeyBinding>, guiAppender: GuiAppender) { + var editing = false + var lastPressed = 0 + var lastPressedNonModifier = 0 + var updateButton: (() -> Unit)? = null + val button = object : WButton() { + override fun onKeyPressed(ch: Int, key: Int, modifiers: Int): InputResult { + if (!editing) { + return super.onKeyPressed(ch, key, modifiers) + } + if (ch == GLFW.GLFW_KEY_ESCAPE) { + lastPressedNonModifier = 0 + editing = false + lastPressed = 0 + updateButton!!() + return InputResult.PROCESSED + } + 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 + lastPressed = 0 + lastPressedNonModifier = 0 + } + updateButton!!() + return InputResult.PROCESSED + } + + override fun onFocusLost() { + super.onFocusLost() + lastPressedNonModifier = 0 + editing = false + lastPressed = 0 + updateButton!!() + } + + override fun onKeyReleased(ch: Int, key: Int, modifiers: Int): InputResult { + if (!editing) + return super.onKeyReleased(ch, key, modifiers) + if (lastPressedNonModifier == ch || (lastPressedNonModifier == 0 && ch == lastPressed)) { + opt.value = SavedKeyBinding( + ch, modifiers + ) + editing = false + lastPressed = 0 + lastPressedNonModifier = 0 + } + updateButton!!() + return InputResult.PROCESSED + } + } + + fun updateLabel() { + val stroke = Text.literal("") + if (opt.value.shift) { + stroke.append("SHIFT + ") // TODO: translations? + } + if (opt.value.alt) { + stroke.append("ALT + ") + } + if (opt.value.ctrl) { + stroke.append("CTRL + ") + } + stroke.append(InputUtil.Type.KEYSYM.createFromCode(opt.value.keyCode).localizedText) + if (editing) + stroke.styled { it.withColor(Formatting.YELLOW) } + button.setLabel(stroke) + } + updateButton = ::updateLabel + updateButton() + button.setOnClick { + editing = true + button.requestFocus() + updateButton() + } + guiAppender.appendLabeledRow(opt.labelText, button) + } + +} diff --git a/src/main/kotlin/moe/nea/firmament/gui/config/ManagedConfig.kt b/src/main/kotlin/moe/nea/firmament/gui/config/ManagedConfig.kt index 56aca46..8743293 100644 --- a/src/main/kotlin/moe/nea/firmament/gui/config/ManagedConfig.kt +++ b/src/main/kotlin/moe/nea/firmament/gui/config/ManagedConfig.kt @@ -14,21 +14,22 @@ import io.github.cottonmc.cotton.gui.widget.WLabel import io.github.cottonmc.cotton.gui.widget.data.Axis import io.github.cottonmc.cotton.gui.widget.data.Insets import io.github.cottonmc.cotton.gui.widget.data.VerticalAlignment -import moe.nea.jarvis.api.Point 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.time.Duration -import net.minecraft.client.gui.screen.Screen -import net.minecraft.text.Text import moe.nea.firmament.Firmament import moe.nea.firmament.gui.WTightScrollPanel +import moe.nea.firmament.keybindings.SavedKeyBinding import moe.nea.firmament.util.MC import moe.nea.firmament.util.ScreenUtil.setScreenLater +import moe.nea.jarvis.api.Point +import net.minecraft.client.gui.screen.Screen +import net.minecraft.text.Text +import kotlin.io.path.createDirectories +import kotlin.io.path.readText +import kotlin.io.path.writeText +import kotlin.time.Duration abstract class ManagedConfig(override val name: String) : ManagedConfigElement() { @@ -106,6 +107,18 @@ abstract class ManagedConfig(override val name: String) : ManagedConfigElement() }, HudMetaHandler(this, label, width, height)) } + protected fun keyBinding( + propertyName: String, + default: () -> Int, + ): ManagedOption<SavedKeyBinding> = keyBindingWithDefaultModifiers(propertyName) { SavedKeyBinding(default()) } + + protected fun keyBindingWithDefaultModifiers( + propertyName: String, + default: () -> SavedKeyBinding, + ): ManagedOption<SavedKeyBinding> { + return option(propertyName, default, KeyBindingHandler("firmament.config.${name}.${propertyName}", this)) + } + protected fun integer( propertyName: String, min: Int, @@ -125,7 +138,7 @@ abstract class ManagedConfig(override val name: String) : ManagedConfigElement() fun reloadGui() { - latestGuiAppender?.reloadables?.forEach {it() } + latestGuiAppender?.reloadables?.forEach { it() } } fun getConfigEditor(parent: Screen? = null): CottonClientScreen { @@ -137,7 +150,11 @@ abstract class ManagedConfig(override val name: String) : ManagedConfigElement() guiapp.appendFullRow(WBox(Axis.HORIZONTAL).also { it.add(WButton(Text.literal("←")).also { it.setOnClick { - AllConfigsGui.showAllGuis() + if (parent != null) { + setScreenLater(parent) + } else { + AllConfigsGui.showAllGuis() + } } }) it.add(WLabel(Text.translatable("firmament.config.${name}")).also { diff --git a/src/main/kotlin/moe/nea/firmament/keybindings/FirmamentKeyBindings.kt b/src/main/kotlin/moe/nea/firmament/keybindings/FirmamentKeyBindings.kt index a7f25c0..770f4f6 100644 --- a/src/main/kotlin/moe/nea/firmament/keybindings/FirmamentKeyBindings.kt +++ b/src/main/kotlin/moe/nea/firmament/keybindings/FirmamentKeyBindings.kt @@ -7,17 +7,24 @@ package moe.nea.firmament.keybindings import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper -import org.lwjgl.glfw.GLFW import net.minecraft.client.option.KeyBinding import net.minecraft.client.util.InputUtil +import moe.nea.firmament.features.inventory.SlotLocking +import moe.nea.firmament.gui.config.ManagedConfig object FirmamentKeyBindings { - val SLOT_LOCKING = KeyBindingHelper.registerKeyBinding( - KeyBinding( - "firmament.key.slotlocking", - InputUtil.Type.KEYSYM, - GLFW.GLFW_KEY_L, - "firmament.key.category" + fun registerKeyBinding(name: String, config: ManagedConfig) { + val vanillaKeyBinding = KeyBindingHelper.registerKeyBinding( + KeyBinding( + name, + InputUtil.Type.KEYSYM, + -1, + "firmament.key.category" + ) ) - ) + keyBindings[vanillaKeyBinding] = config + } + + val keyBindings = mutableMapOf<KeyBinding, ManagedConfig>() + } diff --git a/src/main/kotlin/moe/nea/firmament/keybindings/SavedKeyBinding.kt b/src/main/kotlin/moe/nea/firmament/keybindings/SavedKeyBinding.kt new file mode 100644 index 0000000..f73eb49 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/keybindings/SavedKeyBinding.kt @@ -0,0 +1,36 @@ +package moe.nea.firmament.keybindings + +import kotlinx.serialization.Serializable +import org.lwjgl.glfw.GLFW + +@Serializable +data class SavedKeyBinding( + val keyCode: Int, + val shift: Boolean = false, + val ctrl: Boolean = false, + val alt: Boolean = false, +) : IKeyBinding { + constructor(keyCode: Int, mods: Triple<Boolean, Boolean, Boolean>) : this( + keyCode, + mods.first && keyCode != GLFW.GLFW_KEY_LEFT_SHIFT && keyCode != GLFW.GLFW_KEY_RIGHT_SHIFT, + mods.second && keyCode != GLFW.GLFW_KEY_LEFT_CONTROL && keyCode != GLFW.GLFW_KEY_RIGHT_CONTROL, + mods.third && keyCode != GLFW.GLFW_KEY_LEFT_ALT && keyCode != GLFW.GLFW_KEY_RIGHT_ALT, + ) + + constructor(keyCode: Int, mods: Int) : this(keyCode, getMods(mods)) + + companion object { + fun getMods(modifiers: Int): Triple<Boolean, Boolean, Boolean> { + return Triple( + modifiers and GLFW.GLFW_MOD_SHIFT != 0, + modifiers and GLFW.GLFW_MOD_CONTROL != 0, + modifiers and GLFW.GLFW_MOD_ALT != 0 + ) + } + } + + override fun matches(keyCode: Int, scanCode: Int, modifiers: Int): Boolean { + return keyCode == this.keyCode && getMods(modifiers) == Triple(shift, ctrl, alt) + } + +} diff --git a/src/main/kotlin/moe/nea/firmament/util/ScreenUtil.kt b/src/main/kotlin/moe/nea/firmament/util/ScreenUtil.kt index 39d4541..25c603a 100644 --- a/src/main/kotlin/moe/nea/firmament/util/ScreenUtil.kt +++ b/src/main/kotlin/moe/nea/firmament/util/ScreenUtil.kt @@ -6,10 +6,10 @@ package moe.nea.firmament.util -import moe.nea.firmament.Firmament import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents import net.minecraft.client.MinecraftClient import net.minecraft.client.gui.screen.Screen +import moe.nea.firmament.Firmament object ScreenUtil { init { @@ -29,10 +29,10 @@ object ScreenUtil { private var nextOpenedGui: Screen? = null - fun setScreenLater(nextScreen: Screen) { + fun setScreenLater(nextScreen: Screen?) { val nog = nextOpenedGui if (nog != null) { - Firmament.logger.warn("Setting screen ${nextScreen::class.qualifiedName} to be opened later, but ${nog::class.qualifiedName} is already queued.") + Firmament.logger.warn("Setting screen ${if (nextScreen == null) "null" else nextScreen::class.qualifiedName} to be opened later, but ${nog::class.qualifiedName} is already queued.") return } nextOpenedGui = nextScreen diff --git a/src/main/resources/assets/firmament/lang/en_us.json b/src/main/resources/assets/firmament/lang/en_us.json index 5df94a7..ad799e2 100644 --- a/src/main/resources/assets/firmament/lang/en_us.json +++ b/src/main/resources/assets/firmament/lang/en_us.json @@ -79,6 +79,9 @@ "firmament.config.chat-links.allowed-hosts": "Allowed Image Hosts", "firmament.config.chat-links.position": "Chat Image Preview", "firmament.hud.edit": "Edit %s", + "firmament.keybinding.external": "External", + "firmament.config.slot-locking": "Slot Locking", + "firmament.config.slot-locking.lock": "Lock Slot", "firmament.config.custom-skyblock-textures": "Custom SkyBlock Item Textures", "firmament.config.custom-skyblock-textures.cache-duration": "Model Cache Duration", "firmament.config.custom-skyblock-textures.enabled": "Enable Custom Item Textures", |