Key bindings

Let players pick a key with the Keybind widget, store it as text with Key.name, and compare it with the game's input on old and new versions.

Last updated

Steps

The Keybind widget lets the player pick a key. The key is a Key, which is the same object on every Minecraft version, so your config and menu stay version-independent.

Edit a key in the menu#

Keybind.builder("zoomKey")
        .label(Text.translatable("mymod.zoomKey", "Zoom"))
        .value(() -> config.zoomKey)          // null or Key.UNBOUND show as unbound
        .onChange(key -> config.zoomKey = key)
        .build();

The player clicks the button, then presses a key or mouse button. Escape unbinds (your onChange receives Key.UNBOUND). Call .allowMouse(false) to accept keyboard keys only.

Make keys#

Call Gives
Key.keyboard(glfwCode) a keyboard key by its GLFW key code, e.g. Key.keyboard(82) for R, Key.keyboard(340) for Left Shift
Key.mouse(button) a mouse button: 0 left, 1 right, 2 middle, 3 and up the side buttons
Key.UNBOUND no key
Key.ESCAPE the Escape key

Key is immutable; compare keys with equals. isUnbound(), type() (KEYBOARD or MOUSE) and code() describe a key, and englishName() gives "R", "Left Shift" or "Not Bound" for logs and tests (the widget itself shows vanilla's translated name).

Store a key#

name() is the text 1.13+ writes to options.txt, and Key.fromName reads it back:

String stored = config.zoomKey.name();     // "key.keyboard.z"
Key loaded = Key.fromName(stored);         // null if the text is no key name

config.zoomKey = loaded != null ? loaded : Key.keyboard(90);   // default: Z

The same text works on every version, including 1.7.10 – 1.12.2, so one config file format fits all.

Reset to a default#

Keep the built Keybind and call set, which passes the key to your onChange:

Keybind zoom = Keybind.builder("zoomKey")
        .label(Text.translatable("mymod.zoomKey", "Zoom"))
        .value(() -> config.zoomKey)
        .onChange(key -> config.zoomKey = key)
        .build();

Button reset = Button.builder("zoomKey.reset")
        .label(Text.translatable("mymod.zoomKey.reset", "Reset"))
        .onPress(() -> zoom.set(Key.keyboard(90)))
        .build();

Check the key in game#

Listening for input is version-specific, so it belongs in your game layer (Build one mod for every loader). Compare the key there with what that version's input gives you:

Minecraft Input codes Compare with
1.13 – 26.3 GLFW key codes; mouse buttons by number key.code() and key.type()
1.7.10 – 1.12.2 LWJGL 2 key codes; mouse button b as -100 + b key.toLwjgl2()

Key.fromLwjgl2(code) converts the other way. A few keys exist on only one side: F20 – F25 and the World 1/2 keys can't be bound on 1.7.10 – 1.12.2, and LWJGL 2 keys with no GLFW equivalent become Key.UNBOUND.

Test it#

FakeHost.pressKey(id, key) clicks the button and presses the key:

host.pressKey("zoomKey", Key.keyboard(82));
assertTrue(host.render().contains("[keybind zoomKey] Zoom: R"));
host.pressKey("zoomKey", Key.ESCAPE);
assertEquals(Key.UNBOUND, config.zoomKey);

Next steps#