From f651c230dce24868576942aa7cf969ca2e96468e Mon Sep 17 00:00:00 2001 From: noramibuu <50046813+noramibu@users.noreply.github.com> Date: Sun, 11 Jan 2026 04:21:43 +0300 Subject: [PATCH 01/22] add keyboard hud element --- .../meteorclient/systems/hud/Hud.java | 1 + .../systems/hud/elements/KeyboardHud.java | 620 ++++++++++++++++++ 2 files changed, 621 insertions(+) create mode 100644 src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/Hud.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/Hud.java index b5fa527675..0217f9eb98 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/hud/Hud.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/Hud.java @@ -130,6 +130,7 @@ public void init() { register(PotionTimersHud.INFO); register(CombatHud.INFO); register(MapHud.INFO); + register(KeyboardHud.INFO); // Default config if (isFirstInit) resetToDefaultElements(); diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java new file mode 100644 index 0000000000..c0fb376895 --- /dev/null +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java @@ -0,0 +1,620 @@ +/* + * This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client). + * Copyright (c) Meteor Development. + */ + +package meteordevelopment.meteorclient.systems.hud.elements; + +import meteordevelopment.meteorclient.gui.GuiTheme; +import meteordevelopment.meteorclient.gui.widgets.WWidget; +import meteordevelopment.meteorclient.settings.*; +import meteordevelopment.meteorclient.systems.hud.Hud; +import meteordevelopment.meteorclient.systems.hud.HudElement; +import meteordevelopment.meteorclient.systems.hud.HudElementInfo; +import meteordevelopment.meteorclient.systems.hud.HudRenderer; +import meteordevelopment.meteorclient.utils.player.ChatUtils; +import meteordevelopment.meteorclient.utils.misc.Keybind; +import meteordevelopment.meteorclient.mixin.KeyBindingAccessor; +import meteordevelopment.meteorclient.utils.render.color.Color; +import meteordevelopment.meteorclient.utils.render.color.SettingColor; +import net.minecraft.client.option.KeyBinding; +import org.lwjgl.glfw.GLFW; + +import java.util.ArrayList; +import java.util.List; + +import static meteordevelopment.meteorclient.MeteorClient.mc; + +public class KeyboardHud extends HudElement { + public static final HudElementInfo INFO = new HudElementInfo<>(Hud.GROUP, "keyboard", + "Displays pressed keys.", KeyboardHud::new); + + public enum Alignment { + Left, + Center, + Right + } + + private final SettingGroup sgGeneral = settings.getDefaultGroup(); + private final SettingGroup sgColor = settings.createGroup("Color"); + private final SettingGroup sgBackground = settings.createGroup("Background"); + + private final Setting preset = sgGeneral.add(new EnumSetting.Builder() + .name("preset") + .description("Which keys to display.") + .defaultValue(Preset.Movement) + .onChanged(this::onPresetChanged) + .build() + ); + + private final Setting customKeys = sgGeneral.add(new StringSetting.Builder() + .name("custom-keys") + .description("Custom keys configuration (KeyName X Y Width Height [flags]...). Flags: cps") + .defaultValue("LMB 0 0 40 40 cps, RMB 44 0 40 40 cps") + .visible(() -> preset.get() == Preset.AdvancedCustomization) + .onChanged(this::onCustomKeysChanged) + .build() + ); + + private final Setting customKey = sgGeneral.add(new KeybindSetting.Builder() + .name("custom-key") + .description("The key to display.") + .defaultValue(Keybind.fromKey(GLFW.GLFW_KEY_SPACE)) + .visible(() -> preset.get() == Preset.CustomKey) + .onChanged(k -> onPresetChanged(preset.get())) + .build() + ); + + private final Setting customKeyWidth = sgGeneral.add(new DoubleSetting.Builder() + .name("key-width") + .description("Width of the key.") + .defaultValue(60) + .min(20) + .sliderRange(20, 200) + .visible(() -> preset.get() == Preset.CustomKey) + .onChanged(v -> onPresetChanged(preset.get())) + .build() + ); + + private final Setting customKeyHeight = sgGeneral.add(new DoubleSetting.Builder() + .name("key-height") + .description("Height of the key.") + .defaultValue(40) + .min(20) + .sliderRange(20, 200) + .visible(() -> preset.get() == Preset.CustomKey) + .onChanged(v -> onPresetChanged(preset.get())) + .build() + ); + + private final Setting customKeyShowCps = sgGeneral.add(new BoolSetting.Builder() + .name("show-cps") + .description("Show CPS for this key.") + .defaultValue(false) + .visible(() -> preset.get() == Preset.CustomKey) + .onChanged(v -> onPresetChanged(preset.get())) + .build() + ); + + private final Setting scale = sgGeneral.add(new DoubleSetting.Builder() + .name("scale") + .description("Scale of the keyboard.") + .defaultValue(1.5) + .min(0.5) + .sliderRange(0.5, 5) + .onChanged(s -> calculateSize()) + .build() + ); + + private final Setting spacing = sgGeneral.add(new DoubleSetting.Builder() + .name("spacing") + .description("Spacing between keys.") + .defaultValue(1) + .min(0) + .sliderRange(0, 10) + .visible(() -> preset.get() != Preset.CustomKey && preset.get() != Preset.AdvancedCustomization) + .onChanged(s -> onPresetChanged(preset.get())) + .build() + ); + + private final Setting showCps = sgGeneral.add(new BoolSetting.Builder() + .name("Show CPS") + .description("Shows CPS on mouse buttons in Clicks preset.") + .defaultValue(true) + .visible(() -> preset.get() == Preset.Clicks) + .onChanged(b -> onPresetChanged(preset.get())) + .build() + ); + + private final Setting alignment = sgGeneral.add(new EnumSetting.Builder() + .name("alignment") + .description("Horizontal alignment of the text.") + .defaultValue(Alignment.Center) + .build() + ); + + private final Setting pressedColor = sgColor.add(new ColorSetting.Builder() + .name("pressed-color") + .description("Color of pressed keys.") + .defaultValue(new SettingColor(200, 200, 200, 100)) + .build() + ); + + private final Setting unpressedColor = sgColor.add(new ColorSetting.Builder() + .name("unpressed-color") + .description("Color of unpressed keys.") + .defaultValue(new SettingColor(0, 0, 0, 100)) + .build() + ); + + private final Setting textColor = sgColor.add(new ColorSetting.Builder() + .name("text-color") + .description("Color of the key name.") + .defaultValue(new SettingColor(255, 255, 255)) + .build() + ); + + private final Setting opacity = sgColor.add(new DoubleSetting.Builder() + .name("opacity") + .description("Opacity of the whole element.") + .defaultValue(1) + .min(0) + .max(1) + .sliderMax(1) + .build() + ); + + private final Setting background = sgBackground.add(new BoolSetting.Builder() + .name("background") + .description("Displays background.") + .defaultValue(false) + .build() + ); + + private final Setting backgroundColor = sgBackground.add(new ColorSetting.Builder() + .name("background-color") + .description("Color used for the background.") + .visible(background::get) + .defaultValue(new SettingColor(25, 25, 25, 50)) + .build() + ); + + private final List keys = new ArrayList<>(); + + private Color getColor(SettingColor color) { + Color c = new Color(color); + c.a = (int) (c.a * opacity.get()); + return c; + } + + public KeyboardHud() { + super(INFO); + if (mc.options != null) + onPresetChanged(preset.get()); + } + + @Override + public WWidget getWidget(GuiTheme theme) { + return null; + } + + @Override + public void tick(HudRenderer renderer) { + for (Key key : keys) { + key.tick(); + } + } + + private void onPresetChanged(Preset preset) { + if (mc.options == null) + return; + + keys.clear(); + double w = 40; + double h = 40; + double s = spacing.get() * 2; + + switch (preset) { + case Movement -> { + keys.add(new Key(mc.options.forwardKey, w + s, 0, w, h)); + keys.add(new Key(mc.options.leftKey, 0, h + s, w, h)); + keys.add(new Key(mc.options.backKey, w + s, h + s, w, h)); + keys.add(new Key(mc.options.rightKey, (w + s) * 2, h + s, w, h)); + keys.add(new Key(mc.options.sneakKey, 0, (h + s) * 2, w, h)); + keys.add(new Key(mc.options.jumpKey, w + s, (h + s) * 2, (w * 2) + s, h)); + } + case Clicks -> { + keys.add(new Key(mc.options.attackKey, "LMB", 0, 0, w, h).setShowCps(showCps.get())); + keys.add(new Key(mc.options.useKey, "RMB", w + s, 0, w, h).setShowCps(showCps.get())); + } + case Actions -> { + keys.add(new Key(mc.options.dropKey, 0, 0, w, h)); + keys.add(new Key(mc.options.swapHandsKey, w + s, 0, w, h)); + keys.add(new Key(mc.options.inventoryKey, (w + s) * 2, 0, w, h)); + } + case Hotbar -> { + for (int i = 0; i < 9; i++) { + keys.add(new Key(mc.options.hotbarKeys[i], i * (w + s), 0, w, h)); + } + } + case CustomKey -> { + Key key = new Key(customKey.get(), 0, 0, customKeyWidth.get(), customKeyHeight.get()); + key.setShowCps(customKeyShowCps.get()); + keys.add(key); + } + case AdvancedCustomization -> { + onCustomKeysChanged(customKeys.get()); + ChatUtils.info("Keyboard HUD Custom Format: Key X Y Weight Height [cps]. Ex: 'LMB 0 0 40 40 cps'."); + } + } + calculateSize(); + } + + private void onCustomKeysChanged(String config) { + if (preset.get() != Preset.AdvancedCustomization) + return; + keys.clear(); + + String[] parts = config.split(","); + for (String part : parts) { + String[] args = part.trim().split(" "); + if (args.length >= 3) { + try { + String name = args[0]; + double x = Double.parseDouble(args[1]); + double y = Double.parseDouble(args[2]); + double w = args.length > 3 ? Double.parseDouble(args[3]) : 40; + double h = args.length > 4 ? Double.parseDouble(args[4]) : 40; + + int code = resolveKey(name); + Key key = new Key(name, code, x, y, w, h); + + if (args.length > 5) { + for (int i = 5; i < args.length; i++) { + if (args[i].equalsIgnoreCase("cps")) { + key.setShowCps(true); + } + } + } + + keys.add(key); + } catch (Exception ignored) { + } + } + } + calculateSize(); + } + + private int resolveKey(String name) { + if (name.length() == 1) { + int c = name.toUpperCase().charAt(0); + if (c >= 'A' && c <= 'Z') + return GLFW.GLFW_KEY_A + (c - 'A'); + if (c >= '0' && c <= '9') + return GLFW.GLFW_KEY_0 + (c - '0'); + return switch (name) { + case "[" -> GLFW.GLFW_KEY_LEFT_BRACKET; + case "]" -> GLFW.GLFW_KEY_RIGHT_BRACKET; + case ";" -> GLFW.GLFW_KEY_SEMICOLON; + case "'" -> GLFW.GLFW_KEY_APOSTROPHE; + case "\\" -> GLFW.GLFW_KEY_BACKSLASH; + case "`" -> GLFW.GLFW_KEY_GRAVE_ACCENT; + case "-" -> GLFW.GLFW_KEY_MINUS; + case "=" -> GLFW.GLFW_KEY_EQUAL; + case "." -> GLFW.GLFW_KEY_PERIOD; + case "/" -> GLFW.GLFW_KEY_SLASH; + default -> -1; + }; + } + return switch (name.toUpperCase()) { + case "SPACE" -> GLFW.GLFW_KEY_SPACE; + case "LSHIFT" -> GLFW.GLFW_KEY_LEFT_SHIFT; + case "RSHIFT" -> GLFW.GLFW_KEY_RIGHT_SHIFT; + case "CTRL", "LCTRL" -> GLFW.GLFW_KEY_LEFT_CONTROL; + case "RCTRL" -> GLFW.GLFW_KEY_RIGHT_CONTROL; + case "ALT", "LALT" -> GLFW.GLFW_KEY_LEFT_ALT; + case "RALT" -> GLFW.GLFW_KEY_RIGHT_ALT; + case "TAB" -> GLFW.GLFW_KEY_TAB; + case "ENTER" -> GLFW.GLFW_KEY_ENTER; + case "BACKSPACE" -> GLFW.GLFW_KEY_BACKSPACE; + case "ESC" -> GLFW.GLFW_KEY_ESCAPE; + case "CAPS" -> GLFW.GLFW_KEY_CAPS_LOCK; + case "WIN", "SUPER", "LWIN", "LSUPER" -> GLFW.GLFW_KEY_LEFT_SUPER; + case "RWIN", "RSUPER" -> GLFW.GLFW_KEY_RIGHT_SUPER; + case "PRINT", "PRTSC" -> GLFW.GLFW_KEY_PRINT_SCREEN; + case "SCROLL" -> GLFW.GLFW_KEY_SCROLL_LOCK; + case "PAUSE" -> GLFW.GLFW_KEY_PAUSE; + case "NUMLOCK" -> GLFW.GLFW_KEY_NUM_LOCK; + case "COMMA" -> GLFW.GLFW_KEY_COMMA; + case "PERIOD", "DOT" -> GLFW.GLFW_KEY_PERIOD; + case "SLASH" -> GLFW.GLFW_KEY_SLASH; + case "F1" -> GLFW.GLFW_KEY_F1; + case "F2" -> GLFW.GLFW_KEY_F2; + case "F3" -> GLFW.GLFW_KEY_F3; + case "F4" -> GLFW.GLFW_KEY_F4; + case "F5" -> GLFW.GLFW_KEY_F5; + case "F6" -> GLFW.GLFW_KEY_F6; + case "F7" -> GLFW.GLFW_KEY_F7; + case "F8" -> GLFW.GLFW_KEY_F8; + case "F9" -> GLFW.GLFW_KEY_F9; + case "F10" -> GLFW.GLFW_KEY_F10; + case "F11" -> GLFW.GLFW_KEY_F11; + case "F12" -> GLFW.GLFW_KEY_F12; + case "UP" -> GLFW.GLFW_KEY_UP; + case "DOWN" -> GLFW.GLFW_KEY_DOWN; + case "LEFT" -> GLFW.GLFW_KEY_LEFT; + case "RIGHT" -> GLFW.GLFW_KEY_RIGHT; + case "HOME" -> GLFW.GLFW_KEY_HOME; + case "END" -> GLFW.GLFW_KEY_END; + case "PAGEUP", "PGUP" -> GLFW.GLFW_KEY_PAGE_UP; + case "PAGEDOWN", "PGDN" -> GLFW.GLFW_KEY_PAGE_DOWN; + case "INSERT", "INS" -> GLFW.GLFW_KEY_INSERT; + case "DELETE", "DEL" -> GLFW.GLFW_KEY_DELETE; + case "NUM0" -> GLFW.GLFW_KEY_KP_0; + case "NUM1" -> GLFW.GLFW_KEY_KP_1; + case "NUM2" -> GLFW.GLFW_KEY_KP_2; + case "NUM3" -> GLFW.GLFW_KEY_KP_3; + case "NUM4" -> GLFW.GLFW_KEY_KP_4; + case "NUM5" -> GLFW.GLFW_KEY_KP_5; + case "NUM6" -> GLFW.GLFW_KEY_KP_6; + case "NUM7" -> GLFW.GLFW_KEY_KP_7; + case "NUM8" -> GLFW.GLFW_KEY_KP_8; + case "NUM9" -> GLFW.GLFW_KEY_KP_9; + case "NUMADD" -> GLFW.GLFW_KEY_KP_ADD; + case "NUMSUB" -> GLFW.GLFW_KEY_KP_SUBTRACT; + case "NUMMUL" -> GLFW.GLFW_KEY_KP_MULTIPLY; + case "NUMDIV" -> GLFW.GLFW_KEY_KP_DIVIDE; + case "NUMDOT" -> GLFW.GLFW_KEY_KP_DECIMAL; + case "NUMENTER" -> GLFW.GLFW_KEY_KP_ENTER; + case "LMB", "MB1" -> GLFW.GLFW_MOUSE_BUTTON_LEFT; + case "RMB", "MB2" -> GLFW.GLFW_MOUSE_BUTTON_RIGHT; + case "MMB", "MB3" -> GLFW.GLFW_MOUSE_BUTTON_MIDDLE; + case "MB4" -> GLFW.GLFW_MOUSE_BUTTON_4; + case "MB5" -> GLFW.GLFW_MOUSE_BUTTON_5; + case "MB6" -> GLFW.GLFW_MOUSE_BUTTON_6; + case "MB7" -> GLFW.GLFW_MOUSE_BUTTON_7; + case "MB8" -> GLFW.GLFW_MOUSE_BUTTON_8; + default -> -1; + }; + } + + private void calculateSize() { + if (keys.isEmpty()) { + setSize(0, 0); + return; + } + + double maxX = 0; + double maxY = 0; + + for (Key key : keys) { + maxX = Math.max(maxX, key.x + key.width); + maxY = Math.max(maxY, key.y + key.height); + } + + double s = scale.get(); + setSize(maxX * s, maxY * s); + } + + private String getShortName(String name) { + if (name == null) + return "?"; + return switch (name.toUpperCase()) { + case "LEFT SHIFT", "LSHIFT" -> "LSh"; + case "RIGHT SHIFT", "RSHIFT" -> "RSh"; + case "LEFT CONTROL", "LCTRL" -> "LCtrl"; + case "RIGHT CONTROL", "RCTRL" -> "RCtrl"; + case "LEFT ALT", "LALT" -> "LAlt"; + case "RIGHT ALT", "RALT" -> "RAlt"; + case "COMMA" -> ","; + case "DOT", "PERIOD" -> "."; + case "SLASH" -> "/"; + case "BACKSPACE" -> "BS"; + case "ENTER" -> "Ent"; + case "SCROLL" -> "ScrL"; + case "PRINT", "PRTSC" -> "PrtS"; + case "PAUSE" -> "Paus"; + case "PAGEUP", "PGUP" -> "PgUp"; + case "PAGEDOWN", "PGDN" -> "PgDn"; + case "INSERT", "INS" -> "Ins"; + case "DELETE", "DEL" -> "Del"; + case "HOME" -> "Home"; + case "END" -> "End"; + case "UP" -> "Up"; + case "DOWN" -> "Dn"; + case "LEFT" -> "Lt"; + case "RIGHT" -> "Rt"; + default -> name; + }; + } + + @Override + public void render(HudRenderer renderer) { + if (keys.isEmpty()) { + if (mc.options != null && preset.get() != Preset.AdvancedCustomization) { + onPresetChanged(preset.get()); + } + return; + } + + if (background.get()) { + renderer.quad(x, y, getWidth(), getHeight(), getColor(backgroundColor.get())); + } + + double s = scale.get(); + + for (Key key : keys) { + boolean pressed = key.isPressed(); + Color color = pressed ? getColor(pressedColor.get()) : getColor(unpressedColor.get()); + + double kX = x + key.x * s; + double kY = y + key.y * s; + double kW = key.width * s; + double kH = key.height * s; + + renderer.quad(kX, kY, kW, kH, color); + + String text = getShortName(key.getName()); + String cpsText = key.showCps ? key.getCps() + " CPS" : ""; + Color txtColor = getColor(textColor.get()); + + double padding = 2 * s; + double availableWidth = kW - padding * 2; + double availableHeight = kH - padding * 2; + double tH = renderer.textHeight(); + + if (cpsText.isEmpty()) { + double tW = renderer.textWidth(text); + double widthScale = tW > availableWidth ? availableWidth / tW : 1.0; + double heightScale = tH > availableHeight * 0.6 ? (availableHeight * 0.6) / tH : 1.0; + double textScale = Math.min(widthScale, heightScale); + double yText = kY + (kH - tH * textScale) / 2; + drawTextLine(renderer, text, kX, yText, kW, textScale, txtColor); + } else { + double topW = renderer.textWidth(text); + double topWidthScale = topW > availableWidth ? availableWidth / topW : 1.0; + double topHeightScale = tH > availableHeight * 0.4 ? (availableHeight * 0.4) / tH : 1.0; + double topScale = Math.min(topWidthScale, topHeightScale); + + double botW = renderer.textWidth(cpsText); + double botWidthScale = botW > availableWidth ? availableWidth / botW : 1.0; + double botHeightScale = tH > availableHeight * 0.4 ? (availableHeight * 0.4) / tH : 1.0; + double botScale = Math.min(botWidthScale, botHeightScale); + + double totalHeight = (tH * topScale) + (tH * botScale); + double startY = kY + (kH - totalHeight) / 2; + + drawTextLine(renderer, text, kX, startY, kW, topScale, txtColor); + drawTextLine(renderer, cpsText, kX, startY + tH * topScale, kW, botScale, txtColor); + } + } + } + + private void drawTextLine(HudRenderer renderer, String text, double x, double y, double w, double textScale, + Color color) { + double tW = renderer.textWidth(text); + double s = scale.get(); + double padding = 2 * s; + + double xText = x + (w - tW * textScale) / 2; + if (alignment.get() == Alignment.Left) + xText = x + padding; + else if (alignment.get() == Alignment.Right) + xText = x + w - padding - tW * textScale; + + renderer.text(text, xText, y, color, false, textScale); + } + + public enum Preset { + Movement, + Clicks, + Actions, + Hotbar, + CustomKey, + AdvancedCustomization + } + + private static class Key { + public String name; + public KeyBinding binding; + public Keybind keybind; + public int code; + public double x, y, width, height; + public boolean showCps = false; + + private final RollingCps rollingCps = new RollingCps(); + private boolean wasPressed; + + public Key(KeyBinding binding, double x, double y, double width, double height) { + this(binding, null, x, y, width, height); + } + + public Key(KeyBinding binding, String name, double x, double y, double width, double height) { + this.binding = binding; + this.name = name; + this.code = -1; + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + public Key(String name, int code, double x, double y, double width, double height) { + this.name = name; + this.code = code; + this.binding = null; + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + public Key(Keybind keybind, double x, double y, double width, double height) { + this.keybind = keybind; + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + public Key setShowCps(boolean show) { + this.showCps = show; + return this; + } + + public String getName() { + if (keybind != null) return keybind.toString(); + if (name != null) return name; + if (binding != null) return binding.getBoundKeyLocalizedText().getString().toUpperCase(); + return "?"; + } + + public boolean isPressed() { + long window = mc.getWindow().getHandle(); + if (keybind != null) { + int key = keybind.getValue(); + boolean isMouse = !keybind.isKey(); + if (isMouse) return GLFW.glfwGetMouseButton(window, key) == GLFW.GLFW_PRESS; + return GLFW.glfwGetKey(window, key) == GLFW.GLFW_PRESS; + } + if (binding != null) { + int key = ((KeyBindingAccessor) binding).meteor$getKey().getCode(); + if (key >= 0 && key < 8) { + return GLFW.glfwGetMouseButton(window, key) == GLFW.GLFW_PRESS; + } + return GLFW.glfwGetKey(window, key) == GLFW.GLFW_PRESS; + } + if (code >= 0) { + if (code < 8) return GLFW.glfwGetMouseButton(window, code) == GLFW.GLFW_PRESS; + return GLFW.glfwGetKey(window, code) == GLFW.GLFW_PRESS; + } + return false; + } + + public void tick() { + boolean pressed = isPressed(); + if (pressed && !wasPressed && showCps) { + rollingCps.add(); + } + wasPressed = pressed; + } + + public int getCps() { + return rollingCps.get(); + } + } + + private static class RollingCps { + private final List clicks = new ArrayList<>(); + + public void add() { + clicks.add(System.currentTimeMillis()); + } + + public int get() { + long time = System.currentTimeMillis(); + clicks.removeIf(val -> val + 1000 < time); + return clicks.size(); + } + } +} From 5f6ed04abdf302151f27eb511080c8701f3f8d1a Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Sat, 10 Jan 2026 22:39:31 -0500 Subject: [PATCH 02/22] rewrite custom keys to use a setting type --- .../gui/DefaultSettingsWidgetFactory.java | 7 + .../meteorclient/gui/widgets/WKeybind.java | 1 + .../systems/hud/elements/KeyboardHud.java | 454 ++++++++++-------- 3 files changed, 259 insertions(+), 203 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/gui/DefaultSettingsWidgetFactory.java b/src/main/java/meteordevelopment/meteorclient/gui/DefaultSettingsWidgetFactory.java index 6a1b773118..53cf90ff64 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/DefaultSettingsWidgetFactory.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/DefaultSettingsWidgetFactory.java @@ -22,6 +22,7 @@ import meteordevelopment.meteorclient.gui.widgets.pressable.WPlus; import meteordevelopment.meteorclient.renderer.Fonts; import meteordevelopment.meteorclient.settings.*; +import meteordevelopment.meteorclient.systems.hud.elements.KeyboardHud; import meteordevelopment.meteorclient.utils.Utils; import meteordevelopment.meteorclient.utils.render.color.SettingColor; import net.minecraft.client.resource.language.I18n; @@ -71,6 +72,7 @@ public DefaultSettingsWidgetFactory(GuiTheme theme) { factories.put(ColorListSetting.class, (table, setting) -> colorListW(table, (ColorListSetting) setting)); factories.put(FontFaceSetting.class, (table, setting) -> fontW(table, (FontFaceSetting) setting)); factories.put(Vector3dSetting.class, (table, setting) -> vector3dW(table, (Vector3dSetting) setting)); + factories.put(KeyboardHud.CustomKeyListSetting.class, (table,setting) -> customKeyListW(table, (KeyboardHud.CustomKeyListSetting) setting)); } @Override @@ -466,6 +468,11 @@ private WDoubleEdit addVectorComponent(WTable table, String label, double value, return component; } + private void customKeyListW(WTable table, KeyboardHud.CustomKeyListSetting setting) { + WTable wtable = table.add(theme.table()).expandX().widget(); + KeyboardHud.fillTable(theme, wtable, setting); + } + // Other private void selectW(WContainer c, Setting setting, Runnable action) { diff --git a/src/main/java/meteordevelopment/meteorclient/gui/widgets/WKeybind.java b/src/main/java/meteordevelopment/meteorclient/gui/widgets/WKeybind.java index 16174de395..4b282ea382 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/widgets/WKeybind.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/widgets/WKeybind.java @@ -71,6 +71,7 @@ public void reset() { if (Modules.get().isBinding()) { Modules.get().setModuleToBind(null); } + if (action != null) action.run(); } private void refreshLabel() { diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java index c0fb376895..6afde6d661 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java @@ -5,22 +5,32 @@ package meteordevelopment.meteorclient.systems.hud.elements; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; import meteordevelopment.meteorclient.gui.GuiTheme; +import meteordevelopment.meteorclient.gui.WidgetScreen; +import meteordevelopment.meteorclient.gui.WindowScreen; +import meteordevelopment.meteorclient.gui.renderer.GuiRenderer; import meteordevelopment.meteorclient.gui.widgets.WWidget; +import meteordevelopment.meteorclient.gui.widgets.containers.WTable; +import meteordevelopment.meteorclient.gui.widgets.pressable.WButton; +import meteordevelopment.meteorclient.gui.widgets.pressable.WMinus; +import meteordevelopment.meteorclient.mixin.KeyBindingAccessor; import meteordevelopment.meteorclient.settings.*; import meteordevelopment.meteorclient.systems.hud.Hud; import meteordevelopment.meteorclient.systems.hud.HudElement; import meteordevelopment.meteorclient.systems.hud.HudElementInfo; import meteordevelopment.meteorclient.systems.hud.HudRenderer; -import meteordevelopment.meteorclient.utils.player.ChatUtils; import meteordevelopment.meteorclient.utils.misc.Keybind; -import meteordevelopment.meteorclient.mixin.KeyBindingAccessor; import meteordevelopment.meteorclient.utils.render.color.Color; import meteordevelopment.meteorclient.utils.render.color.SettingColor; import net.minecraft.client.option.KeyBinding; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtList; import org.lwjgl.glfw.GLFW; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import static meteordevelopment.meteorclient.MeteorClient.mc; @@ -47,54 +57,7 @@ public enum Alignment { .build() ); - private final Setting customKeys = sgGeneral.add(new StringSetting.Builder() - .name("custom-keys") - .description("Custom keys configuration (KeyName X Y Width Height [flags]...). Flags: cps") - .defaultValue("LMB 0 0 40 40 cps, RMB 44 0 40 40 cps") - .visible(() -> preset.get() == Preset.AdvancedCustomization) - .onChanged(this::onCustomKeysChanged) - .build() - ); - - private final Setting customKey = sgGeneral.add(new KeybindSetting.Builder() - .name("custom-key") - .description("The key to display.") - .defaultValue(Keybind.fromKey(GLFW.GLFW_KEY_SPACE)) - .visible(() -> preset.get() == Preset.CustomKey) - .onChanged(k -> onPresetChanged(preset.get())) - .build() - ); - - private final Setting customKeyWidth = sgGeneral.add(new DoubleSetting.Builder() - .name("key-width") - .description("Width of the key.") - .defaultValue(60) - .min(20) - .sliderRange(20, 200) - .visible(() -> preset.get() == Preset.CustomKey) - .onChanged(v -> onPresetChanged(preset.get())) - .build() - ); - - private final Setting customKeyHeight = sgGeneral.add(new DoubleSetting.Builder() - .name("key-height") - .description("Height of the key.") - .defaultValue(40) - .min(20) - .sliderRange(20, 200) - .visible(() -> preset.get() == Preset.CustomKey) - .onChanged(v -> onPresetChanged(preset.get())) - .build() - ); - - private final Setting customKeyShowCps = sgGeneral.add(new BoolSetting.Builder() - .name("show-cps") - .description("Show CPS for this key.") - .defaultValue(false) - .visible(() -> preset.get() == Preset.CustomKey) - .onChanged(v -> onPresetChanged(preset.get())) - .build() - ); + private final Setting> customKeys = sgGeneral.add(new CustomKeyListSetting()); private final Setting scale = sgGeneral.add(new DoubleSetting.Builder() .name("scale") @@ -112,7 +75,7 @@ public enum Alignment { .defaultValue(1) .min(0) .sliderRange(0, 10) - .visible(() -> preset.get() != Preset.CustomKey && preset.get() != Preset.AdvancedCustomization) + .visible(() -> preset.get() != Preset.Custom) .onChanged(s -> onPresetChanged(preset.get())) .build() ); @@ -237,147 +200,13 @@ private void onPresetChanged(Preset preset) { keys.add(new Key(mc.options.hotbarKeys[i], i * (w + s), 0, w, h)); } } - case CustomKey -> { - Key key = new Key(customKey.get(), 0, 0, customKeyWidth.get(), customKeyHeight.get()); - key.setShowCps(customKeyShowCps.get()); - keys.add(key); - } - case AdvancedCustomization -> { - onCustomKeysChanged(customKeys.get()); - ChatUtils.info("Keyboard HUD Custom Format: Key X Y Weight Height [cps]. Ex: 'LMB 0 0 40 40 cps'."); + case Custom -> { + keys.addAll(customKeys.get()); } } calculateSize(); } - private void onCustomKeysChanged(String config) { - if (preset.get() != Preset.AdvancedCustomization) - return; - keys.clear(); - - String[] parts = config.split(","); - for (String part : parts) { - String[] args = part.trim().split(" "); - if (args.length >= 3) { - try { - String name = args[0]; - double x = Double.parseDouble(args[1]); - double y = Double.parseDouble(args[2]); - double w = args.length > 3 ? Double.parseDouble(args[3]) : 40; - double h = args.length > 4 ? Double.parseDouble(args[4]) : 40; - - int code = resolveKey(name); - Key key = new Key(name, code, x, y, w, h); - - if (args.length > 5) { - for (int i = 5; i < args.length; i++) { - if (args[i].equalsIgnoreCase("cps")) { - key.setShowCps(true); - } - } - } - - keys.add(key); - } catch (Exception ignored) { - } - } - } - calculateSize(); - } - - private int resolveKey(String name) { - if (name.length() == 1) { - int c = name.toUpperCase().charAt(0); - if (c >= 'A' && c <= 'Z') - return GLFW.GLFW_KEY_A + (c - 'A'); - if (c >= '0' && c <= '9') - return GLFW.GLFW_KEY_0 + (c - '0'); - return switch (name) { - case "[" -> GLFW.GLFW_KEY_LEFT_BRACKET; - case "]" -> GLFW.GLFW_KEY_RIGHT_BRACKET; - case ";" -> GLFW.GLFW_KEY_SEMICOLON; - case "'" -> GLFW.GLFW_KEY_APOSTROPHE; - case "\\" -> GLFW.GLFW_KEY_BACKSLASH; - case "`" -> GLFW.GLFW_KEY_GRAVE_ACCENT; - case "-" -> GLFW.GLFW_KEY_MINUS; - case "=" -> GLFW.GLFW_KEY_EQUAL; - case "." -> GLFW.GLFW_KEY_PERIOD; - case "/" -> GLFW.GLFW_KEY_SLASH; - default -> -1; - }; - } - return switch (name.toUpperCase()) { - case "SPACE" -> GLFW.GLFW_KEY_SPACE; - case "LSHIFT" -> GLFW.GLFW_KEY_LEFT_SHIFT; - case "RSHIFT" -> GLFW.GLFW_KEY_RIGHT_SHIFT; - case "CTRL", "LCTRL" -> GLFW.GLFW_KEY_LEFT_CONTROL; - case "RCTRL" -> GLFW.GLFW_KEY_RIGHT_CONTROL; - case "ALT", "LALT" -> GLFW.GLFW_KEY_LEFT_ALT; - case "RALT" -> GLFW.GLFW_KEY_RIGHT_ALT; - case "TAB" -> GLFW.GLFW_KEY_TAB; - case "ENTER" -> GLFW.GLFW_KEY_ENTER; - case "BACKSPACE" -> GLFW.GLFW_KEY_BACKSPACE; - case "ESC" -> GLFW.GLFW_KEY_ESCAPE; - case "CAPS" -> GLFW.GLFW_KEY_CAPS_LOCK; - case "WIN", "SUPER", "LWIN", "LSUPER" -> GLFW.GLFW_KEY_LEFT_SUPER; - case "RWIN", "RSUPER" -> GLFW.GLFW_KEY_RIGHT_SUPER; - case "PRINT", "PRTSC" -> GLFW.GLFW_KEY_PRINT_SCREEN; - case "SCROLL" -> GLFW.GLFW_KEY_SCROLL_LOCK; - case "PAUSE" -> GLFW.GLFW_KEY_PAUSE; - case "NUMLOCK" -> GLFW.GLFW_KEY_NUM_LOCK; - case "COMMA" -> GLFW.GLFW_KEY_COMMA; - case "PERIOD", "DOT" -> GLFW.GLFW_KEY_PERIOD; - case "SLASH" -> GLFW.GLFW_KEY_SLASH; - case "F1" -> GLFW.GLFW_KEY_F1; - case "F2" -> GLFW.GLFW_KEY_F2; - case "F3" -> GLFW.GLFW_KEY_F3; - case "F4" -> GLFW.GLFW_KEY_F4; - case "F5" -> GLFW.GLFW_KEY_F5; - case "F6" -> GLFW.GLFW_KEY_F6; - case "F7" -> GLFW.GLFW_KEY_F7; - case "F8" -> GLFW.GLFW_KEY_F8; - case "F9" -> GLFW.GLFW_KEY_F9; - case "F10" -> GLFW.GLFW_KEY_F10; - case "F11" -> GLFW.GLFW_KEY_F11; - case "F12" -> GLFW.GLFW_KEY_F12; - case "UP" -> GLFW.GLFW_KEY_UP; - case "DOWN" -> GLFW.GLFW_KEY_DOWN; - case "LEFT" -> GLFW.GLFW_KEY_LEFT; - case "RIGHT" -> GLFW.GLFW_KEY_RIGHT; - case "HOME" -> GLFW.GLFW_KEY_HOME; - case "END" -> GLFW.GLFW_KEY_END; - case "PAGEUP", "PGUP" -> GLFW.GLFW_KEY_PAGE_UP; - case "PAGEDOWN", "PGDN" -> GLFW.GLFW_KEY_PAGE_DOWN; - case "INSERT", "INS" -> GLFW.GLFW_KEY_INSERT; - case "DELETE", "DEL" -> GLFW.GLFW_KEY_DELETE; - case "NUM0" -> GLFW.GLFW_KEY_KP_0; - case "NUM1" -> GLFW.GLFW_KEY_KP_1; - case "NUM2" -> GLFW.GLFW_KEY_KP_2; - case "NUM3" -> GLFW.GLFW_KEY_KP_3; - case "NUM4" -> GLFW.GLFW_KEY_KP_4; - case "NUM5" -> GLFW.GLFW_KEY_KP_5; - case "NUM6" -> GLFW.GLFW_KEY_KP_6; - case "NUM7" -> GLFW.GLFW_KEY_KP_7; - case "NUM8" -> GLFW.GLFW_KEY_KP_8; - case "NUM9" -> GLFW.GLFW_KEY_KP_9; - case "NUMADD" -> GLFW.GLFW_KEY_KP_ADD; - case "NUMSUB" -> GLFW.GLFW_KEY_KP_SUBTRACT; - case "NUMMUL" -> GLFW.GLFW_KEY_KP_MULTIPLY; - case "NUMDIV" -> GLFW.GLFW_KEY_KP_DIVIDE; - case "NUMDOT" -> GLFW.GLFW_KEY_KP_DECIMAL; - case "NUMENTER" -> GLFW.GLFW_KEY_KP_ENTER; - case "LMB", "MB1" -> GLFW.GLFW_MOUSE_BUTTON_LEFT; - case "RMB", "MB2" -> GLFW.GLFW_MOUSE_BUTTON_RIGHT; - case "MMB", "MB3" -> GLFW.GLFW_MOUSE_BUTTON_MIDDLE; - case "MB4" -> GLFW.GLFW_MOUSE_BUTTON_4; - case "MB5" -> GLFW.GLFW_MOUSE_BUTTON_5; - case "MB6" -> GLFW.GLFW_MOUSE_BUTTON_6; - case "MB7" -> GLFW.GLFW_MOUSE_BUTTON_7; - case "MB8" -> GLFW.GLFW_MOUSE_BUTTON_8; - default -> -1; - }; - } - private void calculateSize() { if (keys.isEmpty()) { setSize(0, 0); @@ -431,7 +260,7 @@ private String getShortName(String name) { @Override public void render(HudRenderer renderer) { if (keys.isEmpty()) { - if (mc.options != null && preset.get() != Preset.AdvancedCustomization) { + if (mc.options != null) { onPresetChanged(preset.get()); } return; @@ -510,12 +339,11 @@ public enum Preset { Clicks, Actions, Hotbar, - CustomKey, - AdvancedCustomization + Custom } - private static class Key { - public String name; + public static class Key { + public String name = ""; public KeyBinding binding; public Keybind keybind; public int code; @@ -525,6 +353,20 @@ private static class Key { private final RollingCps rollingCps = new RollingCps(); private boolean wasPressed; + public Key() { + this(Keybind.fromKey(GLFW.GLFW_KEY_SPACE), 0, 0, 60, 40); + } + + public Key(NbtCompound compound) { + this.keybind = Keybind.none().fromTag(compound.getCompoundOrEmpty("key")); + this.name = compound.getString("name", ""); + this.x = compound.getDouble("x", 0); + this.y = compound.getDouble("y", 0); + this.width = compound.getDouble("width", 60); + this.height = compound.getDouble("height", 60); + this.showCps = compound.getBoolean("showCps", false); + } + public Key(KeyBinding binding, double x, double y, double width, double height) { this(binding, null, x, y, width, height); } @@ -539,16 +381,6 @@ public Key(KeyBinding binding, String name, double x, double y, double width, do this.height = height; } - public Key(String name, int code, double x, double y, double width, double height) { - this.name = name; - this.code = code; - this.binding = null; - this.x = x; - this.y = y; - this.width = width; - this.height = height; - } - public Key(Keybind keybind, double x, double y, double width, double height) { this.keybind = keybind; this.x = x; @@ -563,8 +395,8 @@ public Key setShowCps(boolean show) { } public String getName() { + if (name != null && !name.isEmpty()) return name; if (keybind != null) return keybind.toString(); - if (name != null) return name; if (binding != null) return binding.getBoundKeyLocalizedText().getString().toUpperCase(); return "?"; } @@ -572,6 +404,7 @@ public String getName() { public boolean isPressed() { long window = mc.getWindow().getHandle(); if (keybind != null) { + if (!keybind.isSet()) return false; int key = keybind.getValue(); boolean isMouse = !keybind.isKey(); if (isMouse) return GLFW.glfwGetMouseButton(window, key) == GLFW.GLFW_PRESS; @@ -602,6 +435,18 @@ public void tick() { public int getCps() { return rollingCps.get(); } + + public NbtCompound serialize() { + NbtCompound compound = new NbtCompound(); + compound.put("key", keybind.toTag()); + compound.putString("name", name); + compound.putDouble("x", x); + compound.putDouble("y", y); + compound.putDouble("width", width); + compound.putDouble("height", height); + compound.putBoolean("showCps", showCps); + return compound; + } } private static class RollingCps { @@ -617,4 +462,207 @@ public int get() { return clicks.size(); } } + + public class CustomKeyListSetting extends Setting> { + public CustomKeyListSetting() { + super("custom-keys", "Configure the custom keys display.", List.of(), k -> onPresetChanged(preset.get()), s -> {}, () -> preset.get() == Preset.Custom); + } + + @Override + protected void resetImpl() { + this.value = new ObjectArrayList<>(); + this.value.add(new Key()); + } + + @Override + protected List parseImpl(String str) { + return List.of(); + } + + @Override + protected boolean isValueValid(List value) { + return true; + } + + @Override + protected NbtCompound save(NbtCompound tag) { + NbtList valueTag = new NbtList(); + for (Key key : get()) { + valueTag.add(key.serialize()); + } + tag.put("value", valueTag); + + return tag; + } + + @Override + protected List load(NbtCompound tag) { + get().clear(); + + for (NbtElement tagI : tag.getListOrEmpty("value")) { + tagI.asCompound().ifPresent(nbtCompound -> get().add(new Key(nbtCompound))); + } + + return get(); + } + } + + public static class CustomKeySettingScreen extends WindowScreen { + private final CustomKeyListSetting setting; + private final Key key; + private final WidgetScreen screen; + + public CustomKeySettingScreen(GuiTheme theme, CustomKeyListSetting setting, Key key, WidgetScreen screen) { + super(theme, "Select Key"); + this.setting = setting; + this.key = key; + this.screen = screen; + } + + @Override + public void initWidgets() { + Settings settings = new Settings(); + SettingGroup sgGeneral = settings.getDefaultGroup(); + + sgGeneral.add(new KeybindSetting.Builder() + .name("custom-key") + .description("The key to display.") + .defaultValue(Keybind.fromKey(GLFW.GLFW_KEY_SPACE)) + .onChanged(k -> { + this.key.keybind = k; + this.screen.reload(); + }) + .onModuleActivated(setting -> setting.set(this.key.keybind)) + .build() + ); + + sgGeneral.add(new StringSetting.Builder() + .name("custom-label") + .description("Replace the Key name with custom text.") + .defaultValue("") + .onChanged(s -> this.key.name = s) + .onModuleActivated(setting -> setting.set(this.key.name)) + .build() + ); + + sgGeneral.add(new DoubleSetting.Builder() + .name("key-width") + .description("Width of the key.") + .defaultValue(60) + .min(20) + .sliderRange(20, 200) + .decimalPlaces(1) + .onChanged(d -> { + this.key.width = d; + this.setting.onChanged(); + }) + .onModuleActivated(setting -> setting.set(this.key.width)) + .build() + ); + + sgGeneral.add(new DoubleSetting.Builder() + .name("key-height") + .description("Height of the key.") + .defaultValue(40) + .min(20) + .sliderRange(20, 200) + .decimalPlaces(1) + .onChanged(d -> { + this.key.height = d; + this.setting.onChanged(); + }) + .onModuleActivated(setting -> setting.set(this.key.height)) + .build() + ); + + sgGeneral.add(new DoubleSetting.Builder() + .name("key-x") + .description("X position offset of the key.") + .defaultValue(0) + .sliderRange(-200, 200) + .decimalPlaces(1) + .onChanged(d -> { + this.key.x = d; + this.setting.onChanged(); + }) + .onModuleActivated(setting -> setting.set(this.key.x)) + .build() + ); + + sgGeneral.add(new DoubleSetting.Builder() + .name("key-y") + .description("Y position offset of the key.") + .defaultValue(0) + .sliderRange(-200, 200) + .decimalPlaces(1) + .onChanged(d -> { + this.key.y = d; + this.setting.onChanged(); + }) + .onModuleActivated(setting -> setting.set(this.key.y)) + .build() + ); + + sgGeneral.add(new BoolSetting.Builder() + .name("show-cps") + .description("Show CPS for this key.") + .defaultValue(false) + .onChanged(b -> this.key.showCps = b) + .onModuleActivated(setting -> setting.set(this.key.showCps)) + .build() + ); + + settings.onActivated(); + + this.add(theme.settings(settings)).expandX(); + } + } + + public static void fillTable(GuiTheme theme, WTable table, CustomKeyListSetting setting) { + table.clear(); + + for (Iterator it = setting.get().iterator(); it.hasNext();) { + Key key = it.next(); + + table.add(theme.label("Key")).expandWidgetX().widget().color(theme.textSecondaryColor()); + table.add(theme.label(String.format("(%s)", key.keybind))).expandWidgetX(); + + WButton edit = table.add(theme.button(GuiRenderer.EDIT)).expandCellX().widget(); + edit.action = () -> { + WidgetScreen screen = (WidgetScreen) mc.currentScreen; + mc.setScreen(new CustomKeySettingScreen(theme, setting, key, screen)); + }; + + WMinus delete = table.add(theme.minus()).right().widget(); + delete.action = () -> { + it.remove(); + setting.onChanged(); + + fillTable(theme, table, setting); + }; + + table.row(); + } + + if (!setting.get().isEmpty()) { + table.add(theme.horizontalSeparator()).expandX(); + table.row(); + } + + WButton add = table.add(theme.button("Add")).expandX().widget(); + add.action = () -> { + setting.get().add(new Key()); + setting.onChanged(); + + fillTable(theme, table, setting); + }; + + WButton reset = table.add(theme.button(GuiRenderer.RESET)).widget(); + reset.action = () -> { + setting.reset(); + + fillTable(theme, table, setting); + }; + reset.tooltip = "Reset"; + } } From 7d2a04b9c1d16958cad2e14909e3b07c291996db Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Sat, 10 Jan 2026 22:40:32 -0500 Subject: [PATCH 03/22] slight cleanup --- .../meteorclient/systems/hud/elements/KeyboardHud.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java index 6afde6d661..0b8c120233 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java @@ -10,7 +10,6 @@ import meteordevelopment.meteorclient.gui.WidgetScreen; import meteordevelopment.meteorclient.gui.WindowScreen; import meteordevelopment.meteorclient.gui.renderer.GuiRenderer; -import meteordevelopment.meteorclient.gui.widgets.WWidget; import meteordevelopment.meteorclient.gui.widgets.containers.WTable; import meteordevelopment.meteorclient.gui.widgets.pressable.WButton; import meteordevelopment.meteorclient.gui.widgets.pressable.WMinus; @@ -65,6 +64,7 @@ public enum Alignment { .defaultValue(1.5) .min(0.5) .sliderRange(0.5, 5) + .decimalPlaces(1) .onChanged(s -> calculateSize()) .build() ); @@ -75,6 +75,7 @@ public enum Alignment { .defaultValue(1) .min(0) .sliderRange(0, 10) + .decimalPlaces(1) .visible(() -> preset.get() != Preset.Custom) .onChanged(s -> onPresetChanged(preset.get())) .build() @@ -156,11 +157,6 @@ public KeyboardHud() { onPresetChanged(preset.get()); } - @Override - public WWidget getWidget(GuiTheme theme) { - return null; - } - @Override public void tick(HudRenderer renderer) { for (Key key : keys) { From 29845460dbf667812a462d64effcea227b40ec33 Mon Sep 17 00:00:00 2001 From: noramibuu <50046813+noramibu@users.noreply.github.com> Date: Sun, 11 Jan 2026 07:11:42 +0300 Subject: [PATCH 04/22] fix for negative offsets --- .../systems/hud/elements/KeyboardHud.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java index 0b8c120233..93451523c3 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java @@ -144,6 +144,7 @@ public enum Alignment { ); private final List keys = new ArrayList<>(); + private double minX, minY; private Color getColor(SettingColor color) { Color c = new Color(color); @@ -208,17 +209,15 @@ private void calculateSize() { setSize(0, 0); return; } - - double maxX = 0; - double maxY = 0; + minX = minY = 0; + double maxX = 0, maxY = 0; for (Key key : keys) { - maxX = Math.max(maxX, key.x + key.width); - maxY = Math.max(maxY, key.y + key.height); + minX = Math.min(minX, key.x); minY = Math.min(minY, key.y); + maxX = Math.max(maxX, key.x + key.width); maxY = Math.max(maxY, key.y + key.height); } - double s = scale.get(); - setSize(maxX * s, maxY * s); + setSize((maxX - minX) * scale.get(), (maxY - minY) * scale.get()); } private String getShortName(String name) { @@ -272,8 +271,8 @@ public void render(HudRenderer renderer) { boolean pressed = key.isPressed(); Color color = pressed ? getColor(pressedColor.get()) : getColor(unpressedColor.get()); - double kX = x + key.x * s; - double kY = y + key.y * s; + double kX = x + (key.x - minX) * s; + double kY = y + (key.y - minY) * s; double kW = key.width * s; double kH = key.height * s; From f4d6ab5d38d6369666460a453328031154bec7af Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Sun, 11 Jan 2026 22:26:38 -0500 Subject: [PATCH 05/22] keyboard preset --- .../systems/hud/elements/KeyboardHud.java | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java index 93451523c3..20e442aebc 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java @@ -197,6 +197,95 @@ private void onPresetChanged(Preset preset) { keys.add(new Key(mc.options.hotbarKeys[i], i * (w + s), 0, w, h)); } } + case Keyboard -> { + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_ESCAPE), 0, 0, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F1), 70, 0, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F2), 109, 0, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F3), 148, 0, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F4), 187, 0, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F5), 241, 0, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F6), 280, 0, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F7), 319, 0, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F8), 358, 0, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F9), 412, 0, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F10), 451, 0, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F11), 490, 0, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F12), 529, 0, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PRINT_SCREEN), 578, 0, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_SCROLL_LOCK), 617, 0, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAUSE), 656, 0, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_GRAVE_ACCENT), 0, 50, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_1), 39, 50, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_2), 78, 50, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_3), 117, 50, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_4), 156, 50, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_5), 195, 50, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_6), 234, 50, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_7), 273, 50, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_8), 312, 50, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_9), 351, 50, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_0), 390, 50, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_MINUS), 429, 50, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_EQUAL), 468, 50, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_BACKSPACE), 507, 50, 57, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_INSERT), 578, 50, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_HOME), 617, 50, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAGE_UP), 656, 50, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_TAB), 0, 89, 52, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_Q), 56, 89, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_W), 95, 89, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_E), 134, 89, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_R), 173, 89, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_T), 212, 89, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_Y), 251, 89, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_U), 290, 89, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_I), 329, 89, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_O), 368, 89, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_P), 407, 89, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_BRACKET), 446, 89, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_BRACKET), 485, 89, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_BACKSLASH), 524, 89, 40, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_DELETE), 578, 89, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_END), 617, 89, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAGE_DOWN), 656, 89, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_CAPS_LOCK), 0, 128, 62, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_A), 66, 128, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_S), 105, 128, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_D), 144, 128, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F), 183, 128, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_G), 222, 128, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_H), 261, 128, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_J), 300, 128, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_K), 339, 128, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_L), 378, 128, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_SEMICOLON), 417, 128, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_APOSTROPHE), 456, 128, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_ENTER), 495, 128, 69, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_SHIFT), 0, 167, 75, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_Z), 79, 167, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_X), 118, 167, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_C), 157, 167, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_V), 196, 167, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_B), 235, 167, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_N), 274, 167, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_M), 313, 167, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_COMMA), 352, 167, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PERIOD), 391, 167, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_SLASH), 430, 167, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_SHIFT), 469, 167, 95, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_UP), 617, 167, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_CONTROL), 0, 206, 50, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_SUPER), 54, 206, 45, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_ALT), 103, 206, 45, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_SPACE), 152, 206, 200, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_ALT), 356, 206, 45, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_SUPER), 405, 206, 45, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_MENU), 454, 206, 50, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_CONTROL), 508, 206, 56, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT), 578, 206, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_DOWN), 617, 206, 35, 35)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT), 656, 206, 35, 35)); + } case Custom -> { keys.addAll(customKeys.get()); } @@ -334,6 +423,7 @@ public enum Preset { Clicks, Actions, Hotbar, + Keyboard, Custom } From f771e92417cd037e16ea1d8f372dae0b709e1b8e Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Sun, 11 Jan 2026 22:40:18 -0500 Subject: [PATCH 06/22] map more keyboard buttons --- .../systems/hud/elements/KeyboardHud.java | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java index 20e442aebc..9e6d8eaeb3 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java @@ -31,6 +31,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Locale; import static meteordevelopment.meteorclient.MeteorClient.mc; @@ -312,31 +313,36 @@ private void calculateSize() { private String getShortName(String name) { if (name == null) return "?"; - return switch (name.toUpperCase()) { + return switch (name.toUpperCase(Locale.ROOT)) { case "LEFT SHIFT", "LSHIFT" -> "LSh"; case "RIGHT SHIFT", "RSHIFT" -> "RSh"; case "LEFT CONTROL", "LCTRL" -> "LCtrl"; case "RIGHT CONTROL", "RCTRL" -> "RCtrl"; case "LEFT ALT", "LALT" -> "LAlt"; case "RIGHT ALT", "RALT" -> "RAlt"; + case "LEFT SUPER" -> "LSup"; + case "RIGHT SUPER" -> "RSup"; + case "GRAVE ACCENT" -> "`"; case "COMMA" -> ","; case "DOT", "PERIOD" -> "."; case "SLASH" -> "/"; + case "APOSTROPHE" -> "'"; case "BACKSPACE" -> "BS"; case "ENTER" -> "Ent"; case "SCROLL" -> "ScrL"; - case "PRINT", "PRTSC" -> "PrtS"; + case "PRINT", "PRTSC", "PRINT SCREEN" -> "PrtS"; case "PAUSE" -> "Paus"; - case "PAGEUP", "PGUP" -> "PgUp"; - case "PAGEDOWN", "PGDN" -> "PgDn"; + case "PAGEUP", "PAGE UP", "PGUP" -> "PgUp"; + case "PAGEDOWN", "PAGE DOWN", "PGDN" -> "PgDn"; case "INSERT", "INS" -> "Ins"; case "DELETE", "DEL" -> "Del"; case "HOME" -> "Home"; case "END" -> "End"; - case "UP" -> "Up"; - case "DOWN" -> "Dn"; - case "LEFT" -> "Lt"; - case "RIGHT" -> "Rt"; + case "ARROW UP", "UP" -> "Up"; + case "ARROW DOWN", "DOWN" -> "Dn"; + case "ARROW LEFT", "LEFT" -> "Lt"; + case "ARROW RIGHT", "RIGHT" -> "Rt"; + case "UNKNOWN" -> "?"; default -> name; }; } @@ -482,7 +488,7 @@ public Key setShowCps(boolean show) { public String getName() { if (name != null && !name.isEmpty()) return name; if (keybind != null) return keybind.toString(); - if (binding != null) return binding.getBoundKeyLocalizedText().getString().toUpperCase(); + if (binding != null) return binding.getBoundKeyLocalizedText().getString(); return "?"; } From bab4d9130a9af4e07106863a66397e9882c5bbd8 Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Sun, 11 Jan 2026 22:51:18 -0500 Subject: [PATCH 07/22] optimizations, cleanups, and cleandowns --- .../systems/hud/elements/KeyboardHud.java | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java index 9e6d8eaeb3..1e4a707075 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java @@ -199,6 +199,7 @@ private void onPresetChanged(Preset preset) { } } case Keyboard -> { + // spacing? i aint see no damn spacing keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_ESCAPE), 0, 0, 35, 35)); keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F1), 70, 0, 35, 35)); keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F2), 109, 0, 35, 35)); @@ -287,9 +288,7 @@ private void onPresetChanged(Preset preset) { keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_DOWN), 617, 206, 35, 35)); keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT), 656, 206, 35, 35)); } - case Custom -> { - keys.addAll(customKeys.get()); - } + case Custom -> keys.addAll(customKeys.get()); } calculateSize(); } @@ -310,9 +309,7 @@ private void calculateSize() { setSize((maxX - minX) * scale.get(), (maxY - minY) * scale.get()); } - private String getShortName(String name) { - if (name == null) - return "?"; + private static String getShortName(String name) { return switch (name.toUpperCase(Locale.ROOT)) { case "LEFT SHIFT", "LSHIFT" -> "LSh"; case "RIGHT SHIFT", "RSHIFT" -> "RSh"; @@ -373,8 +370,7 @@ public void render(HudRenderer renderer) { renderer.quad(kX, kY, kW, kH, color); - String text = getShortName(key.getName()); - String cpsText = key.showCps ? key.getCps() + " CPS" : ""; + String text = key.getName(); Color txtColor = getColor(textColor.get()); double padding = 2 * s; @@ -382,19 +378,20 @@ public void render(HudRenderer renderer) { double availableHeight = kH - padding * 2; double tH = renderer.textHeight(); - if (cpsText.isEmpty()) { + if (!key.showCps) { double tW = renderer.textWidth(text); double widthScale = tW > availableWidth ? availableWidth / tW : 1.0; double heightScale = tH > availableHeight * 0.6 ? (availableHeight * 0.6) / tH : 1.0; double textScale = Math.min(widthScale, heightScale); double yText = kY + (kH - tH * textScale) / 2; - drawTextLine(renderer, text, kX, yText, kW, textScale, txtColor); + drawTextLine(renderer, text, tW, kX, yText, kW, textScale, txtColor); } else { double topW = renderer.textWidth(text); double topWidthScale = topW > availableWidth ? availableWidth / topW : 1.0; double topHeightScale = tH > availableHeight * 0.4 ? (availableHeight * 0.4) / tH : 1.0; double topScale = Math.min(topWidthScale, topHeightScale); + String cpsText = key.getCps() + " CPS"; double botW = renderer.textWidth(cpsText); double botWidthScale = botW > availableWidth ? availableWidth / botW : 1.0; double botHeightScale = tH > availableHeight * 0.4 ? (availableHeight * 0.4) / tH : 1.0; @@ -403,23 +400,22 @@ public void render(HudRenderer renderer) { double totalHeight = (tH * topScale) + (tH * botScale); double startY = kY + (kH - totalHeight) / 2; - drawTextLine(renderer, text, kX, startY, kW, topScale, txtColor); - drawTextLine(renderer, cpsText, kX, startY + tH * topScale, kW, botScale, txtColor); + drawTextLine(renderer, text, topW, kX, startY, kW, topScale, txtColor); + drawTextLine(renderer, cpsText, botW, kX, startY + tH * topScale, kW, botScale, txtColor); } } } - private void drawTextLine(HudRenderer renderer, String text, double x, double y, double w, double textScale, + private void drawTextLine(HudRenderer renderer, String text, double textWidth, double x, double y, double w, double textScale, Color color) { - double tW = renderer.textWidth(text); double s = scale.get(); double padding = 2 * s; - double xText = x + (w - tW * textScale) / 2; + double xText = x + (w - textWidth * textScale) / 2; if (alignment.get() == Alignment.Left) xText = x + padding; else if (alignment.get() == Alignment.Right) - xText = x + w - padding - tW * textScale; + xText = x + w - padding - textWidth * textScale; renderer.text(text, xText, y, color, false, textScale); } @@ -487,8 +483,8 @@ public Key setShowCps(boolean show) { public String getName() { if (name != null && !name.isEmpty()) return name; - if (keybind != null) return keybind.toString(); - if (binding != null) return binding.getBoundKeyLocalizedText().getString(); + if (keybind != null) return getShortName(keybind.toString()); + if (binding != null) return getShortName(binding.getBoundKeyLocalizedText().getString()); return "?"; } From ed8bf3c8ae6ea2b9c0f681ec97f7e8d57de583e6 Mon Sep 17 00:00:00 2001 From: noramibuu <50046813+noramibu@users.noreply.github.com> Date: Mon, 12 Jan 2026 07:09:55 +0300 Subject: [PATCH 08/22] refactor keyboard preset --- .../systems/hud/elements/KeyboardHud.java | 145 +++++++----------- .../meteorclient/utils/Utils.java | 1 + 2 files changed, 57 insertions(+), 89 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java index 1e4a707075..e43c585513 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java @@ -199,94 +199,61 @@ private void onPresetChanged(Preset preset) { } } case Keyboard -> { - // spacing? i aint see no damn spacing - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_ESCAPE), 0, 0, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F1), 70, 0, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F2), 109, 0, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F3), 148, 0, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F4), 187, 0, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F5), 241, 0, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F6), 280, 0, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F7), 319, 0, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F8), 358, 0, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F9), 412, 0, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F10), 451, 0, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F11), 490, 0, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F12), 529, 0, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PRINT_SCREEN), 578, 0, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_SCROLL_LOCK), 617, 0, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAUSE), 656, 0, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_GRAVE_ACCENT), 0, 50, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_1), 39, 50, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_2), 78, 50, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_3), 117, 50, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_4), 156, 50, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_5), 195, 50, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_6), 234, 50, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_7), 273, 50, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_8), 312, 50, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_9), 351, 50, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_0), 390, 50, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_MINUS), 429, 50, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_EQUAL), 468, 50, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_BACKSPACE), 507, 50, 57, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_INSERT), 578, 50, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_HOME), 617, 50, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAGE_UP), 656, 50, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_TAB), 0, 89, 52, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_Q), 56, 89, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_W), 95, 89, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_E), 134, 89, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_R), 173, 89, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_T), 212, 89, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_Y), 251, 89, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_U), 290, 89, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_I), 329, 89, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_O), 368, 89, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_P), 407, 89, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_BRACKET), 446, 89, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_BRACKET), 485, 89, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_BACKSLASH), 524, 89, 40, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_DELETE), 578, 89, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_END), 617, 89, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAGE_DOWN), 656, 89, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_CAPS_LOCK), 0, 128, 62, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_A), 66, 128, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_S), 105, 128, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_D), 144, 128, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F), 183, 128, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_G), 222, 128, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_H), 261, 128, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_J), 300, 128, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_K), 339, 128, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_L), 378, 128, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_SEMICOLON), 417, 128, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_APOSTROPHE), 456, 128, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_ENTER), 495, 128, 69, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_SHIFT), 0, 167, 75, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_Z), 79, 167, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_X), 118, 167, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_C), 157, 167, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_V), 196, 167, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_B), 235, 167, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_N), 274, 167, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_M), 313, 167, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_COMMA), 352, 167, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PERIOD), 391, 167, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_SLASH), 430, 167, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_SHIFT), 469, 167, 95, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_UP), 617, 167, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_CONTROL), 0, 206, 50, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_SUPER), 54, 206, 45, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_ALT), 103, 206, 45, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_SPACE), 152, 206, 200, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_ALT), 356, 206, 45, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_SUPER), 405, 206, 45, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_MENU), 454, 206, 50, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_CONTROL), 508, 206, 56, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT), 578, 206, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_DOWN), 617, 206, 35, 35)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT), 656, 206, 35, 35)); + double u = 35; // base key unit size + double g = s; // gap between keys (spacing setting) + double row0 = 0, row1 = u + 15 + g, row2 = row1 + u + g, row3 = row2 + u + g, row4 = row3 + u + g, row5 = row4 + u + g; + + // Row 0: ESC, F1-F12, Print/Scroll/Pause + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_ESCAPE), 0, row0, u, u)); + for (int i = 0; i < 4; i++) keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F1 + i), (u + g) * 2 + i * (u + g), row0, u, u)); + for (int i = 0; i < 4; i++) keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F5 + i), (u + g) * 6.5 + i * (u + g), row0, u, u)); + for (int i = 0; i < 4; i++) keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F9 + i), (u + g) * 11 + i * (u + g), row0, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PRINT_SCREEN), (u + g) * 15.5, row0, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_SCROLL_LOCK), (u + g) * 16.5, row0, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAUSE), (u + g) * 17.5, row0, u, u)); + + // Row 1: ` 1-0 - = BS, Ins/Home/PgUp + int[] row1Keys = {GLFW.GLFW_KEY_GRAVE_ACCENT, GLFW.GLFW_KEY_1, GLFW.GLFW_KEY_2, GLFW.GLFW_KEY_3, GLFW.GLFW_KEY_4, GLFW.GLFW_KEY_5, GLFW.GLFW_KEY_6, GLFW.GLFW_KEY_7, GLFW.GLFW_KEY_8, GLFW.GLFW_KEY_9, GLFW.GLFW_KEY_0, GLFW.GLFW_KEY_MINUS, GLFW.GLFW_KEY_EQUAL}; + for (int i = 0; i < row1Keys.length; i++) keys.add(new Key(Keybind.fromKey(row1Keys[i]), i * (u + g), row1, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_BACKSPACE), 13 * (u + g), row1, u * 1.6 + g, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_INSERT), (u + g) * 15.5, row1, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_HOME), (u + g) * 16.5, row1, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAGE_UP), (u + g) * 17.5, row1, u, u)); + + // Row 2: Tab QWERTY..., Del/End/PgDn + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_TAB), 0, row2, u * 1.5, u)); + int[] row2Keys = {GLFW.GLFW_KEY_Q, GLFW.GLFW_KEY_W, GLFW.GLFW_KEY_E, GLFW.GLFW_KEY_R, GLFW.GLFW_KEY_T, GLFW.GLFW_KEY_Y, GLFW.GLFW_KEY_U, GLFW.GLFW_KEY_I, GLFW.GLFW_KEY_O, GLFW.GLFW_KEY_P, GLFW.GLFW_KEY_LEFT_BRACKET, GLFW.GLFW_KEY_RIGHT_BRACKET}; + for (int i = 0; i < row2Keys.length; i++) keys.add(new Key(Keybind.fromKey(row2Keys[i]), u * 1.5 + g + i * (u + g), row2, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_BACKSLASH), u * 1.5 + g + 12 * (u + g), row2, u * 1.1 + g, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_DELETE), (u + g) * 15.5, row2, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_END), (u + g) * 16.5, row2, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAGE_DOWN), (u + g) * 17.5, row2, u, u)); + + // Row 3: Caps ASDF..., Enter + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_CAPS_LOCK), 0, row3, u * 1.75, u)); + int[] row3Keys = {GLFW.GLFW_KEY_A, GLFW.GLFW_KEY_S, GLFW.GLFW_KEY_D, GLFW.GLFW_KEY_F, GLFW.GLFW_KEY_G, GLFW.GLFW_KEY_H, GLFW.GLFW_KEY_J, GLFW.GLFW_KEY_K, GLFW.GLFW_KEY_L, GLFW.GLFW_KEY_SEMICOLON, GLFW.GLFW_KEY_APOSTROPHE}; + for (int i = 0; i < row3Keys.length; i++) keys.add(new Key(Keybind.fromKey(row3Keys[i]), u * 1.75 + g + i * (u + g), row3, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_ENTER), u * 1.75 + g + 11 * (u + g), row3, u * 2 + g, u)); + + // Row 4: LShift ZXCV..., RShift, Up + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_SHIFT), 0, row4, u * 2.1, u)); + int[] row4Keys = {GLFW.GLFW_KEY_Z, GLFW.GLFW_KEY_X, GLFW.GLFW_KEY_C, GLFW.GLFW_KEY_V, GLFW.GLFW_KEY_B, GLFW.GLFW_KEY_N, GLFW.GLFW_KEY_M, GLFW.GLFW_KEY_COMMA, GLFW.GLFW_KEY_PERIOD, GLFW.GLFW_KEY_SLASH}; + for (int i = 0; i < row4Keys.length; i++) keys.add(new Key(Keybind.fromKey(row4Keys[i]), u * 2.1 + g + i * (u + g), row4, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_SHIFT), u * 2.1 + g + 10 * (u + g), row4, u * 2.7, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_UP), (u + g) * 16.5, row4, u, u)); + + // Row 5: Ctrl/Win/Alt/Space/Alt/Win/Menu/Ctrl, Arrows + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_CONTROL), 0, row5, u * 1.4, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_SUPER), u * 1.4 + g, row5, u * 1.25, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_ALT), u * 2.65 + 2 * g, row5, u * 1.25, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_SPACE), u * 3.9 + 3 * g, row5, u * 5.6, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_ALT), u * 9.5 + 4 * g, row5, u * 1.25, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_SUPER), u * 10.75 + 5 * g, row5, u * 1.25, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_MENU), u * 12 + 6 * g, row5, u * 1.4, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_CONTROL), u * 13.4 + 7 * g, row5, u * 1.5, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT), (u + g) * 15.5, row5, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_DOWN), (u + g) * 16.5, row5, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT), (u + g) * 17.5, row5, u, u)); } case Custom -> keys.addAll(customKeys.get()); } @@ -326,7 +293,7 @@ private static String getShortName(String name) { case "APOSTROPHE" -> "'"; case "BACKSPACE" -> "BS"; case "ENTER" -> "Ent"; - case "SCROLL" -> "ScrL"; + case "SCROLL", "SCROLL LOCK" -> "ScrL"; case "PRINT", "PRTSC", "PRINT SCREEN" -> "PrtS"; case "PAUSE" -> "Paus"; case "PAGEUP", "PAGE UP", "PGUP" -> "PgUp"; diff --git a/src/main/java/meteordevelopment/meteorclient/utils/Utils.java b/src/main/java/meteordevelopment/meteorclient/utils/Utils.java index 1e5332be61..ced5a526ce 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/Utils.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/Utils.java @@ -463,6 +463,7 @@ public static String getKeyName(int key) { case GLFW_KEY_ENTER -> "Enter"; case GLFW_KEY_KP_ENTER -> "Numpad Enter"; case GLFW_KEY_NUM_LOCK -> "Num Lock"; + case GLFW_KEY_SCROLL_LOCK -> "Scroll Lock"; case GLFW_KEY_SPACE -> "Space"; case GLFW_KEY_F1 -> "F1"; case GLFW_KEY_F2 -> "F2"; From c2c12599c7cc7cdf9a20d0ef885420f86a185f40 Mon Sep 17 00:00:00 2001 From: noramibuu <50046813+noramibu@users.noreply.github.com> Date: Mon, 12 Jan 2026 07:31:43 +0300 Subject: [PATCH 09/22] added border, fixed rainbow --- .../systems/hud/elements/KeyboardHud.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java index e43c585513..e2276e067d 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java @@ -47,6 +47,7 @@ public enum Alignment { private final SettingGroup sgGeneral = settings.getDefaultGroup(); private final SettingGroup sgColor = settings.createGroup("Color"); + private final SettingGroup sgBorder = settings.createGroup("Border"); private final SettingGroup sgBackground = settings.createGroup("Background"); private final Setting preset = sgGeneral.add(new EnumSetting.Builder() @@ -119,6 +120,31 @@ public enum Alignment { .build() ); + private final Setting border = sgBorder.add(new BoolSetting.Builder() + .name("border") + .description("Draw a border around keys.") + .defaultValue(true) + .build() + ); + + private final Setting borderColor = sgBorder.add(new ColorSetting.Builder() + .name("border-color") + .description("Color of the key border.") + .visible(border::get) + .defaultValue(new SettingColor(255, 255, 255, 200)) + .build() + ); + + private final Setting borderWidth = sgBorder.add(new DoubleSetting.Builder() + .name("border-width") + .description("Width of the key border.") + .visible(border::get) + .defaultValue(1.0) + .min(0.5) + .sliderRange(0.5, 5) + .build() + ); + private final Setting opacity = sgColor.add(new DoubleSetting.Builder() .name("opacity") .description("Opacity of the whole element.") @@ -148,6 +174,7 @@ public enum Alignment { private double minX, minY; private Color getColor(SettingColor color) { + color.update(); Color c = new Color(color); c.a = (int) (c.a * opacity.get()); return c; @@ -337,6 +364,15 @@ public void render(HudRenderer renderer) { renderer.quad(kX, kY, kW, kH, color); + if (border.get()) { + Color bColor = getColor(borderColor.get()); + double bw = borderWidth.get(); + renderer.quad(kX, kY, kW, bw, bColor); + renderer.quad(kX, kY + kH - bw, kW, bw, bColor); + renderer.quad(kX, kY, bw, kH, bColor); + renderer.quad(kX + kW - bw, kY, bw, kH, bColor); + } + String text = key.getName(); Color txtColor = getColor(textColor.get()); From 0995f7beda22da4383231c571833b83dd6d144e9 Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Mon, 12 Jan 2026 00:15:44 -0500 Subject: [PATCH 10/22] color fade & optimize color handling --- .../systems/hud/elements/KeyboardHud.java | 81 ++++++++++++++++--- 1 file changed, 71 insertions(+), 10 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java index e2276e067d..e0bba3fe29 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java @@ -22,10 +22,12 @@ import meteordevelopment.meteorclient.utils.misc.Keybind; import meteordevelopment.meteorclient.utils.render.color.Color; import meteordevelopment.meteorclient.utils.render.color.SettingColor; +import net.minecraft.client.MinecraftClient; import net.minecraft.client.option.KeyBinding; import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtElement; import net.minecraft.nbt.NbtList; +import net.minecraft.util.math.MathHelper; import org.lwjgl.glfw.GLFW; import java.util.ArrayList; @@ -113,6 +115,24 @@ public enum Alignment { .build() ); + private final Setting colorFade = sgColor.add(new BoolSetting.Builder() + .name("color-fade") + .description("Whether to fade the key color when pressing/unpressing.") + .defaultValue(false) + .build() + ); + + private final Setting fadeTime = sgColor.add(new DoubleSetting.Builder() + .name("fade-time") + .description("How long to fade the color for, in seconds.") + .visible(colorFade::get) + .defaultValue(0.2d) + .min(0.01d) + .sliderRange(0.01d, 1d) + .decimalPlaces(2) + .build() + ); + private final Setting textColor = sgColor.add(new ColorSetting.Builder() .name("text-color") .description("Color of the key name.") @@ -173,11 +193,50 @@ public enum Alignment { private final List keys = new ArrayList<>(); private double minX, minY; - private Color getColor(SettingColor color) { - color.update(); - Color c = new Color(color); - c.a = (int) (c.a * opacity.get()); - return c; + private Color getColor(SettingColor color, SettingColor out) { + out.set(color); + out.a = (int) (out.a * opacity.get()); + return out; + } + + private Color getKeyColor(Key key, SettingColor out) { + if (colorFade.get()) { + boolean pressed = key.isPressed(); + float target = pressed ? 1 : 0; + + float tickDelta = MinecraftClient.getInstance().getRenderTickCounter().getDynamicDeltaTicks(); + float frameDelta = (float) (tickDelta / 20 / fadeTime.get()) * (pressed ? 1 : -1); + key.delta = Math.clamp(key.delta + frameDelta, 0, 1); + + if (key.delta == target) { + out.set(target == 1 ? pressedColor.get() : unpressedColor.get()); + } else { + Color c1 = pressedColor.get(); + Color c2 = unpressedColor.get(); + + float[] hsb1 = new float[3]; + float[] hsb2 = new float[3]; + + java.awt.Color.RGBtoHSB(c1.r, c1.g, c1.b, hsb2); + java.awt.Color.RGBtoHSB(c2.r, c2.g, c2.b, hsb1); + + int rgb = java.awt.Color.HSBtoRGB( + MathHelper.lerp(key.delta, hsb1[0], hsb2[0]), + MathHelper.lerp(key.delta, hsb1[1], hsb2[1]), + MathHelper.lerp(key.delta, hsb1[2], hsb2[2]) + ); + + out.r = Color.toRGBAR(rgb); + out.g = Color.toRGBAG(rgb); + out.b = Color.toRGBAB(rgb); + out.a = MathHelper.lerp(key.delta, pressedColor.get().a, unpressedColor.get().a); + } + } else { + out.set(key.isPressed() ? pressedColor.get() : unpressedColor.get()); + } + + out.a = (int) (out.a * opacity.get()); + return out; } public KeyboardHud() { @@ -347,15 +406,16 @@ public void render(HudRenderer renderer) { return; } + SettingColor mutableColor = new SettingColor(); + if (background.get()) { - renderer.quad(x, y, getWidth(), getHeight(), getColor(backgroundColor.get())); + renderer.quad(x, y, getWidth(), getHeight(), getColor(backgroundColor.get(), mutableColor)); } double s = scale.get(); for (Key key : keys) { - boolean pressed = key.isPressed(); - Color color = pressed ? getColor(pressedColor.get()) : getColor(unpressedColor.get()); + Color color = getKeyColor(key, mutableColor); double kX = x + (key.x - minX) * s; double kY = y + (key.y - minY) * s; @@ -365,7 +425,7 @@ public void render(HudRenderer renderer) { renderer.quad(kX, kY, kW, kH, color); if (border.get()) { - Color bColor = getColor(borderColor.get()); + Color bColor = getColor(borderColor.get(), mutableColor); double bw = borderWidth.get(); renderer.quad(kX, kY, kW, bw, bColor); renderer.quad(kX, kY + kH - bw, kW, bw, bColor); @@ -374,7 +434,7 @@ public void render(HudRenderer renderer) { } String text = key.getName(); - Color txtColor = getColor(textColor.get()); + Color txtColor = getColor(textColor.get(), mutableColor); double padding = 2 * s; double availableWidth = kW - padding * 2; @@ -442,6 +502,7 @@ public static class Key { private final RollingCps rollingCps = new RollingCps(); private boolean wasPressed; + private float delta; public Key() { this(Keybind.fromKey(GLFW.GLFW_KEY_SPACE), 0, 0, 60, 40); From bff55305ccad1f03a5e123c084ddac3c78fc74b5 Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Mon, 12 Jan 2026 01:07:23 -0500 Subject: [PATCH 11/22] update rainbow colors --- .../meteorclient/systems/hud/elements/KeyboardHud.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java index e0bba3fe29..3d1fb7ef9b 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java @@ -407,6 +407,11 @@ public void render(HudRenderer renderer) { } SettingColor mutableColor = new SettingColor(); + pressedColor.get().update(); + unpressedColor.get().update(); + textColor.get().update(); + borderColor.get().update(); + backgroundColor.get().update(); if (background.get()) { renderer.quad(x, y, getWidth(), getHeight(), getColor(backgroundColor.get(), mutableColor)); From 8ba0c668b1c67e72ca9d76e3ff96dd355a8742a5 Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Mon, 12 Jan 2026 03:16:14 -0500 Subject: [PATCH 12/22] improve keyboard row scaling algorithm --- .../systems/hud/elements/KeyboardHud.java | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java index 3d1fb7ef9b..17c5fbf0d8 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java @@ -289,11 +289,15 @@ private void onPresetChanged(Preset preset) { double g = s; // gap between keys (spacing setting) double row0 = 0, row1 = u + 15 + g, row2 = row1 + u + g, row3 = row2 + u + g, row4 = row3 + u + g, row5 = row4 + u + g; + double targetLen = 14 * (u + g) + u * 0.6; + double padding; + // Row 0: ESC, F1-F12, Print/Scroll/Pause + padding = targetLen - (u * 13 + g * 12); keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_ESCAPE), 0, row0, u, u)); - for (int i = 0; i < 4; i++) keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F1 + i), (u + g) * 2 + i * (u + g), row0, u, u)); - for (int i = 0; i < 4; i++) keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F5 + i), (u + g) * 6.5 + i * (u + g), row0, u, u)); - for (int i = 0; i < 4; i++) keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F9 + i), (u + g) * 11 + i * (u + g), row0, u, u)); + for (int i = 0; i < 4; i++) keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F1 + i), (u + g) + padding / 2 + i * (u + g), row0, u, u)); + for (int i = 0; i < 4; i++) keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F5 + i), (u + g) * 5 + padding * 3 / 4 + i * (u + g), row0, u, u)); + for (int i = 0; i < 4; i++) keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F9 + i), (u + g) * 9 + padding + i * (u + g), row0, u, u)); keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PRINT_SCREEN), (u + g) * 15.5, row0, u, u)); keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_SCROLL_LOCK), (u + g) * 16.5, row0, u, u)); keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAUSE), (u + g) * 17.5, row0, u, u)); @@ -316,27 +320,30 @@ private void onPresetChanged(Preset preset) { keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAGE_DOWN), (u + g) * 17.5, row2, u, u)); // Row 3: Caps ASDF..., Enter - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_CAPS_LOCK), 0, row3, u * 1.75, u)); + padding = targetLen - (u * 11 + g * 12); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_CAPS_LOCK), 0, row3, padding * (1.75 / 3.75), u)); int[] row3Keys = {GLFW.GLFW_KEY_A, GLFW.GLFW_KEY_S, GLFW.GLFW_KEY_D, GLFW.GLFW_KEY_F, GLFW.GLFW_KEY_G, GLFW.GLFW_KEY_H, GLFW.GLFW_KEY_J, GLFW.GLFW_KEY_K, GLFW.GLFW_KEY_L, GLFW.GLFW_KEY_SEMICOLON, GLFW.GLFW_KEY_APOSTROPHE}; - for (int i = 0; i < row3Keys.length; i++) keys.add(new Key(Keybind.fromKey(row3Keys[i]), u * 1.75 + g + i * (u + g), row3, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_ENTER), u * 1.75 + g + 11 * (u + g), row3, u * 2 + g, u)); + for (int i = 0; i < row3Keys.length; i++) keys.add(new Key(Keybind.fromKey(row3Keys[i]), padding * (1.75 / 3.75) + g + i * (u + g), row3, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_ENTER), padding * (1.75 / 3.75) + g + 11 * (u + g), row3, padding * (2 / 3.75), u)); // Row 4: LShift ZXCV..., RShift, Up - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_SHIFT), 0, row4, u * 2.1, u)); + padding = targetLen - (u * 10 + g * 11); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_SHIFT), 0, row4, padding * (2.1 / 4.8), u)); int[] row4Keys = {GLFW.GLFW_KEY_Z, GLFW.GLFW_KEY_X, GLFW.GLFW_KEY_C, GLFW.GLFW_KEY_V, GLFW.GLFW_KEY_B, GLFW.GLFW_KEY_N, GLFW.GLFW_KEY_M, GLFW.GLFW_KEY_COMMA, GLFW.GLFW_KEY_PERIOD, GLFW.GLFW_KEY_SLASH}; - for (int i = 0; i < row4Keys.length; i++) keys.add(new Key(Keybind.fromKey(row4Keys[i]), u * 2.1 + g + i * (u + g), row4, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_SHIFT), u * 2.1 + g + 10 * (u + g), row4, u * 2.7, u)); + for (int i = 0; i < row4Keys.length; i++) keys.add(new Key(Keybind.fromKey(row4Keys[i]), padding * (2.1 / 4.8) + g + i * (u + g), row4, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_SHIFT), padding * (2.1 / 4.8) + g + 10 * (u + g), row4, padding * (2.7 / 4.8), u)); keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_UP), (u + g) * 16.5, row4, u, u)); // Row 5: Ctrl/Win/Alt/Space/Alt/Win/Menu/Ctrl, Arrows - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_CONTROL), 0, row5, u * 1.4, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_SUPER), u * 1.4 + g, row5, u * 1.25, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_ALT), u * 2.65 + 2 * g, row5, u * 1.25, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_SPACE), u * 3.9 + 3 * g, row5, u * 5.6, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_ALT), u * 9.5 + 4 * g, row5, u * 1.25, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_SUPER), u * 10.75 + 5 * g, row5, u * 1.25, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_MENU), u * 12 + 6 * g, row5, u * 1.4, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_CONTROL), u * 13.4 + 7 * g, row5, u * 1.5, u)); + padding = targetLen - (g * 7); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_CONTROL), 0, row5, padding * (1.4 / 14.9), u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_SUPER), padding * (1.4 / 14.9) + g, row5, padding * (1.25 / 14.9), u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_ALT), padding * (2.65 / 14.9) + g * 2, row5, padding * (1.25 / 14.9), u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_SPACE), padding * (3.9 / 14.9) + g * 3, row5, padding * (5.6 / 14.9), u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_ALT), padding * (9.5 / 14.9) + g * 4, row5, padding * (1.25 / 14.9), u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_SUPER), padding * (10.75 / 14.9) + g * 5, row5, padding * (1.25 / 14.9), u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_MENU), padding * (12 / 14.9) + g * 6, row5, padding * (1.4 / 14.9), u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_CONTROL), padding * (13.4 / 14.9) + g * 7, row5, padding * (1.5 / 14.9), u)); keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT), (u + g) * 15.5, row5, u, u)); keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_DOWN), (u + g) * 16.5, row5, u, u)); keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT), (u + g) * 17.5, row5, u, u)); From 6387dd9feff3ec6ecb57e80a940855847607740b Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Mon, 12 Jan 2026 04:30:13 -0500 Subject: [PATCH 13/22] use event handling to register inputs --- .../systems/hud/elements/KeyboardHud.java | 102 ++++++++++++------ 1 file changed, 69 insertions(+), 33 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java index 17c5fbf0d8..262f895cd8 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java @@ -6,6 +6,9 @@ package meteordevelopment.meteorclient.systems.hud.elements; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import meteordevelopment.meteorclient.MeteorClient; +import meteordevelopment.meteorclient.events.meteor.KeyEvent; +import meteordevelopment.meteorclient.events.meteor.MouseClickEvent; import meteordevelopment.meteorclient.gui.GuiTheme; import meteordevelopment.meteorclient.gui.WidgetScreen; import meteordevelopment.meteorclient.gui.WindowScreen; @@ -20,10 +23,15 @@ import meteordevelopment.meteorclient.systems.hud.HudElementInfo; import meteordevelopment.meteorclient.systems.hud.HudRenderer; import meteordevelopment.meteorclient.utils.misc.Keybind; +import meteordevelopment.meteorclient.utils.misc.input.KeyAction; +import meteordevelopment.meteorclient.utils.misc.input.KeyBinds; import meteordevelopment.meteorclient.utils.render.color.Color; import meteordevelopment.meteorclient.utils.render.color.SettingColor; +import meteordevelopment.orbit.EventHandler; +import meteordevelopment.orbit.EventPriority; import net.minecraft.client.MinecraftClient; import net.minecraft.client.option.KeyBinding; +import net.minecraft.client.util.InputUtil; import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtElement; import net.minecraft.nbt.NbtList; @@ -201,7 +209,7 @@ private Color getColor(SettingColor color, SettingColor out) { private Color getKeyColor(Key key, SettingColor out) { if (colorFade.get()) { - boolean pressed = key.isPressed(); + boolean pressed = key.isPressed; float target = pressed ? 1 : 0; float tickDelta = MinecraftClient.getInstance().getRenderTickCounter().getDynamicDeltaTicks(); @@ -232,26 +240,38 @@ private Color getKeyColor(Key key, SettingColor out) { out.a = MathHelper.lerp(key.delta, pressedColor.get().a, unpressedColor.get().a); } } else { - out.set(key.isPressed() ? pressedColor.get() : unpressedColor.get()); + out.set(key.isPressed ? pressedColor.get() : unpressedColor.get()); } out.a = (int) (out.a * opacity.get()); return out; } - public KeyboardHud() { - super(INFO); - if (mc.options != null) - onPresetChanged(preset.get()); + @EventHandler(priority = EventPriority.HIGH) + private void onKey(KeyEvent event) { + for (Key key : keys) { + if (key.matches(event.input.key(), event.input.scancode(), true)) { + key.update(event.action); + } + } } - @Override - public void tick(HudRenderer renderer) { + @EventHandler(priority = EventPriority.HIGH) + private void onMouseClick(MouseClickEvent event) { for (Key key : keys) { - key.tick(); + if (key.matches(event.input.button(), -1, false)) { + key.update(event.action); + } } } + public KeyboardHud() { + super(INFO); + if (mc.options != null) + onPresetChanged(preset.get()); + MeteorClient.EVENT_BUS.subscribe(this); + } + private void onPresetChanged(Preset preset) { if (mc.options == null) return; @@ -426,7 +446,16 @@ public void render(HudRenderer renderer) { double s = scale.get(); + // because of a meteor bug, the modules search field swallows inputs + InputUtil.Key guiKey = ((KeyBindingAccessor) KeyBinds.OPEN_GUI).meteor$getKey(); + for (Key key : keys) { + if (key.matches(guiKey.getCode(), guiKey.getCode(), guiKey.getCategory() != InputUtil.Type.MOUSE)) { + if (key.isPressed != key.isNativelyPressed()) { + key.update(key.isPressed ? KeyAction.Release : KeyAction.Press); + } + } + Color color = getKeyColor(key, mutableColor); double kX = x + (key.x - minX) * s; @@ -513,7 +542,7 @@ public static class Key { public boolean showCps = false; private final RollingCps rollingCps = new RollingCps(); - private boolean wasPressed; + private boolean isPressed; private float delta; public Key() { @@ -564,35 +593,42 @@ public String getName() { return "?"; } - public boolean isPressed() { - long window = mc.getWindow().getHandle(); - if (keybind != null) { - if (!keybind.isSet()) return false; - int key = keybind.getValue(); - boolean isMouse = !keybind.isKey(); - if (isMouse) return GLFW.glfwGetMouseButton(window, key) == GLFW.GLFW_PRESS; - return GLFW.glfwGetKey(window, key) == GLFW.GLFW_PRESS; + public boolean matches(int input, int scancode, boolean key) { + if (keybind != null ) { + return keybind.isKey() == key && keybind.getValue() == input; + } else { + InputUtil.Key inputKey = ((KeyBindingAccessor) binding).meteor$getKey(); + boolean isKey = inputKey.getCategory() != InputUtil.Type.MOUSE; + return isKey == key && inputKey.getCategory() == InputUtil.Type.SCANCODE + ? scancode == inputKey.getCode() + : input == inputKey.getCode(); } - if (binding != null) { - int key = ((KeyBindingAccessor) binding).meteor$getKey().getCode(); - if (key >= 0 && key < 8) { - return GLFW.glfwGetMouseButton(window, key) == GLFW.GLFW_PRESS; + } + + public void update(KeyAction action) { + if (action == KeyAction.Press) { + isPressed = true; + if (showCps) { + rollingCps.add(); } - return GLFW.glfwGetKey(window, key) == GLFW.GLFW_PRESS; - } - if (code >= 0) { - if (code < 8) return GLFW.glfwGetMouseButton(window, code) == GLFW.GLFW_PRESS; - return GLFW.glfwGetKey(window, code) == GLFW.GLFW_PRESS; + } else { + isPressed = false; } - return false; } - public void tick() { - boolean pressed = isPressed(); - if (pressed && !wasPressed && showCps) { - rollingCps.add(); + public boolean isNativelyPressed() { + long window = mc.getWindow().getHandle(); + if (keybind != null) { + if (!keybind.isSet()) return false; + return keybind.isKey() + ? GLFW.glfwGetKey(window, keybind.getValue()) != GLFW.GLFW_RELEASE + : GLFW.glfwGetMouseButton(window, keybind.getValue()) != GLFW.GLFW_RELEASE; + } else { + int key = ((KeyBindingAccessor) binding).meteor$getKey().getCode(); + return key >= 0 && key < 8 + ? GLFW.glfwGetMouseButton(window, key) != GLFW.GLFW_RELEASE + : GLFW.glfwGetKey(window, key) != GLFW.GLFW_RELEASE; } - wasPressed = pressed; } public int getCps() { From 9f0858e6b24993ca77ed22ea0264c51db80884dc Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Mon, 12 Jan 2026 04:38:36 -0500 Subject: [PATCH 14/22] use `LongList` for the cps calculator --- .../meteorclient/systems/hud/elements/KeyboardHud.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java index 262f895cd8..e00ad54d73 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java @@ -5,6 +5,8 @@ package meteordevelopment.meteorclient.systems.hud.elements; +import it.unimi.dsi.fastutil.longs.LongArrayList; +import it.unimi.dsi.fastutil.longs.LongList; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import meteordevelopment.meteorclient.MeteorClient; import meteordevelopment.meteorclient.events.meteor.KeyEvent; @@ -649,7 +651,7 @@ public NbtCompound serialize() { } private static class RollingCps { - private final List clicks = new ArrayList<>(); + private final LongList clicks = new LongArrayList(); public void add() { clicks.add(System.currentTimeMillis()); From 06303b1276f3685a5c5505c9438d1d3acd5f0f8b Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Mon, 12 Jan 2026 04:39:16 -0500 Subject: [PATCH 15/22] default to without borders --- .../meteorclient/systems/hud/elements/KeyboardHud.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java index e00ad54d73..d25414b9ed 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java @@ -153,7 +153,7 @@ public enum Alignment { private final Setting border = sgBorder.add(new BoolSetting.Builder() .name("border") .description("Draw a border around keys.") - .defaultValue(true) + .defaultValue(false) .build() ); From 103770fdfa14ecb75c32114fef07ad5bc67be74b Mon Sep 17 00:00:00 2001 From: Crosby <32882447+crosby-moe@users.noreply.github.com> Date: Mon, 12 Jan 2026 06:05:00 -0500 Subject: [PATCH 16/22] solve issues --- .../meteorclient/systems/hud/elements/KeyboardHud.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java index d25414b9ed..716b1b4498 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java @@ -608,9 +608,9 @@ public boolean matches(int input, int scancode, boolean key) { } public void update(KeyAction action) { - if (action == KeyAction.Press) { + if (action != KeyAction.Release) { isPressed = true; - if (showCps) { + if (showCps && action == KeyAction.Press) { rollingCps.add(); } } else { From 2f056535e6faa542de100519b03b7f5ee83c3437 Mon Sep 17 00:00:00 2001 From: noramibuu <50046813+noramibu@users.noreply.github.com> Date: Mon, 12 Jan 2026 23:09:46 +0300 Subject: [PATCH 17/22] added keyboard layouts Co-authored-by: @Big-Iron-Cheems --- .../systems/hud/elements/KeyboardHud.java | 355 ++++++++++++++---- 1 file changed, 290 insertions(+), 65 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java index 716b1b4498..101878eab2 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java @@ -48,8 +48,7 @@ import static meteordevelopment.meteorclient.MeteorClient.mc; public class KeyboardHud extends HudElement { - public static final HudElementInfo INFO = new HudElementInfo<>(Hud.GROUP, "keyboard", - "Displays pressed keys.", KeyboardHud::new); + public static final HudElementInfo INFO = new HudElementInfo<>(Hud.GROUP, "keyboard", "Displays pressed keys.", KeyboardHud::new); public enum Alignment { Left, @@ -95,6 +94,15 @@ public enum Alignment { .build() ); + private final Setting keyboardLayout = sgGeneral.add(new EnumSetting.Builder() + .name("keyboard-layout") + .description("Physical keyboard layout (ANSI or ISO).") + .defaultValue(KeyboardLayout.ANSI) + .visible(() -> preset.get() == Preset.Keyboard) + .onChanged(layout -> onPresetChanged(preset.get())) + .build() + ); + private final Setting showCps = sgGeneral.add(new BoolSetting.Builder() .name("Show CPS") .description("Shows CPS on mouse buttons in Clicks preset.") @@ -309,72 +317,200 @@ private void onPresetChanged(Preset preset) { case Keyboard -> { double u = 35; // base key unit size double g = s; // gap between keys (spacing setting) - double row0 = 0, row1 = u + 15 + g, row2 = row1 + u + g, row3 = row2 + u + g, row4 = row3 + u + g, row5 = row4 + u + g; - - double targetLen = 14 * (u + g) + u * 0.6; - double padding; - - // Row 0: ESC, F1-F12, Print/Scroll/Pause - padding = targetLen - (u * 13 + g * 12); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_ESCAPE), 0, row0, u, u)); - for (int i = 0; i < 4; i++) keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F1 + i), (u + g) + padding / 2 + i * (u + g), row0, u, u)); - for (int i = 0; i < 4; i++) keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F5 + i), (u + g) * 5 + padding * 3 / 4 + i * (u + g), row0, u, u)); - for (int i = 0; i < 4; i++) keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F9 + i), (u + g) * 9 + padding + i * (u + g), row0, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PRINT_SCREEN), (u + g) * 15.5, row0, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_SCROLL_LOCK), (u + g) * 16.5, row0, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAUSE), (u + g) * 17.5, row0, u, u)); - - // Row 1: ` 1-0 - = BS, Ins/Home/PgUp - int[] row1Keys = {GLFW.GLFW_KEY_GRAVE_ACCENT, GLFW.GLFW_KEY_1, GLFW.GLFW_KEY_2, GLFW.GLFW_KEY_3, GLFW.GLFW_KEY_4, GLFW.GLFW_KEY_5, GLFW.GLFW_KEY_6, GLFW.GLFW_KEY_7, GLFW.GLFW_KEY_8, GLFW.GLFW_KEY_9, GLFW.GLFW_KEY_0, GLFW.GLFW_KEY_MINUS, GLFW.GLFW_KEY_EQUAL}; - for (int i = 0; i < row1Keys.length; i++) keys.add(new Key(Keybind.fromKey(row1Keys[i]), i * (u + g), row1, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_BACKSPACE), 13 * (u + g), row1, u * 1.6 + g, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_INSERT), (u + g) * 15.5, row1, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_HOME), (u + g) * 16.5, row1, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAGE_UP), (u + g) * 17.5, row1, u, u)); - - // Row 2: Tab QWERTY..., Del/End/PgDn - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_TAB), 0, row2, u * 1.5, u)); - int[] row2Keys = {GLFW.GLFW_KEY_Q, GLFW.GLFW_KEY_W, GLFW.GLFW_KEY_E, GLFW.GLFW_KEY_R, GLFW.GLFW_KEY_T, GLFW.GLFW_KEY_Y, GLFW.GLFW_KEY_U, GLFW.GLFW_KEY_I, GLFW.GLFW_KEY_O, GLFW.GLFW_KEY_P, GLFW.GLFW_KEY_LEFT_BRACKET, GLFW.GLFW_KEY_RIGHT_BRACKET}; - for (int i = 0; i < row2Keys.length; i++) keys.add(new Key(Keybind.fromKey(row2Keys[i]), u * 1.5 + g + i * (u + g), row2, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_BACKSLASH), u * 1.5 + g + 12 * (u + g), row2, u * 1.1 + g, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_DELETE), (u + g) * 15.5, row2, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_END), (u + g) * 16.5, row2, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAGE_DOWN), (u + g) * 17.5, row2, u, u)); - - // Row 3: Caps ASDF..., Enter - padding = targetLen - (u * 11 + g * 12); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_CAPS_LOCK), 0, row3, padding * (1.75 / 3.75), u)); - int[] row3Keys = {GLFW.GLFW_KEY_A, GLFW.GLFW_KEY_S, GLFW.GLFW_KEY_D, GLFW.GLFW_KEY_F, GLFW.GLFW_KEY_G, GLFW.GLFW_KEY_H, GLFW.GLFW_KEY_J, GLFW.GLFW_KEY_K, GLFW.GLFW_KEY_L, GLFW.GLFW_KEY_SEMICOLON, GLFW.GLFW_KEY_APOSTROPHE}; - for (int i = 0; i < row3Keys.length; i++) keys.add(new Key(Keybind.fromKey(row3Keys[i]), padding * (1.75 / 3.75) + g + i * (u + g), row3, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_ENTER), padding * (1.75 / 3.75) + g + 11 * (u + g), row3, padding * (2 / 3.75), u)); - - // Row 4: LShift ZXCV..., RShift, Up - padding = targetLen - (u * 10 + g * 11); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_SHIFT), 0, row4, padding * (2.1 / 4.8), u)); - int[] row4Keys = {GLFW.GLFW_KEY_Z, GLFW.GLFW_KEY_X, GLFW.GLFW_KEY_C, GLFW.GLFW_KEY_V, GLFW.GLFW_KEY_B, GLFW.GLFW_KEY_N, GLFW.GLFW_KEY_M, GLFW.GLFW_KEY_COMMA, GLFW.GLFW_KEY_PERIOD, GLFW.GLFW_KEY_SLASH}; - for (int i = 0; i < row4Keys.length; i++) keys.add(new Key(Keybind.fromKey(row4Keys[i]), padding * (2.1 / 4.8) + g + i * (u + g), row4, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_SHIFT), padding * (2.1 / 4.8) + g + 10 * (u + g), row4, padding * (2.7 / 4.8), u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_UP), (u + g) * 16.5, row4, u, u)); - - // Row 5: Ctrl/Win/Alt/Space/Alt/Win/Menu/Ctrl, Arrows - padding = targetLen - (g * 7); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_CONTROL), 0, row5, padding * (1.4 / 14.9), u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_SUPER), padding * (1.4 / 14.9) + g, row5, padding * (1.25 / 14.9), u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_ALT), padding * (2.65 / 14.9) + g * 2, row5, padding * (1.25 / 14.9), u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_SPACE), padding * (3.9 / 14.9) + g * 3, row5, padding * (5.6 / 14.9), u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_ALT), padding * (9.5 / 14.9) + g * 4, row5, padding * (1.25 / 14.9), u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_SUPER), padding * (10.75 / 14.9) + g * 5, row5, padding * (1.25 / 14.9), u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_MENU), padding * (12 / 14.9) + g * 6, row5, padding * (1.4 / 14.9), u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_CONTROL), padding * (13.4 / 14.9) + g * 7, row5, padding * (1.5 / 14.9), u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT), (u + g) * 15.5, row5, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_DOWN), (u + g) * 16.5, row5, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT), (u + g) * 17.5, row5, u, u)); + + if (keyboardLayout.get() == KeyboardLayout.ANSI) { + buildAnsiLayout(u, g); + } else { + buildIsoLayout(u, g); + } } case Custom -> keys.addAll(customKeys.get()); } calculateSize(); } + private void buildAnsiLayout(double u, double g) { + double row0 = 0, row1 = u + 15 + g, row2 = row1 + u + g, row3 = row2 + u + g, row4 = row3 + u + g, row5 = row4 + u + g; + double targetLen = 14 * (u + g) + u * 0.6; + double padding; + + // Row 0: ESC, F1-F12, Print/Scroll/Pause + padding = targetLen - (u * 13 + g * 12); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_ESCAPE), 0, row0, u, u)); + for (int i = 0; i < 4; i++) + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F1 + i), (u + g) + padding / 2 + i * (u + g), row0, u, u)); + for (int i = 0; i < 4; i++) + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F5 + i), (u + g) * 5 + padding * 3 / 4 + i * (u + g), row0, u, u)); + for (int i = 0; i < 4; i++) + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F9 + i), (u + g) * 9 + padding + i * (u + g), row0, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PRINT_SCREEN), (u + g) * 15.5, row0, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_SCROLL_LOCK), (u + g) * 16.5, row0, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAUSE), (u + g) * 17.5, row0, u, u)); + + // Row 1: ` 1-0 - = BS, Ins/Home/PgUp + int[] row1Keys = {GLFW.GLFW_KEY_GRAVE_ACCENT, GLFW.GLFW_KEY_1, GLFW.GLFW_KEY_2, GLFW.GLFW_KEY_3, GLFW.GLFW_KEY_4, GLFW.GLFW_KEY_5, GLFW.GLFW_KEY_6, GLFW.GLFW_KEY_7, GLFW.GLFW_KEY_8, GLFW.GLFW_KEY_9, GLFW.GLFW_KEY_0, GLFW.GLFW_KEY_MINUS, GLFW.GLFW_KEY_EQUAL}; + for (int i = 0; i < row1Keys.length; i++) + keys.add(new Key(Keybind.fromKey(row1Keys[i]), i * (u + g), row1, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_BACKSPACE), 13 * (u + g), row1, u * 1.6 + g, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_INSERT), (u + g) * 15.5, row1, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_HOME), (u + g) * 16.5, row1, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAGE_UP), (u + g) * 17.5, row1, u, u)); + + // Row 2: Tab QWERTY..., Del/End/PgDn + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_TAB), 0, row2, u * 1.5, u)); + int[] row2Keys = {GLFW.GLFW_KEY_Q, GLFW.GLFW_KEY_W, GLFW.GLFW_KEY_E, GLFW.GLFW_KEY_R, GLFW.GLFW_KEY_T, GLFW.GLFW_KEY_Y, GLFW.GLFW_KEY_U, GLFW.GLFW_KEY_I, GLFW.GLFW_KEY_O, GLFW.GLFW_KEY_P, GLFW.GLFW_KEY_LEFT_BRACKET, GLFW.GLFW_KEY_RIGHT_BRACKET}; + for (int i = 0; i < row2Keys.length; i++) + keys.add(new Key(Keybind.fromKey(row2Keys[i]), u * 1.5 + g + i * (u + g), row2, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_BACKSLASH), u * 1.5 + g + 12 * (u + g), row2, u * 1.1 + g, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_DELETE), (u + g) * 15.5, row2, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_END), (u + g) * 16.5, row2, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAGE_DOWN), (u + g) * 17.5, row2, u, u)); + + // Row 3: Caps ASDF..., Enter + padding = targetLen - (u * 11 + g * 12); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_CAPS_LOCK), 0, row3, padding * (1.75 / 3.75), u)); + int[] row3Keys = {GLFW.GLFW_KEY_A, GLFW.GLFW_KEY_S, GLFW.GLFW_KEY_D, GLFW.GLFW_KEY_F, GLFW.GLFW_KEY_G, GLFW.GLFW_KEY_H, GLFW.GLFW_KEY_J, GLFW.GLFW_KEY_K, GLFW.GLFW_KEY_L, GLFW.GLFW_KEY_SEMICOLON, GLFW.GLFW_KEY_APOSTROPHE}; + for (int i = 0; i < row3Keys.length; i++) + keys.add(new Key(Keybind.fromKey(row3Keys[i]), padding * (1.75 / 3.75) + g + i * (u + g), row3, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_ENTER), padding * (1.75 / 3.75) + g + 11 * (u + g), row3, padding * (2 / 3.75), u)); + + // Row 4: LShift ZXCV..., RShift, Up + padding = targetLen - (u * 10 + g * 11); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_SHIFT), 0, row4, padding * (2.1 / 4.8), u)); + int[] row4Keys = {GLFW.GLFW_KEY_Z, GLFW.GLFW_KEY_X, GLFW.GLFW_KEY_C, GLFW.GLFW_KEY_V, GLFW.GLFW_KEY_B, GLFW.GLFW_KEY_N, GLFW.GLFW_KEY_M, GLFW.GLFW_KEY_COMMA, GLFW.GLFW_KEY_PERIOD, GLFW.GLFW_KEY_SLASH}; + for (int i = 0; i < row4Keys.length; i++) + keys.add(new Key(Keybind.fromKey(row4Keys[i]), padding * (2.1 / 4.8) + g + i * (u + g), row4, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_SHIFT), padding * (2.1 / 4.8) + g + 10 * (u + g), row4, padding * (2.7 / 4.8), u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_UP), (u + g) * 16.5, row4, u, u)); + + // Row 5: Ctrl/Win/Alt/Space/Alt/Win/Menu/Ctrl, Arrows + padding = targetLen - (g * 7); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_CONTROL), 0, row5, padding * (1.4 / 14.9), u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_SUPER), padding * (1.4 / 14.9) + g, row5, padding * (1.25 / 14.9), u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_ALT), padding * (2.65 / 14.9) + g * 2, row5, padding * (1.25 / 14.9), u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_SPACE), padding * (3.9 / 14.9) + g * 3, row5, padding * (5.6 / 14.9), u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_ALT), padding * (9.5 / 14.9) + g * 4, row5, padding * (1.25 / 14.9), u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_SUPER), padding * (10.75 / 14.9) + g * 5, row5, padding * (1.25 / 14.9), u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_MENU), padding * (12 / 14.9) + g * 6, row5, padding * (1.4 / 14.9), u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_CONTROL), padding * (13.4 / 14.9) + g * 7, row5, padding * (1.5 / 14.9), u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT), (u + g) * 15.5, row5, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_DOWN), (u + g) * 16.5, row5, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT), (u + g) * 17.5, row5, u, u)); + } + + private void buildIsoLayout(double u, double g) { + double row0 = 0, row1 = u + 15 + g, row2 = row1 + u + g, row3 = row2 + u + g, row4 = row3 + u + g, row5 = row4 + u + g; + double targetLen = 14 * (u + g) + u * 0.6; + double padding; + + // Row 0: ESC, F1-F12, Print/Scroll/Pause + padding = targetLen - (u * 13 + g * 12); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_ESCAPE), 0, row0, u, u)); + for (int i = 0; i < 4; i++) + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F1 + i), (u + g) + padding / 2 + i * (u + g), row0, u, u)); + for (int i = 0; i < 4; i++) + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F5 + i), (u + g) * 5 + padding * 3 / 4 + i * (u + g), row0, u, u)); + for (int i = 0; i < 4; i++) + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F9 + i), (u + g) * 9 + padding + i * (u + g), row0, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PRINT_SCREEN), (u + g) * 15.5, row0, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_SCROLL_LOCK), (u + g) * 16.5, row0, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAUSE), (u + g) * 17.5, row0, u, u)); + + // Row 1: ` 1-0 - = BS, Ins/Home/PgUp + int[] row1Keys = {GLFW.GLFW_KEY_GRAVE_ACCENT, GLFW.GLFW_KEY_1, GLFW.GLFW_KEY_2, GLFW.GLFW_KEY_3, GLFW.GLFW_KEY_4, GLFW.GLFW_KEY_5, GLFW.GLFW_KEY_6, GLFW.GLFW_KEY_7, GLFW.GLFW_KEY_8, GLFW.GLFW_KEY_9, GLFW.GLFW_KEY_0, GLFW.GLFW_KEY_MINUS, GLFW.GLFW_KEY_EQUAL}; + for (int i = 0; i < row1Keys.length; i++) + keys.add(new Key(Keybind.fromKey(row1Keys[i]), i * (u + g), row1, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_BACKSPACE), 13 * (u + g), row1, u * 1.6 + g, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_INSERT), (u + g) * 15.5, row1, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_HOME), (u + g) * 16.5, row1, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAGE_UP), (u + g) * 17.5, row1, u, u)); + + // Row 2: Tab QWERTY... brackets + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_TAB), 0, row2, u * 1.5, u)); + int[] row2Keys = {GLFW.GLFW_KEY_Q, GLFW.GLFW_KEY_W, GLFW.GLFW_KEY_E, GLFW.GLFW_KEY_R, GLFW.GLFW_KEY_T, GLFW.GLFW_KEY_Y, GLFW.GLFW_KEY_U, GLFW.GLFW_KEY_I, GLFW.GLFW_KEY_O, GLFW.GLFW_KEY_P, GLFW.GLFW_KEY_LEFT_BRACKET, GLFW.GLFW_KEY_RIGHT_BRACKET}; + for (int i = 0; i < row2Keys.length; i++) + keys.add(new Key(Keybind.fromKey(row2Keys[i]), u * 1.5 + g + i * (u + g), row2, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_DELETE), (u + g) * 15.5, row2, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_END), (u + g) * 16.5, row2, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAGE_DOWN), (u + g) * 17.5, row2, u, u)); + + // Row 3: Caps ASDF..., UK # key + padding = targetLen - (u * 11 + g * 12); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_CAPS_LOCK), 0, row3, padding * (1.75 / 3.75), u)); + int[] row3Keys = {GLFW.GLFW_KEY_A, GLFW.GLFW_KEY_S, GLFW.GLFW_KEY_D, GLFW.GLFW_KEY_F, GLFW.GLFW_KEY_G, GLFW.GLFW_KEY_H, GLFW.GLFW_KEY_J, GLFW.GLFW_KEY_K, GLFW.GLFW_KEY_L, GLFW.GLFW_KEY_SEMICOLON, GLFW.GLFW_KEY_APOSTROPHE}; + for (int i = 0; i < row3Keys.length; i++) + keys.add(new Key(Keybind.fromKey(row3Keys[i]), padding * (1.75 / 3.75) + g + i * (u + g), row3, u, u)); + // ISO key (backslash on UK) + double backslashKeyX = padding * (1.75 / 3.75) + g + 11 * (u + g); // After ' key + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_BACKSLASH), backslashKeyX, row3, u, u)); + + // ISO Enter - reverse-L shape spanning rows 2-3 + // Top bar starts after ] bracket on row 2 and extends to targetLen + double topBarStartX = u * 1.5 + g + 12 * (u + g); + + // Stem should be right-aligned: it ends at targetLen + double enterStemWidth = u * 1.25; + double enterStemX = targetLen - enterStemWidth; // Right-aligned + + // Verify there's proper gap between ISO key and Enter stem + double gapBetweenIsoAndEnter = enterStemX - (backslashKeyX + u); + // If gap is too small, adjust stem to start after ISO key with minimum gap + if (gapBetweenIsoAndEnter < g * 0.5) { + enterStemX = backslashKeyX + u + g; + enterStemWidth = targetLen - enterStemX; + } + + IsoEnterKey isoEnter = new IsoEnterKey( + Keybind.fromKey(GLFW.GLFW_KEY_ENTER), + enterStemX, row2, // Start at row 2 + enterStemWidth, u * 2 + g, // Height spans 2 rows + gap + topBarStartX // Top bar starts after ] bracket + ); + keys.add(isoEnter); + + // Row 4: LShift, ISO \| key, ZXCV..., RShift, Up + // ISO: Only Left Shift is shorter (to accommodate extra key), Right Shift stays same size as ANSI + + // Right Shift: Use ANSI's padding calculation to get EXACT same width as ANSI + double ansiPadding = targetLen - (u * 10 + g * 11); // Same as ANSI + double rShiftWidth = ansiPadding * (2.7 / 4.8); // Exact same calculation as ANSI + + // Left Shift: Calculate to fill remaining space (gets shorter due to extra ISO key) + // Total space = targetLen - (ISO key + 10 letters + RShift + 12 gaps) + double lShiftWidth = targetLen - (u + g + 10 * (u + g) + g + rShiftWidth); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_SHIFT), 0, row4, lShiftWidth, u)); + + // ISO \| key next to Left Shift + double isoKeyX = lShiftWidth + g; + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_WORLD_2), isoKeyX, row4, u, u)); + + // Letter keys Z through / + int[] row4Keys = {GLFW.GLFW_KEY_Z, GLFW.GLFW_KEY_X, GLFW.GLFW_KEY_C, GLFW.GLFW_KEY_V, GLFW.GLFW_KEY_B, GLFW.GLFW_KEY_N, GLFW.GLFW_KEY_M, GLFW.GLFW_KEY_COMMA, GLFW.GLFW_KEY_PERIOD, GLFW.GLFW_KEY_SLASH}; + for (int i = 0; i < row4Keys.length; i++) + keys.add(new Key(Keybind.fromKey(row4Keys[i]), isoKeyX + u + g + i * (u + g), row4, u, u)); + + // Right Shift: Same size as ANSI + double rShiftX = isoKeyX + u + g + 10 * (u + g); // After last letter key + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_SHIFT), rShiftX, row4, rShiftWidth, u)); + + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_UP), (u + g) * 16.5, row4, u, u)); + + // Row 5: Ctrl/Win/Alt/Space/AltGr/Win/Menu/Ctrl, Arrows + padding = targetLen - (g * 7); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_CONTROL), 0, row5, padding * (1.4 / 14.9), u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_SUPER), padding * (1.4 / 14.9) + g, row5, padding * (1.25 / 14.9), u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_ALT), padding * (2.65 / 14.9) + g * 2, row5, padding * (1.25 / 14.9), u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_SPACE), padding * (3.9 / 14.9) + g * 3, row5, padding * (5.6 / 14.9), u)); + // AltGr (Right Alt) on ISO keyboards + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_ALT), "AltGr", padding * (9.5 / 14.9) + g * 4, row5, padding * (1.25 / 14.9), u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_SUPER), padding * (10.75 / 14.9) + g * 5, row5, padding * (1.25 / 14.9), u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_MENU), padding * (12 / 14.9) + g * 6, row5, padding * (1.4 / 14.9), u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_CONTROL), padding * (13.4 / 14.9) + g * 7, row5, padding * (1.5 / 14.9), u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT), (u + g) * 15.5, row5, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_DOWN), (u + g) * 16.5, row5, u, u)); + keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT), (u + g) * 17.5, row5, u, u)); + } + private void calculateSize() { if (keys.isEmpty()) { setSize(0, 0); @@ -421,6 +557,8 @@ private static String getShortName(String name) { case "ARROW DOWN", "DOWN" -> "Dn"; case "ARROW LEFT", "LEFT" -> "Lt"; case "ARROW RIGHT", "RIGHT" -> "Rt"; + case "WORLD 1" -> "#"; + case "WORLD 2" -> "\\"; case "UNKNOWN" -> "?"; default -> name; }; @@ -458,6 +596,11 @@ public void render(HudRenderer renderer) { } } + if (key instanceof IsoEnterKey isoEnter) { + isoEnter.render(this, renderer, s, mutableColor); + continue; + } + Color color = getKeyColor(key, mutableColor); double kX = x + (key.x - minX) * s; @@ -512,8 +655,7 @@ public void render(HudRenderer renderer) { } } - private void drawTextLine(HudRenderer renderer, String text, double textWidth, double x, double y, double w, double textScale, - Color color) { + private void drawTextLine(HudRenderer renderer, String text, double textWidth, double x, double y, double w, double textScale, Color color) { double s = scale.get(); double padding = 2 * s; @@ -535,6 +677,11 @@ public enum Preset { Custom } + public enum KeyboardLayout { + ANSI, + ISO + } + public static class Key { public String name = ""; public KeyBinding binding; @@ -583,6 +730,15 @@ public Key(Keybind keybind, double x, double y, double width, double height) { this.height = height; } + public Key(Keybind keybind, String name, double x, double y, double width, double height) { + this.keybind = keybind; + this.name = name; + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + public Key setShowCps(boolean show) { this.showCps = show; return this; @@ -596,7 +752,7 @@ public String getName() { } public boolean matches(int input, int scancode, boolean key) { - if (keybind != null ) { + if (keybind != null) { return keybind.isKey() == key && keybind.getValue() == input; } else { InputUtil.Key inputKey = ((KeyBindingAccessor) binding).meteor$getKey(); @@ -650,6 +806,75 @@ public NbtCompound serialize() { } } + public static class IsoEnterKey extends Key { + private final double topBarStartX; + + public IsoEnterKey(Keybind keybind, double x, double y, double width, double height, double topBarStartX) { + super(keybind, x, y, width, height); + this.topBarStartX = topBarStartX; + } + + // Renders L-shaped Enter key as 3 NON-overlapping quads to avoid transparency artifacts + public void render(KeyboardHud hud, HudRenderer renderer, double s, SettingColor mutableColor) { + double kX = hud.x + (x - hud.minX) * s; + double kY = hud.y + (y - hud.minY) * s; + double kW = width * s; + double kH = height * s; + double u = 35 * s; + + Color color = hud.getKeyColor(this, mutableColor); + + // Calculate positions + double stemRight = kX + kW; + double topBarX = hud.x + (topBarStartX - hud.minX) * s; + double topBarLeftWidth = stemRight - topBarX - kW; + if (topBarLeftWidth > 0) { + renderer.quad(topBarX, kY, topBarLeftWidth, u, color); + } + renderer.quad(kX, kY, kW, u, color); + renderer.quad(kX, kY + u, kW, kH - u, color); + + + // Border + if (hud.border.get()) { + Color bColor = hud.getColor(hud.borderColor.get(), mutableColor); + double bw = hud.borderWidth.get(); + double fullTopBarWidth = topBarLeftWidth + kW; // Total width of top bar + + // Top bar borders + renderer.quad(topBarX, kY, fullTopBarWidth, bw, bColor); // Top edge + renderer.quad(topBarX, kY, bw, u, bColor); // Left edge + renderer.quad(topBarX + fullTopBarWidth - bw, kY, bw, kH, bColor); // Right edge (full height) + + // Stem borders + renderer.quad(kX, kY + kH - bw, kW, bw, bColor); // Bottom edge + renderer.quad(kX, kY + u, bw, kH - u, bColor); // Left edge (from u downward) + + // Bottom of top bar (connecting piece between top bar and stem) + if (topBarLeftWidth > 0) { + renderer.quad(topBarX, kY + u - bw, topBarLeftWidth, bw, bColor); + } + } + + // Text centered in stem + String text = getName(); + Color txtColor = hud.getColor(hud.textColor.get(), mutableColor); + + double padding = 2 * s; + double availableWidth = kW - padding * 2; + double availableHeight = kH - padding * 2; + double tH = renderer.textHeight(); + double tW = renderer.textWidth(text); + + double widthScale = tW > availableWidth ? availableWidth / tW : 1.0; + double heightScale = tH > availableHeight * 0.6 ? (availableHeight * 0.6) / tH : 1.0; + double textScale = Math.min(widthScale, heightScale); + + double yText = kY + (kH - tH * textScale) / 2; + hud.drawTextLine(renderer, text, tW, kX, yText, kW, textScale, txtColor); + } + } + private static class RollingCps { private final LongList clicks = new LongArrayList(); From 2b1c1e8746964178f22ef25d0b4b7f5dbe797205 Mon Sep 17 00:00:00 2001 From: Big-Iron-Cheems <52252627+Big-Iron-Cheems@users.noreply.github.com> Date: Tue, 13 Jan 2026 19:11:00 +0100 Subject: [PATCH 18/22] More improvements - Define KeyDimensions for standard sizes - Add LayoutContext to simplify key positioning and sizing --- .../gui/DefaultSettingsWidgetFactory.java | 4 +- .../meteorclient/systems/hud/Hud.java | 1 + .../hud/elements/keyboard/KeyDimensions.java | 53 ++++ .../elements/{ => keyboard}/KeyboardHud.java | 257 ++++++++---------- .../hud/elements/keyboard/LayoutContext.java | 83 ++++++ 5 files changed, 258 insertions(+), 140 deletions(-) create mode 100644 src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/KeyDimensions.java rename src/main/java/meteordevelopment/meteorclient/systems/hud/elements/{ => keyboard}/KeyboardHud.java (76%) create mode 100644 src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/LayoutContext.java diff --git a/src/main/java/meteordevelopment/meteorclient/gui/DefaultSettingsWidgetFactory.java b/src/main/java/meteordevelopment/meteorclient/gui/DefaultSettingsWidgetFactory.java index 53cf90ff64..0e10738e11 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/DefaultSettingsWidgetFactory.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/DefaultSettingsWidgetFactory.java @@ -22,7 +22,7 @@ import meteordevelopment.meteorclient.gui.widgets.pressable.WPlus; import meteordevelopment.meteorclient.renderer.Fonts; import meteordevelopment.meteorclient.settings.*; -import meteordevelopment.meteorclient.systems.hud.elements.KeyboardHud; +import meteordevelopment.meteorclient.systems.hud.elements.keyboard.KeyboardHud; import meteordevelopment.meteorclient.utils.Utils; import meteordevelopment.meteorclient.utils.render.color.SettingColor; import net.minecraft.client.resource.language.I18n; @@ -72,7 +72,7 @@ public DefaultSettingsWidgetFactory(GuiTheme theme) { factories.put(ColorListSetting.class, (table, setting) -> colorListW(table, (ColorListSetting) setting)); factories.put(FontFaceSetting.class, (table, setting) -> fontW(table, (FontFaceSetting) setting)); factories.put(Vector3dSetting.class, (table, setting) -> vector3dW(table, (Vector3dSetting) setting)); - factories.put(KeyboardHud.CustomKeyListSetting.class, (table,setting) -> customKeyListW(table, (KeyboardHud.CustomKeyListSetting) setting)); + factories.put(KeyboardHud.CustomKeyListSetting.class, (table, setting) -> customKeyListW(table, (KeyboardHud.CustomKeyListSetting) setting)); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/Hud.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/Hud.java index 0217f9eb98..7fe9131e32 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/hud/Hud.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/Hud.java @@ -13,6 +13,7 @@ import meteordevelopment.meteorclient.systems.System; import meteordevelopment.meteorclient.systems.Systems; import meteordevelopment.meteorclient.systems.hud.elements.*; +import meteordevelopment.meteorclient.systems.hud.elements.keyboard.KeyboardHud; import meteordevelopment.meteorclient.systems.hud.screens.HudEditorScreen; import meteordevelopment.meteorclient.utils.Utils; import meteordevelopment.meteorclient.utils.misc.Keybind; diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/KeyDimensions.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/KeyDimensions.java new file mode 100644 index 0000000000..dea2fddd08 --- /dev/null +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/KeyDimensions.java @@ -0,0 +1,53 @@ +/* + * This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client). + * Copyright (c) Meteor Development. + */ + +package meteordevelopment.meteorclient.systems.hud.elements.keyboard; + +enum KeyDimensions { + // Standard sizes + UNIT_1U(1.0), + UNIT_1_25U(1.25), + UNIT_1_5U(1.5), + UNIT_1_75U(1.75), + UNIT_2U(2.0), + UNIT_2_25U(2.25), + UNIT_2_75U(2.75), + UNIT_6_25U(6.25); + + public final double units; + + KeyDimensions(double units) { + this.units = units; + } + + /** + * Converts this dimension to pixels, accounting for gaps. + * Multi-unit keys span gaps: 2u key = 2*baseUnit + 1*gap + */ + public double toPixels(double baseUnit, double gap) { + return units * baseUnit + (units - 1.0) * gap; + } + + public double toPixels(double baseUnit) { + return units * baseUnit; + } + + // Aliases for common keys + public static final KeyDimensions STANDARD = UNIT_1U; + public static final KeyDimensions TAB = UNIT_1_5U; + public static final KeyDimensions CAPS_LOCK = UNIT_1_75U; + public static final KeyDimensions ENTER_ANSI = UNIT_2_25U; + public static final KeyDimensions LEFT_SHIFT_ANSI = UNIT_2_25U; + public static final KeyDimensions RIGHT_SHIFT = UNIT_2_75U; + public static final KeyDimensions BACKSPACE = UNIT_2U; + public static final KeyDimensions LEFT_SHIFT_ISO = UNIT_1_25U; + public static final KeyDimensions ENTER_ISO_WIDTH = UNIT_1_25U; + public static final KeyDimensions ENTER_ISO_HEIGHT = UNIT_2U; + public static final KeyDimensions CTRL = UNIT_1_25U; + public static final KeyDimensions ALT = UNIT_1_25U; + public static final KeyDimensions GUI = UNIT_1_25U; + public static final KeyDimensions MENU = UNIT_1_25U; + public static final KeyDimensions SPACEBAR = UNIT_6_25U; +} diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/KeyboardHud.java similarity index 76% rename from src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java rename to src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/KeyboardHud.java index 101878eab2..68ee0d255b 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/KeyboardHud.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/KeyboardHud.java @@ -3,7 +3,7 @@ * Copyright (c) Meteor Development. */ -package meteordevelopment.meteorclient.systems.hud.elements; +package meteordevelopment.meteorclient.systems.hud.elements.keyboard; import it.unimi.dsi.fastutil.longs.LongArrayList; import it.unimi.dsi.fastutil.longs.LongList; @@ -317,11 +317,12 @@ private void onPresetChanged(Preset preset) { case Keyboard -> { double u = 35; // base key unit size double g = s; // gap between keys (spacing setting) + LayoutContext lCtx = new LayoutContext(u, g, 15); if (keyboardLayout.get() == KeyboardLayout.ANSI) { - buildAnsiLayout(u, g); + buildAnsiLayout(lCtx); } else { - buildIsoLayout(u, g); + buildIsoLayout(lCtx); } } case Custom -> keys.addAll(customKeys.get()); @@ -329,186 +330,163 @@ private void onPresetChanged(Preset preset) { calculateSize(); } - private void buildAnsiLayout(double u, double g) { - double row0 = 0, row1 = u + 15 + g, row2 = row1 + u + g, row3 = row2 + u + g, row4 = row3 + u + g, row5 = row4 + u + g; - double targetLen = 14 * (u + g) + u * 0.6; - double padding; + private void buildAnsiLayout(LayoutContext l) { + double row0 = l.uy(0), row1 = l.uy(1), row2 = l.uy(2), row3 = l.uy(3), row4 = l.uy(4), row5 = l.uy(5); // Row 0: ESC, F1-F12, Print/Scroll/Pause - padding = targetLen - (u * 13 + g * 12); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_ESCAPE), 0, row0, u, u)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_ESCAPE), 0, row0)); for (int i = 0; i < 4; i++) - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F1 + i), (u + g) + padding / 2 + i * (u + g), row0, u, u)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_F1 + i), l.ux(2d + i), row0)); for (int i = 0; i < 4; i++) - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F5 + i), (u + g) * 5 + padding * 3 / 4 + i * (u + g), row0, u, u)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_F5 + i), l.ux(6.5 + i), row0)); for (int i = 0; i < 4; i++) - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F9 + i), (u + g) * 9 + padding + i * (u + g), row0, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PRINT_SCREEN), (u + g) * 15.5, row0, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_SCROLL_LOCK), (u + g) * 16.5, row0, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAUSE), (u + g) * 17.5, row0, u, u)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_F9 + i), l.ux(11d + i), row0)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_PRINT_SCREEN), l.ux(15.5), row0)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_SCROLL_LOCK), l.ux(16.5), row0)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_PAUSE), l.ux(17.5), row0)); // Row 1: ` 1-0 - = BS, Ins/Home/PgUp int[] row1Keys = {GLFW.GLFW_KEY_GRAVE_ACCENT, GLFW.GLFW_KEY_1, GLFW.GLFW_KEY_2, GLFW.GLFW_KEY_3, GLFW.GLFW_KEY_4, GLFW.GLFW_KEY_5, GLFW.GLFW_KEY_6, GLFW.GLFW_KEY_7, GLFW.GLFW_KEY_8, GLFW.GLFW_KEY_9, GLFW.GLFW_KEY_0, GLFW.GLFW_KEY_MINUS, GLFW.GLFW_KEY_EQUAL}; for (int i = 0; i < row1Keys.length; i++) - keys.add(new Key(Keybind.fromKey(row1Keys[i]), i * (u + g), row1, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_BACKSPACE), 13 * (u + g), row1, u * 1.6 + g, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_INSERT), (u + g) * 15.5, row1, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_HOME), (u + g) * 16.5, row1, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAGE_UP), (u + g) * 17.5, row1, u, u)); + keys.add(l.key(Keybind.fromKey(row1Keys[i]), l.ux(i), row1)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_BACKSPACE), l.ux(13), row1, KeyDimensions.BACKSPACE)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_INSERT), l.ux(15.5), row1)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_HOME), l.ux(16.5), row1)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_PAGE_UP), l.ux(17.5), row1)); // Row 2: Tab QWERTY..., Del/End/PgDn - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_TAB), 0, row2, u * 1.5, u)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_TAB), 0, row2, KeyDimensions.TAB)); int[] row2Keys = {GLFW.GLFW_KEY_Q, GLFW.GLFW_KEY_W, GLFW.GLFW_KEY_E, GLFW.GLFW_KEY_R, GLFW.GLFW_KEY_T, GLFW.GLFW_KEY_Y, GLFW.GLFW_KEY_U, GLFW.GLFW_KEY_I, GLFW.GLFW_KEY_O, GLFW.GLFW_KEY_P, GLFW.GLFW_KEY_LEFT_BRACKET, GLFW.GLFW_KEY_RIGHT_BRACKET}; + double tabEnd = l.px(KeyDimensions.TAB) + l.keyGap; for (int i = 0; i < row2Keys.length; i++) - keys.add(new Key(Keybind.fromKey(row2Keys[i]), u * 1.5 + g + i * (u + g), row2, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_BACKSLASH), u * 1.5 + g + 12 * (u + g), row2, u * 1.1 + g, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_DELETE), (u + g) * 15.5, row2, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_END), (u + g) * 16.5, row2, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAGE_DOWN), (u + g) * 17.5, row2, u, u)); + keys.add(l.key(Keybind.fromKey(row2Keys[i]), tabEnd + l.ux(i), row2)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_BACKSLASH), tabEnd + l.ux(12), row2, KeyDimensions.TAB)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_DELETE), l.ux(15.5), row2)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_END), l.ux(16.5), row2)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_PAGE_DOWN), l.ux(17.5), row2)); // Row 3: Caps ASDF..., Enter - padding = targetLen - (u * 11 + g * 12); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_CAPS_LOCK), 0, row3, padding * (1.75 / 3.75), u)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_CAPS_LOCK), 0, row3, KeyDimensions.CAPS_LOCK)); int[] row3Keys = {GLFW.GLFW_KEY_A, GLFW.GLFW_KEY_S, GLFW.GLFW_KEY_D, GLFW.GLFW_KEY_F, GLFW.GLFW_KEY_G, GLFW.GLFW_KEY_H, GLFW.GLFW_KEY_J, GLFW.GLFW_KEY_K, GLFW.GLFW_KEY_L, GLFW.GLFW_KEY_SEMICOLON, GLFW.GLFW_KEY_APOSTROPHE}; + double capsEnd = l.px(KeyDimensions.CAPS_LOCK) + l.keyGap; for (int i = 0; i < row3Keys.length; i++) - keys.add(new Key(Keybind.fromKey(row3Keys[i]), padding * (1.75 / 3.75) + g + i * (u + g), row3, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_ENTER), padding * (1.75 / 3.75) + g + 11 * (u + g), row3, padding * (2 / 3.75), u)); + keys.add(l.key(Keybind.fromKey(row3Keys[i]), capsEnd + l.ux(i), row3)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_ENTER), capsEnd + l.ux(11), row3, KeyDimensions.ENTER_ANSI)); // Row 4: LShift ZXCV..., RShift, Up - padding = targetLen - (u * 10 + g * 11); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_SHIFT), 0, row4, padding * (2.1 / 4.8), u)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_SHIFT), 0, row4, KeyDimensions.LEFT_SHIFT_ANSI)); int[] row4Keys = {GLFW.GLFW_KEY_Z, GLFW.GLFW_KEY_X, GLFW.GLFW_KEY_C, GLFW.GLFW_KEY_V, GLFW.GLFW_KEY_B, GLFW.GLFW_KEY_N, GLFW.GLFW_KEY_M, GLFW.GLFW_KEY_COMMA, GLFW.GLFW_KEY_PERIOD, GLFW.GLFW_KEY_SLASH}; + double lShiftEnd = l.px(KeyDimensions.LEFT_SHIFT_ANSI) + l.keyGap; for (int i = 0; i < row4Keys.length; i++) - keys.add(new Key(Keybind.fromKey(row4Keys[i]), padding * (2.1 / 4.8) + g + i * (u + g), row4, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_SHIFT), padding * (2.1 / 4.8) + g + 10 * (u + g), row4, padding * (2.7 / 4.8), u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_UP), (u + g) * 16.5, row4, u, u)); + keys.add(l.key(Keybind.fromKey(row4Keys[i]), lShiftEnd + l.ux(i), row4)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_SHIFT), lShiftEnd + l.ux(10), row4, KeyDimensions.RIGHT_SHIFT)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_UP), l.ux(16.5), row4)); // Row 5: Ctrl/Win/Alt/Space/Alt/Win/Menu/Ctrl, Arrows - padding = targetLen - (g * 7); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_CONTROL), 0, row5, padding * (1.4 / 14.9), u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_SUPER), padding * (1.4 / 14.9) + g, row5, padding * (1.25 / 14.9), u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_ALT), padding * (2.65 / 14.9) + g * 2, row5, padding * (1.25 / 14.9), u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_SPACE), padding * (3.9 / 14.9) + g * 3, row5, padding * (5.6 / 14.9), u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_ALT), padding * (9.5 / 14.9) + g * 4, row5, padding * (1.25 / 14.9), u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_SUPER), padding * (10.75 / 14.9) + g * 5, row5, padding * (1.25 / 14.9), u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_MENU), padding * (12 / 14.9) + g * 6, row5, padding * (1.4 / 14.9), u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_CONTROL), padding * (13.4 / 14.9) + g * 7, row5, padding * (1.5 / 14.9), u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT), (u + g) * 15.5, row5, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_DOWN), (u + g) * 16.5, row5, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT), (u + g) * 17.5, row5, u, u)); + double xPos = 0; + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_CONTROL), xPos, row5, KeyDimensions.CTRL)); + xPos += l.px(KeyDimensions.CTRL) + l.keyGap; + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_SUPER), xPos, row5, KeyDimensions.GUI)); + xPos += l.px(KeyDimensions.GUI) + l.keyGap; + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_ALT), xPos, row5, KeyDimensions.ALT)); + xPos += l.px(KeyDimensions.ALT) + l.keyGap; + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_SPACE), xPos, row5, KeyDimensions.SPACEBAR)); + xPos += l.px(KeyDimensions.SPACEBAR) + l.keyGap; + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_ALT), xPos, row5, KeyDimensions.ALT)); + xPos += l.px(KeyDimensions.ALT) + l.keyGap; + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_SUPER), xPos, row5, KeyDimensions.GUI)); + xPos += l.px(KeyDimensions.GUI) + l.keyGap; + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_MENU), xPos, row5, KeyDimensions.MENU)); + xPos += l.px(KeyDimensions.MENU) + l.keyGap; + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_CONTROL), xPos, row5, KeyDimensions.CTRL)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT), l.ux(15.5), row5)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_DOWN), l.ux(16.5), row5)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT), l.ux(17.5), row5)); } - private void buildIsoLayout(double u, double g) { - double row0 = 0, row1 = u + 15 + g, row2 = row1 + u + g, row3 = row2 + u + g, row4 = row3 + u + g, row5 = row4 + u + g; - double targetLen = 14 * (u + g) + u * 0.6; - double padding; + private void buildIsoLayout(LayoutContext l) { + double row0 = l.uy(0), row1 = l.uy(1), row2 = l.uy(2), row3 = l.uy(3), row4 = l.uy(4), row5 = l.uy(5); // Row 0: ESC, F1-F12, Print/Scroll/Pause - padding = targetLen - (u * 13 + g * 12); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_ESCAPE), 0, row0, u, u)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_ESCAPE), 0, row0)); for (int i = 0; i < 4; i++) - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F1 + i), (u + g) + padding / 2 + i * (u + g), row0, u, u)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_F1 + i), l.ux(2d + i), row0)); for (int i = 0; i < 4; i++) - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F5 + i), (u + g) * 5 + padding * 3 / 4 + i * (u + g), row0, u, u)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_F5 + i), l.ux(6.5 + i), row0)); for (int i = 0; i < 4; i++) - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_F9 + i), (u + g) * 9 + padding + i * (u + g), row0, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PRINT_SCREEN), (u + g) * 15.5, row0, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_SCROLL_LOCK), (u + g) * 16.5, row0, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAUSE), (u + g) * 17.5, row0, u, u)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_F9 + i), l.ux(11d + i), row0)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_PRINT_SCREEN), l.ux(15.5), row0)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_SCROLL_LOCK), l.ux(16.5), row0)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_PAUSE), l.ux(17.5), row0)); // Row 1: ` 1-0 - = BS, Ins/Home/PgUp int[] row1Keys = {GLFW.GLFW_KEY_GRAVE_ACCENT, GLFW.GLFW_KEY_1, GLFW.GLFW_KEY_2, GLFW.GLFW_KEY_3, GLFW.GLFW_KEY_4, GLFW.GLFW_KEY_5, GLFW.GLFW_KEY_6, GLFW.GLFW_KEY_7, GLFW.GLFW_KEY_8, GLFW.GLFW_KEY_9, GLFW.GLFW_KEY_0, GLFW.GLFW_KEY_MINUS, GLFW.GLFW_KEY_EQUAL}; for (int i = 0; i < row1Keys.length; i++) - keys.add(new Key(Keybind.fromKey(row1Keys[i]), i * (u + g), row1, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_BACKSPACE), 13 * (u + g), row1, u * 1.6 + g, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_INSERT), (u + g) * 15.5, row1, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_HOME), (u + g) * 16.5, row1, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAGE_UP), (u + g) * 17.5, row1, u, u)); + keys.add(l.key(Keybind.fromKey(row1Keys[i]), l.ux(i), row1)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_BACKSPACE), l.ux(13), row1, KeyDimensions.BACKSPACE)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_INSERT), l.ux(15.5), row1)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_HOME), l.ux(16.5), row1)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_PAGE_UP), l.ux(17.5), row1)); // Row 2: Tab QWERTY... brackets - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_TAB), 0, row2, u * 1.5, u)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_TAB), 0, row2, KeyDimensions.TAB)); int[] row2Keys = {GLFW.GLFW_KEY_Q, GLFW.GLFW_KEY_W, GLFW.GLFW_KEY_E, GLFW.GLFW_KEY_R, GLFW.GLFW_KEY_T, GLFW.GLFW_KEY_Y, GLFW.GLFW_KEY_U, GLFW.GLFW_KEY_I, GLFW.GLFW_KEY_O, GLFW.GLFW_KEY_P, GLFW.GLFW_KEY_LEFT_BRACKET, GLFW.GLFW_KEY_RIGHT_BRACKET}; + double tabEnd = l.px(KeyDimensions.TAB) + l.keyGap; for (int i = 0; i < row2Keys.length; i++) - keys.add(new Key(Keybind.fromKey(row2Keys[i]), u * 1.5 + g + i * (u + g), row2, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_DELETE), (u + g) * 15.5, row2, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_END), (u + g) * 16.5, row2, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_PAGE_DOWN), (u + g) * 17.5, row2, u, u)); - - // Row 3: Caps ASDF..., UK # key - padding = targetLen - (u * 11 + g * 12); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_CAPS_LOCK), 0, row3, padding * (1.75 / 3.75), u)); + keys.add(l.key(Keybind.fromKey(row2Keys[i]), tabEnd + l.ux(i), row2)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_DELETE), l.ux(15.5), row2)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_END), l.ux(16.5), row2)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_PAGE_DOWN), l.ux(17.5), row2)); + + // Row 3: Caps ASDF..., ISO # key + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_CAPS_LOCK), 0, row3, KeyDimensions.CAPS_LOCK)); int[] row3Keys = {GLFW.GLFW_KEY_A, GLFW.GLFW_KEY_S, GLFW.GLFW_KEY_D, GLFW.GLFW_KEY_F, GLFW.GLFW_KEY_G, GLFW.GLFW_KEY_H, GLFW.GLFW_KEY_J, GLFW.GLFW_KEY_K, GLFW.GLFW_KEY_L, GLFW.GLFW_KEY_SEMICOLON, GLFW.GLFW_KEY_APOSTROPHE}; + double capsEnd = l.px(KeyDimensions.CAPS_LOCK) + l.keyGap; for (int i = 0; i < row3Keys.length; i++) - keys.add(new Key(Keybind.fromKey(row3Keys[i]), padding * (1.75 / 3.75) + g + i * (u + g), row3, u, u)); - // ISO key (backslash on UK) - double backslashKeyX = padding * (1.75 / 3.75) + g + 11 * (u + g); // After ' key - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_BACKSLASH), backslashKeyX, row3, u, u)); - - // ISO Enter - reverse-L shape spanning rows 2-3 - // Top bar starts after ] bracket on row 2 and extends to targetLen - double topBarStartX = u * 1.5 + g + 12 * (u + g); - - // Stem should be right-aligned: it ends at targetLen - double enterStemWidth = u * 1.25; - double enterStemX = targetLen - enterStemWidth; // Right-aligned - - // Verify there's proper gap between ISO key and Enter stem - double gapBetweenIsoAndEnter = enterStemX - (backslashKeyX + u); - // If gap is too small, adjust stem to start after ISO key with minimum gap - if (gapBetweenIsoAndEnter < g * 0.5) { - enterStemX = backslashKeyX + u + g; - enterStemWidth = targetLen - enterStemX; - } + keys.add(l.key(Keybind.fromKey(row3Keys[i]), capsEnd + l.ux(i), row3)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_BACKSLASH), capsEnd + l.ux(11), row3)); - IsoEnterKey isoEnter = new IsoEnterKey( - Keybind.fromKey(GLFW.GLFW_KEY_ENTER), - enterStemX, row2, // Start at row 2 - enterStemWidth, u * 2 + g, // Height spans 2 rows + gap - topBarStartX // Top bar starts after ] bracket - ); - keys.add(isoEnter); + // ISO Enter + double topBarStartX = tabEnd + l.ux(12); + double mainBlockRightEdge = tabEnd + l.ux(12) + l.px(KeyDimensions.TAB); + double enterStemWidth = l.px(KeyDimensions.ENTER_ISO_WIDTH); + double enterStemHeight = l.px(KeyDimensions.ENTER_ISO_HEIGHT); + double enterStemX = mainBlockRightEdge - enterStemWidth; + keys.add(new IsoEnterKey(Keybind.fromKey(GLFW.GLFW_KEY_ENTER), enterStemX, row2, enterStemWidth, enterStemHeight, topBarStartX)); // Row 4: LShift, ISO \| key, ZXCV..., RShift, Up - // ISO: Only Left Shift is shorter (to accommodate extra key), Right Shift stays same size as ANSI - - // Right Shift: Use ANSI's padding calculation to get EXACT same width as ANSI - double ansiPadding = targetLen - (u * 10 + g * 11); // Same as ANSI - double rShiftWidth = ansiPadding * (2.7 / 4.8); // Exact same calculation as ANSI + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_SHIFT), 0, row4, KeyDimensions.LEFT_SHIFT_ISO)); + double lShiftEnd = l.px(KeyDimensions.LEFT_SHIFT_ISO) + l.keyGap; + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_WORLD_2), lShiftEnd, row4)); - // Left Shift: Calculate to fill remaining space (gets shorter due to extra ISO key) - // Total space = targetLen - (ISO key + 10 letters + RShift + 12 gaps) - double lShiftWidth = targetLen - (u + g + 10 * (u + g) + g + rShiftWidth); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_SHIFT), 0, row4, lShiftWidth, u)); - - // ISO \| key next to Left Shift - double isoKeyX = lShiftWidth + g; - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_WORLD_2), isoKeyX, row4, u, u)); - - // Letter keys Z through / int[] row4Keys = {GLFW.GLFW_KEY_Z, GLFW.GLFW_KEY_X, GLFW.GLFW_KEY_C, GLFW.GLFW_KEY_V, GLFW.GLFW_KEY_B, GLFW.GLFW_KEY_N, GLFW.GLFW_KEY_M, GLFW.GLFW_KEY_COMMA, GLFW.GLFW_KEY_PERIOD, GLFW.GLFW_KEY_SLASH}; for (int i = 0; i < row4Keys.length; i++) - keys.add(new Key(Keybind.fromKey(row4Keys[i]), isoKeyX + u + g + i * (u + g), row4, u, u)); - - // Right Shift: Same size as ANSI - double rShiftX = isoKeyX + u + g + 10 * (u + g); // After last letter key - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_SHIFT), rShiftX, row4, rShiftWidth, u)); + keys.add(l.key(Keybind.fromKey(row4Keys[i]), lShiftEnd + l.ux(1d + i), row4)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_UP), (u + g) * 16.5, row4, u, u)); + double rShiftX = lShiftEnd + l.ux(11); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_SHIFT), rShiftX, row4, KeyDimensions.RIGHT_SHIFT)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_UP), l.ux(16.5), row4)); // Row 5: Ctrl/Win/Alt/Space/AltGr/Win/Menu/Ctrl, Arrows - padding = targetLen - (g * 7); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_CONTROL), 0, row5, padding * (1.4 / 14.9), u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_SUPER), padding * (1.4 / 14.9) + g, row5, padding * (1.25 / 14.9), u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_ALT), padding * (2.65 / 14.9) + g * 2, row5, padding * (1.25 / 14.9), u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_SPACE), padding * (3.9 / 14.9) + g * 3, row5, padding * (5.6 / 14.9), u)); - // AltGr (Right Alt) on ISO keyboards - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_ALT), "AltGr", padding * (9.5 / 14.9) + g * 4, row5, padding * (1.25 / 14.9), u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_SUPER), padding * (10.75 / 14.9) + g * 5, row5, padding * (1.25 / 14.9), u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_MENU), padding * (12 / 14.9) + g * 6, row5, padding * (1.4 / 14.9), u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_CONTROL), padding * (13.4 / 14.9) + g * 7, row5, padding * (1.5 / 14.9), u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT), (u + g) * 15.5, row5, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_DOWN), (u + g) * 16.5, row5, u, u)); - keys.add(new Key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT), (u + g) * 17.5, row5, u, u)); + double xPos = 0; + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_CONTROL), xPos, row5, KeyDimensions.CTRL)); + xPos += l.px(KeyDimensions.CTRL) + l.keyGap; + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_SUPER), xPos, row5, KeyDimensions.GUI)); + xPos += l.px(KeyDimensions.GUI) + l.keyGap; + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT_ALT), xPos, row5, KeyDimensions.ALT)); + xPos += l.px(KeyDimensions.ALT) + l.keyGap; + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_SPACE), xPos, row5, KeyDimensions.SPACEBAR)); + xPos += l.px(KeyDimensions.SPACEBAR) + l.keyGap; + keys.add(l.keyNamed(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_ALT), "AltGr", xPos, row5, KeyDimensions.ALT)); + xPos += l.px(KeyDimensions.ALT) + l.keyGap; + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_SUPER), xPos, row5, KeyDimensions.GUI)); + xPos += l.px(KeyDimensions.GUI) + l.keyGap; + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_MENU), xPos, row5, KeyDimensions.MENU)); + xPos += l.px(KeyDimensions.MENU) + l.keyGap; + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT_CONTROL), xPos, row5, KeyDimensions.CTRL)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_LEFT), l.ux(15.5), row5)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_DOWN), l.ux(16.5), row5)); + keys.add(l.key(Keybind.fromKey(GLFW.GLFW_KEY_RIGHT), l.ux(17.5), row5)); } private void calculateSize() { @@ -520,8 +498,10 @@ private void calculateSize() { double maxX = 0, maxY = 0; for (Key key : keys) { - minX = Math.min(minX, key.x); minY = Math.min(minY, key.y); - maxX = Math.max(maxX, key.x + key.width); maxY = Math.max(maxY, key.y + key.height); + minX = Math.min(minX, key.x); + minY = Math.min(minY, key.y); + maxX = Math.max(maxX, key.x + key.width); + maxY = Math.max(maxY, key.y + key.height); } setSize((maxX - minX) * scale.get(), (maxY - minY) * scale.get()); @@ -891,7 +871,8 @@ public int get() { public class CustomKeyListSetting extends Setting> { public CustomKeyListSetting() { - super("custom-keys", "Configure the custom keys display.", List.of(), k -> onPresetChanged(preset.get()), s -> {}, () -> preset.get() == Preset.Custom); + super("custom-keys", "Configure the custom keys display.", List.of(), k -> onPresetChanged(preset.get()), s -> { + }, () -> preset.get() == Preset.Custom); } @Override @@ -1047,7 +1028,7 @@ public void initWidgets() { public static void fillTable(GuiTheme theme, WTable table, CustomKeyListSetting setting) { table.clear(); - for (Iterator it = setting.get().iterator(); it.hasNext();) { + for (Iterator it = setting.get().iterator(); it.hasNext(); ) { Key key = it.next(); table.add(theme.label("Key")).expandWidgetX().widget().color(theme.textSecondaryColor()); diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/LayoutContext.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/LayoutContext.java new file mode 100644 index 0000000000..ba0d41efc8 --- /dev/null +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/LayoutContext.java @@ -0,0 +1,83 @@ +/* + * This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client). + * Copyright (c) Meteor Development. + */ + +package meteordevelopment.meteorclient.systems.hud.elements.keyboard; + +import meteordevelopment.meteorclient.systems.hud.elements.keyboard.KeyboardHud.Key; +import meteordevelopment.meteorclient.utils.misc.Keybind; + +final class LayoutContext { + final double keyUnit; + final double keyGap; + final double step; + final double functionRowGap; + + LayoutContext(double keyUnit, double keyGap, double functionRowGap) { + this.keyUnit = keyUnit; + this.keyGap = keyGap; + this.step = keyUnit + keyGap; + this.functionRowGap = functionRowGap; + } + + /** + * Converts units to pixels in the X direction (horizontal). + * + * @param units number of key units (e.g., 1.0 = one key width + gap) + * @return pixel position + */ + double ux(double units) { + return units * step; + } + + /** + * Converts rows to pixels in the Y direction (vertical). + * Automatically includes the function row gap for rows 1+. + * + * @param rows number of key rows (e.g., 0 = function row, 1 = number row, etc.) + * @return pixel position including function row gap + */ + double uy(double rows) { + return rows * step + (rows > 0 ? functionRowGap : 0); + } + + /** + * Converts a KeyDimensions to pixels, accounting for gaps. + * + * @param d the key dimension + * @return pixel width/height + */ + double px(KeyDimensions d) { + return d.toPixels(keyUnit, keyGap); + } + + /** + * Creates a standard 1u*1u key at the given position. + */ + Key key(Keybind kb, double x, double y) { + return key(kb, x, y, KeyDimensions.STANDARD, KeyDimensions.STANDARD); + } + + /** + * Creates a key with the given width and standard height. + */ + Key key(Keybind kb, double x, double y, KeyDimensions w) { + return key(kb, x, y, w, KeyDimensions.STANDARD); + } + + /** + * Creates a key with the given width and height. + */ + Key key(Keybind kb, double x, double y, KeyDimensions w, KeyDimensions h) { + return new Key(kb, x, y, px(w), px(h)); + } + + /** + * Creates a named key with the given width and standard height. + */ + Key keyNamed(Keybind kb, String name, double x, double y, KeyDimensions w) { + return new Key(kb, name, x, y, px(w), px(KeyDimensions.STANDARD)); + } +} + From 383ca3ff82ed76f7b07af5757c8787078e2a1768 Mon Sep 17 00:00:00 2001 From: Big-Iron-Cheems <52252627+Big-Iron-Cheems@users.noreply.github.com> Date: Wed, 14 Jan 2026 11:44:11 +0100 Subject: [PATCH 19/22] Use LayoutContext and KeyDimensions in all presets --- .../hud/elements/keyboard/KeyboardHud.java | 38 ++++++++-------- .../hud/elements/keyboard/LayoutContext.java | 43 ++++++++++++++++++- 2 files changed, 59 insertions(+), 22 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/KeyboardHud.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/KeyboardHud.java index 68ee0d255b..0d9d52def8 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/KeyboardHud.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/KeyboardHud.java @@ -287,42 +287,38 @@ private void onPresetChanged(Preset preset) { return; keys.clear(); - double w = 40; - double h = 40; - double s = spacing.get() * 2; + double u = 35; // base key unit size + double g = spacing.get() * 2; // gap between keys (spacing setting) + LayoutContext l = new LayoutContext(u, g, 15); switch (preset) { case Movement -> { - keys.add(new Key(mc.options.forwardKey, w + s, 0, w, h)); - keys.add(new Key(mc.options.leftKey, 0, h + s, w, h)); - keys.add(new Key(mc.options.backKey, w + s, h + s, w, h)); - keys.add(new Key(mc.options.rightKey, (w + s) * 2, h + s, w, h)); - keys.add(new Key(mc.options.sneakKey, 0, (h + s) * 2, w, h)); - keys.add(new Key(mc.options.jumpKey, w + s, (h + s) * 2, (w * 2) + s, h)); + keys.add(l.key(mc.options.forwardKey, l.ux(1), 0)); + keys.add(l.key(mc.options.leftKey, 0, l.y(1))); + keys.add(l.key(mc.options.backKey, l.ux(1), l.y(1))); + keys.add(l.key(mc.options.rightKey, l.ux(2), l.y(1))); + keys.add(l.key(mc.options.sneakKey, 0, l.y(2))); + keys.add(l.key(mc.options.jumpKey, l.ux(1), l.y(2), KeyDimensions.UNIT_2U)); } case Clicks -> { - keys.add(new Key(mc.options.attackKey, "LMB", 0, 0, w, h).setShowCps(showCps.get())); - keys.add(new Key(mc.options.useKey, "RMB", w + s, 0, w, h).setShowCps(showCps.get())); + keys.add(l.key(mc.options.attackKey, "LMB", 0, 0).setShowCps(showCps.get())); + keys.add(l.key(mc.options.useKey, "RMB", l.ux(1), 0).setShowCps(showCps.get())); } case Actions -> { - keys.add(new Key(mc.options.dropKey, 0, 0, w, h)); - keys.add(new Key(mc.options.swapHandsKey, w + s, 0, w, h)); - keys.add(new Key(mc.options.inventoryKey, (w + s) * 2, 0, w, h)); + keys.add(l.key(mc.options.dropKey, 0, 0)); + keys.add(l.key(mc.options.swapHandsKey, l.ux(1), 0)); + keys.add(l.key(mc.options.inventoryKey, l.ux(2), 0)); } case Hotbar -> { for (int i = 0; i < 9; i++) { - keys.add(new Key(mc.options.hotbarKeys[i], i * (w + s), 0, w, h)); + keys.add(l.key(mc.options.hotbarKeys[i], l.ux(i), 0)); } } case Keyboard -> { - double u = 35; // base key unit size - double g = s; // gap between keys (spacing setting) - LayoutContext lCtx = new LayoutContext(u, g, 15); - if (keyboardLayout.get() == KeyboardLayout.ANSI) { - buildAnsiLayout(lCtx); + buildAnsiLayout(l); } else { - buildIsoLayout(lCtx); + buildIsoLayout(l); } } case Custom -> keys.addAll(customKeys.get()); diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/LayoutContext.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/LayoutContext.java index ba0d41efc8..be0557855c 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/LayoutContext.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/LayoutContext.java @@ -7,6 +7,7 @@ import meteordevelopment.meteorclient.systems.hud.elements.keyboard.KeyboardHud.Key; import meteordevelopment.meteorclient.utils.misc.Keybind; +import net.minecraft.client.option.KeyBinding; final class LayoutContext { final double keyUnit; @@ -31,6 +32,17 @@ final class LayoutContext { return units * step; } + /** + * Converts rows to pixels in the Y direction (vertical). + * Does not include function row gap - use for simple layouts. + * + * @param rows number of key rows + * @return pixel position + */ + double y(double rows) { + return rows * step; + } + /** * Converts rows to pixels in the Y direction (vertical). * Automatically includes the function row gap for rows 1+. @@ -79,5 +91,34 @@ Key key(Keybind kb, double x, double y, KeyDimensions w, KeyDimensions h) { Key keyNamed(Keybind kb, String name, double x, double y, KeyDimensions w) { return new Key(kb, name, x, y, px(w), px(KeyDimensions.STANDARD)); } -} + // Overloaded methods for KeyBinding support + + /** + * Creates a standard 1u*1u key at the given position. + */ + Key key(KeyBinding kb, double x, double y) { + return key(kb, x, y, KeyDimensions.STANDARD, KeyDimensions.STANDARD); + } + + /** + * Creates a key with the given width and standard height. + */ + Key key(KeyBinding kb, double x, double y, KeyDimensions w) { + return key(kb, x, y, w, KeyDimensions.STANDARD); + } + + /** + * Creates a key with the given width and height. + */ + Key key(KeyBinding kb, double x, double y, KeyDimensions w, KeyDimensions h) { + return new Key(kb, x, y, px(w), px(h)); + } + + /** + * Creates a named key with the given width and standard height. + */ + Key key(KeyBinding kb, String name, double x, double y) { + return new Key(kb, name, x, y, px(KeyDimensions.STANDARD), px(KeyDimensions.STANDARD)); + } +} From d1107ddcdb8e58d2e3c3197a42de46a5d0ad6fbe Mon Sep 17 00:00:00 2001 From: Big-Iron-Cheems <52252627+Big-Iron-Cheems@users.noreply.github.com> Date: Wed, 14 Jan 2026 11:59:27 +0100 Subject: [PATCH 20/22] Better key positioning for new Custom keys --- .../systems/hud/elements/keyboard/KeyboardHud.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/KeyboardHud.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/KeyboardHud.java index 0d9d52def8..45a387ab4e 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/KeyboardHud.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/KeyboardHud.java @@ -1054,7 +1054,14 @@ public static void fillTable(GuiTheme theme, WTable table, CustomKeyListSetting WButton add = table.add(theme.button("Add")).expandX().widget(); add.action = () -> { - setting.get().add(new Key()); + Key newKey = new Key(); + // Position new key to the right of existing keys to avoid overlap + if (!setting.get().isEmpty()) { + Key lastKey = setting.get().get(setting.get().size() - 1); + newKey.x = lastKey.x + lastKey.width + 10; + newKey.y = lastKey.y; + } + setting.get().add(newKey); setting.onChanged(); fillTable(theme, table, setting); From 801273932a00b2a5212890b2fad43f02ee49dded Mon Sep 17 00:00:00 2001 From: Big-Iron-Cheems <52252627+Big-Iron-Cheems@users.noreply.github.com> Date: Wed, 14 Jan 2026 12:45:55 +0100 Subject: [PATCH 21/22] Improve Key ctors --- .../hud/elements/keyboard/KeyboardHud.java | 24 +++++++------------ .../hud/elements/keyboard/LayoutContext.java | 24 +++++++------------ 2 files changed, 16 insertions(+), 32 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/KeyboardHud.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/KeyboardHud.java index 45a387ab4e..8f000567e1 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/KeyboardHud.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/KeyboardHud.java @@ -671,7 +671,10 @@ public static class Key { private float delta; public Key() { - this(Keybind.fromKey(GLFW.GLFW_KEY_SPACE), 0, 0, 60, 40); + this.keybind = Keybind.fromKey(GLFW.GLFW_KEY_SPACE); + this.code = -1; + this.width = 60; + this.height = 40; } public Key(NbtCompound compound) { @@ -684,11 +687,7 @@ public Key(NbtCompound compound) { this.showCps = compound.getBoolean("showCps", false); } - public Key(KeyBinding binding, double x, double y, double width, double height) { - this(binding, null, x, y, width, height); - } - - public Key(KeyBinding binding, String name, double x, double y, double width, double height) { + Key(KeyBinding binding, String name, double x, double y, double width, double height) { this.binding = binding; this.name = name; this.code = -1; @@ -698,17 +697,10 @@ public Key(KeyBinding binding, String name, double x, double y, double width, do this.height = height; } - public Key(Keybind keybind, double x, double y, double width, double height) { - this.keybind = keybind; - this.x = x; - this.y = y; - this.width = width; - this.height = height; - } - - public Key(Keybind keybind, String name, double x, double y, double width, double height) { + Key(Keybind keybind, String name, double x, double y, double width, double height) { this.keybind = keybind; this.name = name; + this.code = -1; this.x = x; this.y = y; this.width = width; @@ -786,7 +778,7 @@ public static class IsoEnterKey extends Key { private final double topBarStartX; public IsoEnterKey(Keybind keybind, double x, double y, double width, double height, double topBarStartX) { - super(keybind, x, y, width, height); + super(keybind, null, x, y, width, height); this.topBarStartX = topBarStartX; } diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/LayoutContext.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/LayoutContext.java index be0557855c..256388f06a 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/LayoutContext.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/LayoutContext.java @@ -64,55 +64,47 @@ final class LayoutContext { return d.toPixels(keyUnit, keyGap); } + // Core key creation methods - Keybind versions + /** * Creates a standard 1u*1u key at the given position. */ Key key(Keybind kb, double x, double y) { - return key(kb, x, y, KeyDimensions.STANDARD, KeyDimensions.STANDARD); + return new Key(kb, null, x, y, px(KeyDimensions.STANDARD), px(KeyDimensions.STANDARD)); } /** * Creates a key with the given width and standard height. */ Key key(Keybind kb, double x, double y, KeyDimensions w) { - return key(kb, x, y, w, KeyDimensions.STANDARD); + return new Key(kb, null, x, y, px(w), px(KeyDimensions.STANDARD)); } /** * Creates a key with the given width and height. */ Key key(Keybind kb, double x, double y, KeyDimensions w, KeyDimensions h) { - return new Key(kb, x, y, px(w), px(h)); + return new Key(kb, null, x, y, px(w), px(h)); } - /** - * Creates a named key with the given width and standard height. - */ Key keyNamed(Keybind kb, String name, double x, double y, KeyDimensions w) { return new Key(kb, name, x, y, px(w), px(KeyDimensions.STANDARD)); } - // Overloaded methods for KeyBinding support + // Core key creation methods - KeyBinding versions /** * Creates a standard 1u*1u key at the given position. */ Key key(KeyBinding kb, double x, double y) { - return key(kb, x, y, KeyDimensions.STANDARD, KeyDimensions.STANDARD); + return new Key(kb, null, x, y, px(KeyDimensions.STANDARD), px(KeyDimensions.STANDARD)); } /** * Creates a key with the given width and standard height. */ Key key(KeyBinding kb, double x, double y, KeyDimensions w) { - return key(kb, x, y, w, KeyDimensions.STANDARD); - } - - /** - * Creates a key with the given width and height. - */ - Key key(KeyBinding kb, double x, double y, KeyDimensions w, KeyDimensions h) { - return new Key(kb, x, y, px(w), px(h)); + return new Key(kb, null, x, y, px(w), px(KeyDimensions.STANDARD)); } /** From 6c7cebd3624058ab5cd809877f2da2efcf529702 Mon Sep 17 00:00:00 2001 From: Big-Iron-Cheems <52252627+Big-Iron-Cheems@users.noreply.github.com> Date: Wed, 14 Jan 2026 14:22:59 +0100 Subject: [PATCH 22/22] USe `getLast()` --- .../meteorclient/systems/hud/elements/keyboard/KeyboardHud.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/KeyboardHud.java b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/KeyboardHud.java index 8f000567e1..94e40cc57f 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/KeyboardHud.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/hud/elements/keyboard/KeyboardHud.java @@ -1049,7 +1049,7 @@ public static void fillTable(GuiTheme theme, WTable table, CustomKeyListSetting Key newKey = new Key(); // Position new key to the right of existing keys to avoid overlap if (!setting.get().isEmpty()) { - Key lastKey = setting.get().get(setting.get().size() - 1); + Key lastKey = setting.get().getLast(); newKey.x = lastKey.x + lastKey.width + 10; newKey.y = lastKey.y; }