Add Mouse Binding (#1092)

This commit is contained in:
slprime authored and GitHub committed 2026-10-25 02:49:38 +03:00
1 parent ebe83f970f
commit e9a3a494d5
24 files changed
+303 -43

No files matched your search

@@ -219,6 +219,11 @@ public class ItemZoom extends Widget implements IContainerInputHandler {
@Override
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) {
return false;
}
@Override
public boolean onHotkeyPressed(GuiContainer gui) {
if (KeyManager.isHashDown("itemzoom.toggle", NEIClientUtils.SHIFT_HASH)) {
NEIClientConfig.getSetting("inventory.itemzoom.enabled")
+46 -4
View File
@@ -4,6 +4,7 @@ import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.StringJoiner;
import java.util.function.BooleanSupplier;
import net.minecraft.client.Minecraft;
import net.minecraft.client.settings.KeyBinding;
@@ -29,6 +30,7 @@ public class KeyManager {
private static final Map<String, KeyBinding> keyBindings = new HashMap<>();
private static final Map<String, String> keyAliases = new HashMap<>();
private static volatile boolean pendingOptionsReload = false;
private static int clickedButton = -1;
static {
// Aliases for legacy keybinds
@@ -73,7 +75,8 @@ public class KeyManager {
final int keyCode = binding.getKeyCode();
if (keyCode < 0) {
return Mouse.isButtonDown(keyCode + 100);
final int button = keyCode + 100;
return Mouse.isButtonDown(button) || button == clickedButton;
} else if (keyCode > Keyboard.KEY_NONE && keyCode < Keyboard.KEYBOARD_SIZE) {
return Keyboard.isKeyDown(keyCode);
}
@@ -82,6 +85,40 @@ public class KeyManager {
return false;
}
/**
* Whether any NEI key binding is assigned to the given mouse button.
*/
public static boolean isMouseBound(int button) {
for (KeyBinding binding : keyBindings.values()) {
if (binding.getKeyCode() == button - 100) {
return true;
}
}
return false;
}
/**
* Runs a hotkey handler for a click of the given mouse button if the button is bound to any NEI key binding. The
* button is reported as down by {@link #isKeyDown} for the duration of the call, because the LWJGL button state can
* already be released when a quick click is processed.
*
* @return true if the click was consumed by the handler
*/
public static boolean handleMouseHotkey(int button, BooleanSupplier handler) {
if (!isMouseBound(button)) {
return false;
}
final int previous = clickedButton;
clickedButton = button;
try {
return handler.getAsBoolean();
} finally {
clickedButton = previous;
}
}
public static boolean isHashDown(String ident) {
return isHashDown(ident, 0);
}
@@ -107,18 +144,23 @@ public class KeyManager {
public static String getKeyName(String ident, int meta) {
final int keyCode = getKeyCode(ident);
return keyCode == Keyboard.KEY_NONE ? null : NEIKeyboardUtils.getKeyName(keyCode + meta);
return keyCode == Keyboard.KEY_NONE ? null : NEIKeyboardUtils.getKeyName(keyCode, meta);
}
public static String getKeyName(String ident, int meta, int mouseBind) {
final int keyCode = getKeyCode(ident);
return keyCode == Keyboard.KEY_NONE && mouseBind == NEIMouseUtils.MOUSE_BTN_NONE ? null
: getKeyName(keyCode + meta, mouseBind);
: getKeyName(keyCode, meta, mouseBind);
}
public static String getKeyName(int keyBind, int mouseBind) {
final int keyCode = NEIKeyboardUtils.unhash(keyBind);
return getKeyName(keyCode, keyBind - keyCode, mouseBind);
}
private static String getKeyName(int keyCode, int hash, int mouseBind) {
StringJoiner keyText = new StringJoiner(" + ");
String keyHash = keyBind == Keyboard.KEY_NONE ? "" : NEIKeyboardUtils.getKeyName(keyBind);
String keyHash = keyCode == Keyboard.KEY_NONE && hash == 0 ? "" : NEIKeyboardUtils.getKeyName(keyCode, hash);
String mouseHash = mouseBind == NEIMouseUtils.MOUSE_BTN_NONE ? "" : NEIMouseUtils.getKeyName(mouseBind);
if (!keyHash.isEmpty()) {
@@ -220,6 +220,20 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
@Override
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) {
if (isEnabled() && !isHidden()) {
for (Widget widget : controlWidgets) {
if (inputFocused == null) {
widget.lastKeyTyped(keyID, keyChar);
}
}
}
return false;
}
@Override
public boolean onHotkeyPressed(GuiContainer gui) {
if (KeyManager.isKeyDown("gui.hide")) {
toggleBooleanSetting("inventory.hidden");
return true;
@@ -234,8 +248,12 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
return true;
}
if (isEnabled() && !isHidden()) {
for (Widget widget : controlWidgets) if (inputFocused == null) widget.lastKeyTyped(keyID, keyChar);
if (isEnabled() && !isHidden() && inputFocused == null) {
for (Widget widget : controlWidgets) {
if (widget.onHotkeyPressed()) {
return true;
}
}
}
return false;
@@ -305,7 +305,7 @@ public abstract class PanelWidget<T extends ItemsGrid<? extends ItemsGridSlot, ?
}
@Override
public boolean handleKeyPress(int keyID, char keyChar) {
public boolean onHotkeyPressed() {
final Point mouse = GuiDraw.getMousePosition();
if (!contains(mouse.x, mouse.y)) return false;
@@ -23,6 +23,11 @@ public class PopupInputHandler implements IContainerInputHandler {
@Override
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) {
return false;
}
@Override
public boolean onHotkeyPressed(GuiContainer gui) {
if (KeyManager.isHashDown("gui.enchant") && canPerformAction("enchant")) {
NEICPH.sendOpenEnchantmentWindow();
@@ -48,15 +48,19 @@ public abstract class RecipeSearchField extends TextField implements ItemFilterP
}
@Override
public void lastKeyTyped(int keyID, char keyChar) {
public boolean onHotkeyPressed() {
if (focused() && KeyManager.isKeyDown("gui.getprevioussearch")) {
handleNavigateHistory(TextHistory.Direction.PREVIOUS);
return true;
}
if (focused() && KeyManager.isKeyDown("gui.getnextsearch")) {
handleNavigateHistory(TextHistory.Direction.NEXT);
return true;
}
return false;
}
@Override
@@ -227,18 +227,24 @@ public class SearchField extends TextField implements ItemFilterProvider {
}
@Override
public void lastKeyTyped(int keyID, char keyChar) {
public boolean onHotkeyPressed() {
if (isVisible() && KeyManager.isHashDown("gui.search")) {
setFocus(true);
return true;
}
if (focused() && KeyManager.isKeyDown("gui.getprevioussearch")) {
handleNavigateHistory(TextHistory.Direction.PREVIOUS);
return true;
}
if (focused() && KeyManager.isKeyDown("gui.getnextsearch")) {
handleNavigateHistory(TextHistory.Direction.NEXT);
return true;
}
return false;
}
@Override
@@ -29,6 +29,10 @@ public abstract class Widget {
public void lastKeyTyped(int keyID, char keyChar) {}
public boolean onHotkeyPressed() {
return false;
}
public boolean handleClickExt(int mx, int my, int button) {
return false;
}
@@ -162,6 +162,19 @@ public class WidgetContainer extends Widget {
}
@Override
public boolean onHotkeyPressed() {
for (int index = this.widgets.size() - 1; index >= 0; index--) {
final Widget widget = this.widgets.get(index);
if (widget.onHotkeyPressed()) {
return true;
}
}
return false;
}
@Override
public boolean isFocused() {
return this.isFocused;
@@ -58,6 +58,7 @@ import codechicken.nei.drawable.DrawableResource;
import codechicken.nei.guihook.IContainerTooltipHandler;
import codechicken.nei.guihook.IGuiClientSide;
import codechicken.nei.guihook.IGuiHandleMouseWheel;
import codechicken.nei.guihook.IGuiHotkeyListener;
import codechicken.nei.recipe.AcceptsFollowingTooltipLineHandler;
import codechicken.nei.recipe.AutoCraftingManager;
import codechicken.nei.recipe.GuiCraftingRecipe;
@@ -77,7 +78,7 @@ import codechicken.nei.util.SlotInaccessible;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGuiClientSide, IGuiHandleMouseWheel,
IContainerTooltipHandler, IGuiContainerOverlay {
IGuiHotkeyListener, IContainerTooltipHandler, IGuiContainerOverlay {
public static class EventHandler {
@@ -911,6 +912,12 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
return;
}
super.keyTyped(typedChar, keyCode);
}
@Override
public boolean onHotkeyPressed() {
if (this.statsPanelVisible && KeyManager.isKeyDown("recipe.recipe")) {
final Point2D mouse = getMousePosition();
final Widget widget = this.statsPanel.getWidgetUnderMouse((int) mouse.getX(), (int) mouse.getY());
@@ -918,11 +925,11 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
if (widget != null && widget instanceof StatsPanel.ItemStatsSlot itemStatsSlot
&& itemStatsSlot.recipeId != null) {
GuiCraftingRecipe.openRecipeGui("recipeId", itemStatsSlot.recipeId.getResult(), itemStatsSlot.recipeId);
return;
return true;
}
}
super.keyTyped(typedChar, keyCode);
return false;
}
private String getHandlerTitle(String handlerName) {
@@ -79,19 +79,24 @@ public class RightPanel extends GuiWidget {
}
@Override
public void lastKeyTyped(int keyID, char keyChar) {
public boolean onHotkeyPressed() {
if (!focused() && KeyManager.isHashDown("gui.search")) {
setFocus(true);
return true;
}
if (focused() && KeyManager.isKeyDown("gui.getprevioussearch")) {
handleNavigateHistory(TextHistory.Direction.PREVIOUS);
return true;
}
if (focused() && KeyManager.isKeyDown("gui.getnextsearch")) {
handleNavigateHistory(TextHistory.Direction.NEXT);
return true;
}
return false;
}
@Override
@@ -449,6 +449,7 @@ public class GuiContainerManager {
}
private int clickHandled = 0;
private int hotkeyHandled = 0;
public final List<IContainerTooltipHandler> instanceTooltipHandlers;
public GuiContainerManager(GuiContainer screen) {
@@ -483,6 +484,7 @@ public class GuiContainerManager {
public void load() {
clickHandled = 0;
hotkeyHandled = 0;
for (IContainerObjectHandler objectHandler : objectHandlers) objectHandler.load(window);
}
@@ -502,6 +504,46 @@ public class GuiContainerManager {
for (IContainerInputHandler inputhander : inputHandlers)
if (inputhander.lastKeyTyped(window, keyChar, keyID)) return true;
return onHotkeyPressed();
}
public boolean onHotkeyPressed() {
if (window instanceof IGuiHotkeyListener && ((IGuiHotkeyListener) window).onHotkeyPressed()) {
return true;
}
for (IContainerInputHandler inputhander : inputHandlers) {
if (inputhander.onHotkeyPressed(window)) {
return true;
}
}
return window instanceof IGuiHotkeyListener && ((IGuiHotkeyListener) window).onScreenHotkeyPressed();
}
public boolean handleMouseHotkey() {
final int button = Mouse.getEventButton();
if (button < 0) {
return false;
}
if (!Mouse.getEventButtonState()) {
// the press has been consumed, so the release must not reach the screen either
if ((hotkeyHandled & 1 << button) == 0) {
return false;
}
hotkeyHandled &= ~(1 << button);
return true;
}
if (KeyManager.handleMouseHotkey(button, this::onHotkeyPressed)) {
hotkeyHandled |= 1 << button;
return true;
}
return false;
}
@@ -36,6 +36,18 @@ public interface IContainerInputHandler {
*/
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID);
/**
* Called when a key or a mouse button is pressed and the event has not been handled by the first pass. Use this for
* key bindings that work globally and check them with {@link codechicken.nei.KeyManager#isKeyDown}, so they work
* the same for keyboard keys and mouse buttons.
*
* @param gui An instance of the currentscreen
* @return true to terminate further processing of this event.
*/
default boolean onHotkeyPressed(GuiContainer gui) {
return false;
}
/**
* Called when the mouse is clicked in the gui
*
@@ -0,0 +1,27 @@
package codechicken.nei.guihook;
/**
* Interface for GuiContainers to receive NEI compatible hotkey events from both keyboard keys and mouse buttons
*/
public interface IGuiHotkeyListener {
/**
* Called before the input handlers when a key or a mouse button is pressed, check key bindings with
* {@link codechicken.nei.KeyManager#isKeyDown}
*
* @return true to terminate further processing of this event.
*/
default boolean onHotkeyPressed() {
return false;
}
/**
* Called after the input handlers for screen-wide hotkeys that do not depend on the hovered object, so they never
* take priority over more specific actions
*
* @return true to terminate further processing of this event.
*/
default boolean onScreenHotkeyPressed() {
return false;
}
}
@@ -122,18 +122,25 @@ public class GuiFavoriteButton extends GuiRecipeButton {
return Recipe.of(this.handlerRef);
}
public void lastKeyTyped(char keyChar, int keyID) {
@Override
public void lastKeyTyped(char keyChar, int keyID) {}
@Override
public boolean onHotkeyPressed() {
if (KeyManager.isKeyDown("bookmark.add") && NEIClientUtils.shiftKey()) {
saveRecipeInBookmark();
return true;
}
if (KeyManager.isKeyDown("bookmark.open_crafting_tree") && NEIClientUtils.shiftKey()
&& saveRecipeInBookmark()) {
final int groupId = ItemPanels.bookmarkPanel.getGrid().getNextGroupId() - 1;
GuiCraftingTree.openCraftingTreeGui(ItemPanels.bookmarkPanel.getGrid(), groupId);
return true;
}
return false;
}
@Override
@@ -346,14 +346,21 @@ public class GuiOverlayButton extends GuiRecipeButton {
}
@Override
public void lastKeyTyped(char keyChar, int keyID) {
public void lastKeyTyped(char keyChar, int keyID) {}
@Override
public boolean onHotkeyPressed() {
if (KeyManager.isKeyDown("bookmark.add") && NEIClientUtils.shiftKey()) {
saveRecipeInBookmark();
return true;
}
if (KeyManager.isKeyDown("bookmark.chat_link") && NEIClientUtils.controlKey() && NEIClientUtils.shiftKey()) {
sendRecipeChatLink();
return true;
}
return false;
}
public void saveRecipeInBookmark() {
@@ -51,6 +51,7 @@ import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.guihook.IContainerTooltipHandler;
import codechicken.nei.guihook.IGuiClientSide;
import codechicken.nei.guihook.IGuiHandleMouseWheel;
import codechicken.nei.guihook.IGuiHotkeyListener;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.widget.RecipeWidget;
import codechicken.nei.scroll.ScrollBar;
@@ -62,7 +63,7 @@ import codechicken.nei.util.EmptyContainer;
import codechicken.nei.util.SlotInaccessible;
public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer implements IGuiContainerOverlay,
IGuiClientSide, IGuiHandleMouseWheel, IContainerTooltipHandler, INEIGuiHandler {
IGuiClientSide, IGuiHandleMouseWheel, IGuiHotkeyListener, IContainerTooltipHandler, INEIGuiHandler {
private static final int BORDER_PADDING = 5;
private static final int TRANSPARENCY_BORDER = 4;
@@ -697,6 +698,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
if (GuiRecipe.searchField.isVisible() && GuiRecipe.searchField.focused()) {
GuiRecipe.searchField.lastKeyTyped(i, c);
GuiRecipe.searchField.onHotkeyPressed();
return;
}
@@ -721,7 +723,24 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
if (i == mc.gameSettings.keyBindInventory.getKeyCode()) {
this.mc.displayGuiScreen(getFirstScreenGeneral());
NEICPH.sendRequestContainer();
} else if (KeyManager.isKeyDown("recipe.back")) {
}
}
@Override
public boolean onHotkeyPressed() {
if (GuiRecipe.searchField.isVisible() && GuiRecipe.searchField.onHotkeyPressed()
|| this.recipeCatalyst.isShowWidget() && this.recipeCatalyst.onHotkeyPressed()
|| this.container.onHotkeyPressed()) {
return true;
}
return false;
}
@Override
public boolean onScreenHotkeyPressed() {
if (KeyManager.isKeyDown("recipe.back")) {
this.mc.displayGuiScreen(this.prevGui);
} else if (KeyManager.isKeyDown("recipe.prev_machine")) {
prevType();
@@ -731,7 +750,11 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
prevPage();
} else if (KeyManager.isKeyDown("recipe.next_recipe")) {
nextPage();
} else {
return false;
}
return true;
}
@Override
@@ -64,6 +64,10 @@ public abstract class GuiRecipeButton extends GuiNEIButton {
public abstract void lastKeyTyped(char keyChar, int keyID);
public boolean onHotkeyPressed() {
return false;
}
public abstract void drawItemOverlay();
public void update() {
@@ -29,6 +29,11 @@ public class RecipeItemInputHandler implements IContainerInputHandler, IContaine
@Override
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyCode) {
return false;
}
@Override
public boolean onHotkeyPressed(GuiContainer gui) {
return ShortcutInputHandler.handleKeyEvent(GuiContainerManager.getStackMouseOver(gui));
}
@@ -287,6 +287,11 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
@Override
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyCode) {
return false;
}
@Override
public boolean onHotkeyPressed(GuiContainer gui) {
if (!canHandle(gui)) return false;
if (KeyManager.isKeyDown("recipe.recipe")) {
@@ -512,6 +512,11 @@ public class DebugHandlerWidget extends Widget implements IContainerTooltipHandl
return false;
}
@Override
public boolean onHotkeyPressed() {
return this.record != null && this.container.onHotkeyPressed();
}
@Override
public void onGuiClick(int mx, int my) {
if (this.record != null) {
@@ -456,25 +456,6 @@ public class RecipeWidget extends Widget {
}
}
@Override
public boolean handleKeyPress(int keyID, char keyChar) {
if (NEIClientConfig.favoritesEnabled()
&& KeyManager.isHashDown("bookmark.favorite_item", NEIClientUtils.SHIFT_HASH)) {
final Point mouse = GuiDraw.getMousePosition();
final PositionedStack hovered = getCyclingStackMouseOver(mouse.x, mouse.y);
if (hovered != null && getPermutations(hovered).size() > 1) {
FavoriteRecipes.toggleFavoriteItem(hovered.item);
setPaused(false);
return true;
}
}
return false;
}
@Override
public void lastKeyTyped(int keyID, char keyChar) {
final Point mouse = GuiDraw.getMousePosition();
@@ -490,6 +471,27 @@ public class RecipeWidget extends Widget {
}
@Override
public boolean onHotkeyPressed() {
final Point mouse = GuiDraw.getMousePosition();
if (NEIClientConfig.favoritesEnabled()
&& KeyManager.isHashDown("bookmark.favorite_item", NEIClientUtils.SHIFT_HASH)) {
final PositionedStack hovered = getCyclingStackMouseOver(mouse.x, mouse.y);
if (hovered != null && getPermutations(hovered).size() > 1) {
FavoriteRecipes.toggleFavoriteItem(hovered.item);
setPaused(false);
return true;
}
}
return forEachButtons(
button -> button.contains(mouse.x, mouse.y) && button.onHotkeyPressed() ? true : null,
false);
}
@Override
public boolean handleClick(int mouseX, int mouseY, int button) {
@@ -56,18 +56,24 @@ public class NEIKeyboardUtils {
return keyText.toString();
}
public static String getKeyName(int keyBind) {
keyBind = keyHashMap.getOrDefault(keyBind, keyBind);
StringJoiner keyText = new StringJoiner(" + ");
String hashText = getHashName(keyBind);
int keyID = unhash(keyBind);
/**
* Mouse buttons are negative key codes, so the hash can not be packed into them and is passed separately.
*/
public static String getKeyName(int keyCode, int hash) {
if (hash == 0 && keyHashMap.containsKey(keyCode)) {
hash = keyHashMap.get(keyCode);
keyCode = Keyboard.CHAR_NONE;
}
final StringJoiner keyText = new StringJoiner(" + ");
final String hashText = getHashName(hash);
if (!hashText.isEmpty()) {
keyText.add(hashText);
}
if (keyID != Keyboard.CHAR_NONE || hashText.isEmpty()) {
keyText.add(keyID < 0 ? NEIMouseUtils.getKeyName(keyID + 100) : Keyboard.getKeyName(keyID));
if (keyCode != Keyboard.CHAR_NONE || hashText.isEmpty()) {
keyText.add(keyCode < 0 ? NEIMouseUtils.getKeyName(keyCode + 100) : Keyboard.getKeyName(keyCode));
}
return keyText.toString();
@@ -121,6 +121,12 @@ RETURN
list m_handleMouseInput
ALOAD 0
GETFIELD net/minecraft/client/gui/inventory/GuiContainer.manager : Lcodechicken/nei/guihook/GuiContainerManager;
INVOKEVIRTUAL codechicken/nei/guihook/GuiContainerManager.handleMouseHotkey ()Z
IFEQ LCONT
RETURN
LCONT
ALOAD 0
INVOKESPECIAL net/minecraft/client/gui/GuiScreen.func_146274_d ()V
ALOAD 0
GETFIELD net/minecraft/client/gui/inventory/GuiContainer.manager : Lcodechicken/nei/guihook/GuiContainerManager;