Browse Source

2.8.2 Build 104

Fixed #81
Close #82
Close #83
Unknown 6 năm trước cách đây
mục cha
commit
6b865d6e70
31 tập tin đã thay đổi với 336 bổ sung174 xóa
  1. 4 0
      CHANGELOG.md
  2. 1 1
      gradle.properties
  3. 1 1
      src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCore.java
  4. 47 0
      src/main/java/me/shedaniel/rei/api/ClientHelper.java
  5. 8 0
      src/main/java/me/shedaniel/rei/api/DisplayHelper.java
  6. 4 1
      src/main/java/me/shedaniel/rei/api/DisplayVisibility.java
  7. 2 1
      src/main/java/me/shedaniel/rei/api/ItemCheatingMode.java
  8. 6 1
      src/main/java/me/shedaniel/rei/api/PluginFunction.java
  9. 3 0
      src/main/java/me/shedaniel/rei/api/RecipeHelper.java
  10. 21 8
      src/main/java/me/shedaniel/rei/client/BaseBoundsHandlerImpl.java
  11. 68 30
      src/main/java/me/shedaniel/rei/client/ClientHelperImpl.java
  12. 10 17
      src/main/java/me/shedaniel/rei/client/ConfigObject.java
  13. 22 18
      src/main/java/me/shedaniel/rei/client/DisplayHelperImpl.java
  14. 3 1
      src/main/java/me/shedaniel/rei/client/ItemListOrdering.java
  15. 27 10
      src/main/java/me/shedaniel/rei/client/RecipeHelperImpl.java
  16. 5 2
      src/main/java/me/shedaniel/rei/client/SearchArgument.java
  17. 3 1
      src/main/java/me/shedaniel/rei/client/Weather.java
  18. 32 18
      src/main/java/me/shedaniel/rei/gui/ContainerScreenOverlay.java
  19. 12 4
      src/main/java/me/shedaniel/rei/gui/RecipeViewingScreen.java
  20. 6 1
      src/main/java/me/shedaniel/rei/gui/config/ItemListOrderingConfig.java
  21. 17 20
      src/main/java/me/shedaniel/rei/gui/widget/ItemListOverlay.java
  22. 8 8
      src/main/java/me/shedaniel/rei/gui/widget/ItemSlotWidget.java
  23. 0 1
      src/main/java/me/shedaniel/rei/gui/widget/LabelWidget.java
  24. 11 0
      src/main/java/me/shedaniel/rei/gui/widget/SearchFieldWidget.java
  25. 5 10
      src/main/java/me/shedaniel/rei/mixin/MixinContainerScreen.java
  26. 1 2
      src/main/java/me/shedaniel/rei/mixin/MixinCreativePlayerInventoryScreen.java
  27. 3 8
      src/main/java/me/shedaniel/rei/mixin/MixinRecipeBookGui.java
  28. 1 8
      src/main/java/me/shedaniel/rei/plugin/DefaultPlugin.java
  29. 2 1
      src/main/java/me/shedaniel/rei/utils/ClothScreenRegistry.java
  30. 2 0
      src/main/resources/assets/roughlyenoughitems/lang/en_us.json
  31. 1 1
      src/main/resources/fabric.mod.json

+ 4 - 0
CHANGELOG.md

@@ -1,4 +1,8 @@
 View full changelog [here](https://github.com/shedaniel/RoughlyEnoughItems/blob/1.14/CHANGELOG.md).
+## v2.8.2+build.104
+- Fixed [#81](https://github.com/shedaniel/RoughlyEnoughItems/issues/81): Scrolling unaffected by exclusion zones.
+- Added [#82](https://github.com/shedaniel/RoughlyEnoughItems/issues/82): Close search after pressing "Enter"
+- Added [#83](https://github.com/shedaniel/RoughlyEnoughItems/issues/83): 2 More keybinds
 ## v2.8.1+build.103
 - Fixed: Item Searching Layering
 - Added: Some tooltips in the config

+ 1 - 1
gradle.properties

@@ -1,4 +1,4 @@
-mod_version=2.8.1+build.103
+mod_version=2.8.2+build.104
 minecraft_version=1.14
 yarn_version=1.14+build.5
 fabricloader_version=0.4.6+build.141

+ 1 - 1
src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCore.java

@@ -208,7 +208,7 @@ public class RoughlyEnoughItemsCore implements ClientModInitializer {
         });
         ClothClientHooks.SCREEN_MOUSE_SCROLLED.register((minecraftClient, screen, v, v1, v2) -> {
             if (screen instanceof ContainerScreen)
-                if (ScreenHelper.isOverlayVisible() && ScreenHelper.getLastOverlay().getRectangle().contains(ClientUtils.getMouseLocation()) && ScreenHelper.getLastOverlay().mouseScrolled(v, v1, v2))
+                if (ScreenHelper.isOverlayVisible() && ScreenHelper.getLastOverlay().isInside(ClientUtils.getMouseLocation()) && ScreenHelper.getLastOverlay().mouseScrolled(v, v1, v2))
                     return ActionResult.SUCCESS;
             return ActionResult.PASS;
         });

+ 47 - 0
src/main/java/me/shedaniel/rei/api/ClientHelper.java

@@ -0,0 +1,47 @@
+package me.shedaniel.rei.api;
+
+import me.shedaniel.rei.client.ClientHelperImpl;
+import net.fabricmc.api.ClientModInitializer;
+import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+
+import java.util.List;
+
+public interface ClientHelper extends ClientModInitializer {
+    static ClientHelper getInstance() {
+        return ClientHelperImpl.instance;
+    }
+    
+    boolean isCheating();
+    
+    void setCheating(boolean cheating);
+    
+    List<ItemStack> getInventoryItemsTypes();
+    
+    void registerFabricKeyBinds();
+    
+    boolean tryCheatingStack(ItemStack stack);
+    
+    boolean executeRecipeKeyBind(ItemStack stack);
+    
+    boolean executeUsageKeyBind(ItemStack stack);
+    
+    String getModFromItem(Item item);
+    
+    void sendDeletePacket();
+    
+    String getFormattedModFromItem(Item item);
+    
+    FabricKeyBinding getRecipeKeyBinding();
+    
+    FabricKeyBinding getUsageKeyBinding();
+    
+    FabricKeyBinding getHideKeyBinding();
+    
+    FabricKeyBinding getPreviousPageKeyBinding();
+    
+    FabricKeyBinding getNextPageKeyBinding();
+    
+    boolean executeViewAllRecipesKeyBind();
+}

+ 8 - 0
src/main/java/me/shedaniel/rei/api/DisplayHelper.java

@@ -12,6 +12,8 @@ public interface DisplayHelper {
     
     List<DisplayBoundsHandler> getSortedBoundsHandlers(Class screenClass);
     
+    List<DisplayBoundsHandler> getAllBoundsHandlers();
+    
     DisplayBoundsHandler getResponsibleBoundsHandler(Class screenClass);
     
     void registerBoundsHandler(DisplayBoundsHandler handler);
@@ -19,6 +21,8 @@ public interface DisplayHelper {
     BaseBoundsHandler getBaseBoundsHandler();
     
     public static interface DisplayBoundsHandler<T> {
+        public static final Rectangle EMPTY = new Rectangle();
+        
         Class getBaseSupportedClass();
         
         Rectangle getLeftBounds(T screen);
@@ -29,6 +33,10 @@ public interface DisplayHelper {
             return PASS;
         }
         
+        default ActionResult isInZone(boolean isOnRightSide, double mouseX, double mouseY) {
+            return PASS;
+        }
+        
         default Rectangle getItemListArea(Rectangle rectangle) {
             return new Rectangle(rectangle.x + 2, rectangle.y + 24, rectangle.width - 4, rectangle.height - (RoughlyEnoughItemsCore.getConfigManager().getConfig().sideSearchField ? 27 + 22 : 27));
         }

+ 4 - 1
src/main/java/me/shedaniel/rei/api/DisplayVisibility.java

@@ -1,5 +1,8 @@
 package me.shedaniel.rei.api;
 
 public enum DisplayVisibility {
-    ALWAYS_VISIBLE, CONFIG_OPTIONAL, NEVER_VISIBLE, PASS
+    ALWAYS_VISIBLE,
+    CONFIG_OPTIONAL,
+    NEVER_VISIBLE,
+    PASS
 }

+ 2 - 1
src/main/java/me/shedaniel/rei/api/ItemCheatingMode.java

@@ -1,5 +1,6 @@
 package me.shedaniel.rei.api;
 
 public enum ItemCheatingMode {
-    REI_LIKE, JEI_LIKE;
+    REI_LIKE,
+    JEI_LIKE;
 }

+ 6 - 1
src/main/java/me/shedaniel/rei/api/PluginFunction.java

@@ -1,5 +1,10 @@
 package me.shedaniel.rei.api;
 
 public enum PluginFunction {
-    REGISTER_ITEMS, REGISTER_CATEGORIES, REGISTER_RECIPE_DISPLAYS, REGISTER_SPEED_CRAFT, REGISTER_BOUNDS, REGISTER_OTHERS;
+    REGISTER_ITEMS,
+    REGISTER_CATEGORIES,
+    REGISTER_RECIPE_DISPLAYS,
+    REGISTER_SPEED_CRAFT,
+    REGISTER_BOUNDS,
+    REGISTER_OTHERS;
 }

+ 3 - 0
src/main/java/me/shedaniel/rei/api/RecipeHelper.java

@@ -2,6 +2,7 @@ package me.shedaniel.rei.api;
 
 import me.shedaniel.rei.RoughlyEnoughItemsCore;
 import net.minecraft.item.ItemStack;
+import net.minecraft.recipe.Recipe;
 import net.minecraft.recipe.RecipeManager;
 import net.minecraft.util.Identifier;
 
@@ -17,6 +18,8 @@ public interface RecipeHelper {
     
     int getRecipeCount();
     
+    List<Recipe> getVanillaSortedRecipes();
+    
     List<ItemStack> findCraftableByItems(List<ItemStack> inventoryItems);
     
     void registerCategory(RecipeCategory category);

+ 21 - 8
src/main/java/me/shedaniel/rei/client/BaseBoundsHandlerImpl.java

@@ -38,12 +38,12 @@ public class BaseBoundsHandlerImpl implements BaseBoundsHandler {
     
     @Override
     public Rectangle getLeftBounds(Screen screen) {
-        return new Rectangle();
+        return DisplayHelper.DisplayBoundsHandler.EMPTY;
     }
     
     @Override
     public Rectangle getRightBounds(Screen screen) {
-        return new Rectangle();
+        return DisplayHelper.DisplayBoundsHandler.EMPTY;
     }
     
     @Override
@@ -51,21 +51,34 @@ public class BaseBoundsHandlerImpl implements BaseBoundsHandler {
         return -5f;
     }
     
+    @Override
+    public ActionResult isInZone(boolean isOnRightSide, double mouseX, double mouseY) {
+        for(Rectangle zone : getCurrentExclusionZones(MinecraftClient.getInstance().currentScreen.getClass(), isOnRightSide))
+            if (zone.contains(mouseX, mouseY))
+                return ActionResult.FAIL;
+        return ActionResult.PASS;
+    }
+    
     @Override
     public boolean shouldRecalculateArea(boolean isOnRightSide, Rectangle rectangle) {
         if (lastArea == null) {
-            DisplayHelper.DisplayBoundsHandler handler = RoughlyEnoughItemsCore.getDisplayHelper().getResponsibleBoundsHandler(MinecraftClient.getInstance().currentScreen.getClass());
-            lastArea = getStringFromAreas(isOnRightSide ? handler.getRightBounds(MinecraftClient.getInstance().currentScreen) : handler.getLeftBounds(MinecraftClient.getInstance().currentScreen), getCurrentExclusionZones(MinecraftClient.getInstance().currentScreen.getClass(), isOnRightSide));
+            lastArea = getStringFromCurrent(isOnRightSide);
             return false;
         }
-        DisplayHelper.DisplayBoundsHandler handler = RoughlyEnoughItemsCore.getDisplayHelper().getResponsibleBoundsHandler(MinecraftClient.getInstance().currentScreen.getClass());
-        String fromAreas = getStringFromAreas(isOnRightSide ? handler.getRightBounds(MinecraftClient.getInstance().currentScreen) : handler.getLeftBounds(MinecraftClient.getInstance().currentScreen), getCurrentExclusionZones(MinecraftClient.getInstance().currentScreen.getClass(), isOnRightSide));
-        if (lastArea.contentEquals(fromAreas))
+        if (lastArea.contentEquals(getStringFromCurrent(isOnRightSide)))
             return false;
-        lastArea = fromAreas;
+        lastArea = getStringFromCurrent(isOnRightSide);
         return true;
     }
     
+    private DisplayHelper.DisplayBoundsHandler getHandler() {
+        return RoughlyEnoughItemsCore.getDisplayHelper().getResponsibleBoundsHandler(MinecraftClient.getInstance().currentScreen.getClass());
+    }
+    
+    private String getStringFromCurrent(boolean isOnRightSide) {
+        return getStringFromAreas(isOnRightSide ? getHandler().getRightBounds(MinecraftClient.getInstance().currentScreen) : getHandler().getLeftBounds(MinecraftClient.getInstance().currentScreen), getCurrentExclusionZones(MinecraftClient.getInstance().currentScreen.getClass(), isOnRightSide));
+    }
+    
     @Override
     public ActionResult canItemSlotWidgetFit(boolean isOnRightSide, int left, int top, Screen screen, Rectangle fullBounds) {
         List<Rectangle> currentExclusionZones = getCurrentExclusionZones(MinecraftClient.getInstance().currentScreen.getClass(), isOnRightSide);

+ 68 - 30
src/main/java/me/shedaniel/rei/client/ClientHelper.java → src/main/java/me/shedaniel/rei/client/ClientHelperImpl.java

@@ -5,11 +5,11 @@ import com.google.common.collect.Maps;
 import io.netty.buffer.Unpooled;
 import me.shedaniel.rei.RoughlyEnoughItemsCore;
 import me.shedaniel.rei.RoughlyEnoughItemsNetwork;
+import me.shedaniel.rei.api.ClientHelper;
 import me.shedaniel.rei.api.RecipeCategory;
 import me.shedaniel.rei.api.RecipeDisplay;
 import me.shedaniel.rei.api.RecipeHelper;
 import me.shedaniel.rei.gui.RecipeViewingScreen;
-import net.fabricmc.api.ClientModInitializer;
 import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding;
 import net.fabricmc.fabric.api.network.ClientSidePacketRegistry;
 import net.fabricmc.fabric.impl.client.keybinding.KeyBindingRegistryImpl;
@@ -34,49 +34,75 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 
-public class ClientHelper implements ClientModInitializer {
+public class ClientHelperImpl implements ClientHelper {
     
-    private static final Identifier RECIPE_KEYBIND = new Identifier("roughlyenoughitems", "recipe_keybind");
-    private static final Identifier USAGE_KEYBIND = new Identifier("roughlyenoughitems", "usage_keybind");
-    private static final Identifier HIDE_KEYBIND = new Identifier("roughlyenoughitems", "hide_keybind");
-    private static final Map<String, String> MOD_NAME_CACHE = Maps.newHashMap();
-    public static FabricKeyBinding RECIPE, USAGE, HIDE;
+    public static ClientHelperImpl instance;
+    private final Identifier recipeKeybind = new Identifier("roughlyenoughitems", "recipe_keybind");
+    private final Identifier usageKeybind = new Identifier("roughlyenoughitems", "usage_keybind");
+    private final Identifier hideKeybind = new Identifier("roughlyenoughitems", "hide_keybind");
+    private final Identifier previousPageKeybind = new Identifier("roughlyenoughitems", "previous_page");
+    private final Identifier nextPageKeybind = new Identifier("roughlyenoughitems", "next_page");
+    private final Map<String, String> modNameCache = Maps.newHashMap();
+    public FabricKeyBinding recipe, usage, hide, previousPage, nextPage;
     
-    static {
-        MOD_NAME_CACHE.put("minecraft", "Minecraft");
-        MOD_NAME_CACHE.put("c", "Common");
-    }
-    
-    public static String getFormattedModFromItem(Item item) {
+    @Override
+    public String getFormattedModFromItem(Item item) {
         String mod = getModFromItem(item);
         if (mod.equalsIgnoreCase(""))
             return "";
         return "§9§o" + mod;
     }
     
-    public static String getModFromItem(Item item) {
+    @Override
+    public FabricKeyBinding getRecipeKeyBinding() {
+        return recipe;
+    }
+    
+    @Override
+    public FabricKeyBinding getUsageKeyBinding() {
+        return usage;
+    }
+    
+    @Override
+    public FabricKeyBinding getHideKeyBinding() {
+        return hide;
+    }
+    
+    @Override
+    public FabricKeyBinding getPreviousPageKeyBinding() {
+        return previousPage;
+    }
+    
+    @Override
+    public FabricKeyBinding getNextPageKeyBinding() {
+        return nextPage;
+    }
+    
+    public String getModFromItem(Item item) {
         if (item.equals(Items.AIR))
             return "";
         return getModFromIdentifier(Registry.ITEM.getId(item));
     }
     
-    public static String getModFromIdentifier(Identifier identifier) {
+    public String getModFromIdentifier(Identifier identifier) {
         if (identifier == null)
             return "";
-        Optional<String> any = Optional.ofNullable(MOD_NAME_CACHE.getOrDefault(identifier.getNamespace(), null));
+        Optional<String> any = Optional.ofNullable(modNameCache.getOrDefault(identifier.getNamespace(), null));
         if (any.isPresent())
             return any.get();
         String modid = identifier.getNamespace();
         String s = FabricLoader.getInstance().getModContainer(modid).map(ModContainer::getMetadata).map(ModMetadata::getName).orElse(modid);
-        MOD_NAME_CACHE.put(modid, s);
+        modNameCache.put(modid, s);
         return s;
     }
     
-    public static boolean isCheating() {
+    @Override
+    public boolean isCheating() {
         return RoughlyEnoughItemsCore.getConfigManager().getConfig().cheating;
     }
     
-    public static void setCheating(boolean cheating) {
+    @Override
+    public void setCheating(boolean cheating) {
         RoughlyEnoughItemsCore.getConfigManager().getConfig().cheating = cheating;
         try {
             RoughlyEnoughItemsCore.getConfigManager().saveConfig();
@@ -85,7 +111,8 @@ public class ClientHelper implements ClientModInitializer {
         }
     }
     
-    public static void sendDeletePacket() {
+    @Override
+    public void sendDeletePacket() {
         if (ScreenHelper.getLastContainerScreen() instanceof CreativePlayerInventoryScreen) {
             MinecraftClient.getInstance().player.inventory.setCursorStack(ItemStack.EMPTY);
             return;
@@ -93,7 +120,8 @@ public class ClientHelper implements ClientModInitializer {
         ClientSidePacketRegistry.INSTANCE.sendToServer(RoughlyEnoughItemsNetwork.DELETE_ITEMS_PACKET, new PacketByteBuf(Unpooled.buffer()));
     }
     
-    public static boolean tryCheatingStack(ItemStack cheatedStack) {
+    @Override
+    public boolean tryCheatingStack(ItemStack cheatedStack) {
         if (RoughlyEnoughItemsCore.canUsePackets()) {
             try {
                 ClientSidePacketRegistry.INSTANCE.sendToServer(RoughlyEnoughItemsNetwork.CREATE_ITEMS_PACKET, new PacketByteBuf(Unpooled.buffer()).writeItemStack(cheatedStack.copy()));
@@ -108,28 +136,31 @@ public class ClientHelper implements ClientModInitializer {
             String madeUpCommand = og.replaceAll("\\{player_name}", MinecraftClient.getInstance().player.getEntityName()).replaceAll("\\{item_identifier}", identifier.toString()).replaceAll("\\{nbt}", tagMessage).replaceAll("\\{count}", String.valueOf(cheatedStack.getAmount()));
             if (madeUpCommand.length() > 256) {
                 madeUpCommand = og.replaceAll("\\{player_name}", MinecraftClient.getInstance().player.getEntityName()).replaceAll("\\{item_identifier}", identifier.toString()).replaceAll("\\{nbt}", "").replaceAll("\\{count}", String.valueOf(cheatedStack.getAmount()));
-                MinecraftClient.getInstance().player.addChatMessage(new TranslatableTextComponent("text.rei.too_long_nbt"), false);
+                MinecraftClient.getInstance().player.addChatMessage(new TranslatableTextComponent("text.rei" + ".too_long_nbt"), false);
             }
             MinecraftClient.getInstance().player.sendChatMessage(madeUpCommand);
             return true;
         }
     }
     
-    public static boolean executeRecipeKeyBind(ItemStack stack) {
+    @Override
+    public boolean executeRecipeKeyBind(ItemStack stack) {
         Map<RecipeCategory, List<RecipeDisplay>> map = RecipeHelper.getInstance().getRecipesFor(stack);
         if (map.keySet().size() > 0)
             MinecraftClient.getInstance().openScreen(new RecipeViewingScreen(MinecraftClient.getInstance().window, map));
         return map.keySet().size() > 0;
     }
     
-    public static boolean executeUsageKeyBind(ItemStack stack) {
+    @Override
+    public boolean executeUsageKeyBind(ItemStack stack) {
         Map<RecipeCategory, List<RecipeDisplay>> map = RecipeHelper.getInstance().getUsagesFor(stack);
         if (map.keySet().size() > 0)
             MinecraftClient.getInstance().openScreen(new RecipeViewingScreen(MinecraftClient.getInstance().window, map));
         return map.keySet().size() > 0;
     }
     
-    public static List<ItemStack> getInventoryItemsTypes() {
+    @Override
+    public List<ItemStack> getInventoryItemsTypes() {
         List<DefaultedList<ItemStack>> field_7543 = ImmutableList.of(MinecraftClient.getInstance().player.inventory.main, MinecraftClient.getInstance().player.inventory.armor, MinecraftClient.getInstance().player.inventory.offHand);
         List<ItemStack> inventoryStacks = new ArrayList<>();
         field_7543.forEach(itemStacks -> itemStacks.forEach(itemStack -> {
@@ -139,7 +170,8 @@ public class ClientHelper implements ClientModInitializer {
         return inventoryStacks;
     }
     
-    public static boolean executeViewAllRecipesKeyBind() {
+    @Override
+    public boolean executeViewAllRecipesKeyBind() {
         Map<RecipeCategory, List<RecipeDisplay>> map = RecipeHelper.getInstance().getAllRecipes();
         if (map.keySet().size() > 0)
             MinecraftClient.getInstance().openScreen(new RecipeViewingScreen(MinecraftClient.getInstance().window, map));
@@ -148,15 +180,21 @@ public class ClientHelper implements ClientModInitializer {
     
     @Override
     public void onInitializeClient() {
+        ClientHelperImpl.instance = (ClientHelperImpl) this;
         registerFabricKeyBinds();
+        modNameCache.put("minecraft", "Minecraft");
+        modNameCache.put("c", "Common");
     }
     
-    private void registerFabricKeyBinds() {
+    @Override
+    public void registerFabricKeyBinds() {
         String category = "key.rei.category";
         KeyBindingRegistryImpl.INSTANCE.addCategory(category);
-        KeyBindingRegistryImpl.INSTANCE.register(RECIPE = FabricKeyBinding.Builder.create(RECIPE_KEYBIND, InputUtil.Type.KEYSYM, 82, category).build());
-        KeyBindingRegistryImpl.INSTANCE.register(USAGE = FabricKeyBinding.Builder.create(USAGE_KEYBIND, InputUtil.Type.KEYSYM, 85, category).build());
-        KeyBindingRegistryImpl.INSTANCE.register(HIDE = FabricKeyBinding.Builder.create(HIDE_KEYBIND, InputUtil.Type.KEYSYM, 79, category).build());
+        KeyBindingRegistryImpl.INSTANCE.register(recipe = FabricKeyBinding.Builder.create(recipeKeybind, InputUtil.Type.KEYSYM, 82, category).build());
+        KeyBindingRegistryImpl.INSTANCE.register(usage = FabricKeyBinding.Builder.create(usageKeybind, InputUtil.Type.KEYSYM, 85, category).build());
+        KeyBindingRegistryImpl.INSTANCE.register(hide = FabricKeyBinding.Builder.create(hideKeybind, InputUtil.Type.KEYSYM, 79, category).build());
+        KeyBindingRegistryImpl.INSTANCE.register(previousPage = FabricKeyBinding.Builder.create(previousPageKeybind, InputUtil.Type.KEYSYM, -1, category).build());
+        KeyBindingRegistryImpl.INSTANCE.register(nextPage = FabricKeyBinding.Builder.create(nextPageKeybind, InputUtil.Type.KEYSYM, -1, category).build());
     }
     
 }

+ 10 - 17
src/main/java/me/shedaniel/rei/client/ConfigObject.java

@@ -11,8 +11,7 @@ public class ConfigObject {
     @Comment("The ordering of the items on the item panel.")
     public ItemListOrdering itemListOrdering = ItemListOrdering.registry;
     
-    @Comment("The ordering of the items on the item panel.")
-    public boolean isAscending = true;
+    @Comment("The ordering of the items on the item panel.") public boolean isAscending = true;
     
     @Comment("To toggle the craftable button next to the search field.")
     public boolean enableCraftableOnlyButton = true;
@@ -23,40 +22,34 @@ public class ConfigObject {
     @Comment("The command used in servers to cheat items")
     public String giveCommand = "/give {player_name} {item_identifier}{nbt} {count}";
     
-    @Comment("The command used to change gamemode")
-    public String gamemodeCommand = "/gamemode {gamemode}";
+    @Comment("The command used to change gamemode") public String gamemodeCommand = "/gamemode {gamemode}";
     
-    @Comment("The command used to change weather")
-    public String weatherCommand = "/weather {weather}";
+    @Comment("The command used to change weather") public String weatherCommand = "/weather {weather}";
     
-    @Comment("True: item panel on the left, false: on the right")
-    public boolean mirrorItemPanel = false;
+    @Comment("True: item panel on the left, false: on the right") public boolean mirrorItemPanel = false;
     
     @Comment("To disable REI's default plugin, don't change this unless you understand what you are doing")
     public boolean loadDefaultPlugin = true;
     
-    @Comment("Maximum recipes viewed at one time.")
-    public int maxRecipePerPage = 3;
+    @Comment("Maximum recipes viewed at one time.") public int maxRecipePerPage = 3;
     
-    @Comment("Toggle utils buttons")
-    public boolean showUtilsButtons = false;
+    @Comment("Toggle utils buttons") public boolean showUtilsButtons = false;
     
-    @Comment("Disable Recipe Book")
-    public boolean disableRecipeBook = false;
+    @Comment("Disable Recipe Book") public boolean disableRecipeBook = false;
     
     public boolean preferVisibleRecipes = false;
     
     //    @Comment("Enable support for old REI plugins which uses registerSpeedCraft")
     //    public boolean enableLegacySpeedCraftSupport = false;
     
-    @Comment("Force enable 2019 REI April Fools' joke")
-    public boolean aprilFoolsFish2019 = false;
+    @Comment("Force enable 2019 REI April Fools' joke") public boolean aprilFoolsFish2019 = false;
     
     public ItemCheatingMode itemCheatingMode = ItemCheatingMode.REI_LIKE;
     
     public boolean lightGrayRecipeBorder = false;
     
-    @Comment("The location of choose page dialog, will automatically be set to your last location so there is no need to change this.")
+    @Comment(
+            "The location of choose page dialog, will automatically be set to your last location so there is no need to change this.")
     public RelativePoint choosePageDialogPoint = new RelativePoint(.5, .5);
     
 }

+ 22 - 18
src/main/java/me/shedaniel/rei/client/DisplayHelperImpl.java

@@ -14,25 +14,21 @@ import java.util.stream.Collectors;
 
 public class DisplayHelperImpl implements DisplayHelper {
     
-    private static final Comparator BOUNDS_HANDLER_COMPARATOR = Comparator.comparingDouble(value -> {
-        if (value instanceof DisplayBoundsHandler)
-            return (double) ((DisplayBoundsHandler) value).getPriority();
-        return -Double.MAX_VALUE;
-    }).reversed();
+    private static final Comparator<DisplayBoundsHandler> BOUNDS_HANDLER_COMPARATOR;
     private static final DisplayBoundsHandler EMPTY = new DisplayBoundsHandler() {
         @Override
         public Class getBaseSupportedClass() {
-            return null;
+            return Object.class;
         }
         
         @Override
         public Rectangle getLeftBounds(Object screen) {
-            return new Rectangle();
+            return DisplayBoundsHandler.EMPTY;
         }
         
         @Override
         public Rectangle getRightBounds(Object screen) {
-            return new Rectangle();
+            return DisplayBoundsHandler.EMPTY;
         }
         
         @Override
@@ -40,30 +36,38 @@ public class DisplayHelperImpl implements DisplayHelper {
             return -10f;
         }
     };
-    private List<DisplayBoundsHandler> screenDisplayBoundsHandlerMap = Lists.newArrayList();
+    
+    static {
+        Comparator<DisplayBoundsHandler> comparator = Comparator.comparingDouble(DisplayBoundsHandler::getPriority);
+        BOUNDS_HANDLER_COMPARATOR = comparator.reversed();
+    }
+    
+    private List<DisplayBoundsHandler> screenDisplayBoundsHandlers = Lists.newArrayList();
     private Map<Class, DisplayBoundsHandler> handlerCache = Maps.newHashMap();
     private BaseBoundsHandler baseBoundsHandler;
     
     @Override
     public List<DisplayBoundsHandler> getSortedBoundsHandlers(Class screenClass) {
-        List<DisplayBoundsHandler> list = Lists.newArrayList(screenDisplayBoundsHandlerMap.stream().filter(handler -> handler.getBaseSupportedClass().isAssignableFrom(screenClass)).collect(Collectors.toList()));
-        list.sort(BOUNDS_HANDLER_COMPARATOR);
-        return list;
+        return screenDisplayBoundsHandlers.stream().filter(handler -> handler.getBaseSupportedClass().isAssignableFrom(screenClass)).sorted(BOUNDS_HANDLER_COMPARATOR).collect(Collectors.toList());
+    }
+    
+    @Override
+    public List<DisplayBoundsHandler> getAllBoundsHandlers() {
+        return screenDisplayBoundsHandlers;
     }
     
     @Override
     public DisplayBoundsHandler getResponsibleBoundsHandler(Class screenClass) {
-        Optional<DisplayBoundsHandler> handler = handlerCache.entrySet().stream().filter(entry -> entry.getKey().equals(screenClass)).map(Map.Entry::getValue).findAny();
-        if (handler.isPresent())
-            return handler.get();
-        List<DisplayBoundsHandler> sortedBoundsHandlers = getSortedBoundsHandlers(screenClass);
-        handlerCache.put(screenClass, sortedBoundsHandlers.isEmpty() ? EMPTY : sortedBoundsHandlers.get(0));
+        Optional<DisplayBoundsHandler> any = handlerCache.entrySet().stream().filter(entry -> entry.getKey().equals(screenClass)).map(Map.Entry::getValue).findAny();
+        if (any.isPresent())
+            return any.get();
+        handlerCache.put(screenClass, screenDisplayBoundsHandlers.stream().filter(handler -> handler.getBaseSupportedClass().isAssignableFrom(screenClass)).sorted(BOUNDS_HANDLER_COMPARATOR).findAny().orElse(EMPTY));
         return handlerCache.get(screenClass);
     }
     
     @Override
     public void registerBoundsHandler(DisplayBoundsHandler handler) {
-        screenDisplayBoundsHandlerMap.add(handler);
+        screenDisplayBoundsHandlers.add(handler);
     }
     
     @Override

+ 3 - 1
src/main/java/me/shedaniel/rei/client/ItemListOrdering.java

@@ -2,7 +2,9 @@ package me.shedaniel.rei.client;
 
 public enum ItemListOrdering {
     
-    registry("ordering.rei.registry"), name("ordering.rei.name"), item_groups("ordering.rei.item_groups");
+    registry("ordering.rei.registry"),
+    name("ordering.rei.name"),
+    item_groups("ordering.rei.item_groups");
     
     private String nameTranslationKey;
     

+ 27 - 10
src/main/java/me/shedaniel/rei/client/RecipeHelperImpl.java

@@ -5,6 +5,7 @@ import com.google.common.collect.Maps;
 import me.shedaniel.rei.RoughlyEnoughItemsCore;
 import me.shedaniel.rei.api.*;
 import net.minecraft.item.ItemStack;
+import net.minecraft.recipe.Recipe;
 import net.minecraft.recipe.RecipeManager;
 import net.minecraft.util.Identifier;
 
@@ -16,11 +17,20 @@ import java.util.stream.Collectors;
 
 public class RecipeHelperImpl implements RecipeHelper {
     
-    private static final Comparator VISIBILITY_HANDLER_COMPARATOR = Comparator.comparingDouble(value -> {
-        if (value instanceof DisplayVisibilityHandler)
-            return (double) ((DisplayVisibilityHandler) value).getPriority();
-        return -Double.MAX_VALUE;
-    }).reversed();
+    private static final Comparator<DisplayVisibilityHandler> VISIBILITY_HANDLER_COMPARATOR;
+    private static final Comparator<Recipe> RECIPE_COMPARATOR = (o1, o2) -> {
+        int int_1 = o1.getId().getNamespace().compareTo(o2.getId().getNamespace());
+        if (int_1 == 0)
+            int_1 = o1.getId().getPath().compareTo(o2.getId().getPath());
+        return int_1;
+    };
+    
+    static {
+        Comparator<DisplayVisibilityHandler> comparator = Comparator.comparingDouble(DisplayVisibilityHandler::getPriority);
+        VISIBILITY_HANDLER_COMPARATOR = comparator.reversed();
+    }
+    
+    private final List<Recipe> sortedRecipes = new ArrayList<>();
     private final AtomicInteger recipeCount = new AtomicInteger();
     private final Map<Identifier, List<RecipeDisplay>> recipeCategoryListMap = Maps.newHashMap();
     private final Map<Identifier, DisplaySettings> categoryDisplaySettingsMap = Maps.newHashMap();
@@ -184,6 +194,7 @@ public class RecipeHelperImpl implements RecipeHelper {
         this.speedCraftFunctionalMap.clear();
         this.categoryDisplaySettingsMap.clear();
         this.displayVisibilityHandlers.clear();
+        this.sortedRecipes.clear();
         ((DisplayHelperImpl) RoughlyEnoughItemsCore.getDisplayHelper()).resetCache();
         BaseBoundsHandler baseBoundsHandler = new BaseBoundsHandlerImpl();
         RoughlyEnoughItemsCore.getDisplayHelper().registerBoundsHandler(baseBoundsHandler);
@@ -214,7 +225,7 @@ public class RecipeHelperImpl implements RecipeHelper {
                 RoughlyEnoughItemsCore.LOGGER.error("[REI] %s plugin failed to load: %s", identifier.toString(), e.getLocalizedMessage());
             }
         });
-        if (getDisplayVisibilityHandlers().size() == 0)
+        if (getDisplayVisibilityHandlers().isEmpty())
             registerRecipeVisibilityHandler(new DisplayVisibilityHandler() {
                 @Override
                 public DisplayVisibility handleDisplay(RecipeCategory category, RecipeDisplay display) {
@@ -227,7 +238,7 @@ public class RecipeHelperImpl implements RecipeHelper {
                 }
             });
         long usedTime = System.currentTimeMillis() - startTime;
-        RoughlyEnoughItemsCore.LOGGER.info("[REI] Registered %d recipes, %d categories (%s) in %d ms.", recipeCount.get(), categories.size(), String.join(", ", categories.stream().map(RecipeCategory::getCategoryName).collect(Collectors.toList())), usedTime);
+        RoughlyEnoughItemsCore.LOGGER.info("[REI] Registered %d recipes displays, %d bounds handler, %d visibility " + "handlers and %d categories (%s) in %d ms.", recipeCount.get(), RoughlyEnoughItemsCore.getDisplayHelper().getAllBoundsHandlers().size(), getDisplayVisibilityHandlers().size(), categories.size(), String.join(", ", categories.stream().map(RecipeCategory::getCategoryName).collect(Collectors.toList())), usedTime);
     }
     
     @Override
@@ -235,6 +246,13 @@ public class RecipeHelperImpl implements RecipeHelper {
         return recipeCount.get();
     }
     
+    @Override
+    public List<Recipe> getVanillaSortedRecipes() {
+        if (sortedRecipes.isEmpty())
+            sortedRecipes.addAll(getRecipeManager().values().stream().sorted(RECIPE_COMPARATOR).collect(Collectors.toSet()));
+        return sortedRecipes;
+    }
+    
     @Override
     public Map<RecipeCategory, List<RecipeDisplay>> getAllRecipes() {
         Map<RecipeCategory, List<RecipeDisplay>> map = Maps.newLinkedHashMap();
@@ -244,7 +262,7 @@ public class RecipeHelperImpl implements RecipeHelper {
             if (tempMap.containsKey(category.getIdentifier()))
                 map.put(category, tempMap.get(category.getIdentifier()).stream().filter(display -> isDisplayVisible(display, true)).collect(Collectors.toList()));
         });
-        for(RecipeCategory category : Lists.newArrayList(map.keySet()))
+        for(RecipeCategory category : map.keySet())
             if (map.get(category).isEmpty())
                 map.remove(category);
         return map;
@@ -268,8 +286,7 @@ public class RecipeHelperImpl implements RecipeHelper {
     @Override
     public boolean isDisplayVisible(RecipeDisplay display, boolean respectConfig) {
         RecipeCategory category = getCategory(display.getRecipeCategory());
-        List<DisplayVisibilityHandler> list = Lists.newArrayList(getDisplayVisibilityHandlers());
-        list.sort((o1, o2) -> VISIBILITY_HANDLER_COMPARATOR.compare(o1, o2));
+        List<DisplayVisibilityHandler> list = getDisplayVisibilityHandlers().stream().sorted(VISIBILITY_HANDLER_COMPARATOR).collect(Collectors.toList());
         for(DisplayVisibilityHandler displayVisibilityHandler : list) {
             DisplayVisibility visibility = displayVisibilityHandler.handleDisplay(category, display);
             if (visibility != DisplayVisibility.PASS) {

+ 5 - 2
src/main/java/me/shedaniel/rei/client/SearchArgument.java

@@ -1,5 +1,6 @@
 package me.shedaniel.rei.client;
 
+import java.util.Locale;
 import java.util.function.Function;
 import java.util.regex.Pattern;
 
@@ -18,7 +19,7 @@ public class SearchArgument {
     
     public SearchArgument(ArgumentType argumentType, String text, boolean include, boolean autoLowerCase) {
         this.argumentType = argumentType;
-        this.text = autoLowerCase ? text.toLowerCase() : text;
+        this.text = autoLowerCase ? text.toLowerCase(Locale.ROOT) : text;
         this.include = include;
     }
     
@@ -44,7 +45,9 @@ public class SearchArgument {
     }
     
     public enum ArgumentType {
-        TEXT, MOD, TOOLTIP
+        TEXT,
+        MOD,
+        TOOLTIP
     }
     
 }

+ 3 - 1
src/main/java/me/shedaniel/rei/client/Weather.java

@@ -1,7 +1,9 @@
 package me.shedaniel.rei.client;
 
 public enum Weather {
-    CLEAR(0, "text.rei.weather.clear"), RAIN(1, "text.rei.weather.rain"), THUNDER(2, "text.rei.weather.thunder");
+    CLEAR(0, "text.rei.weather.clear"),
+    RAIN(1, "text.rei.weather.rain"),
+    THUNDER(2, "text.rei.weather.thunder");
     
     private final int id;
     private final String translateKey;

+ 32 - 18
src/main/java/me/shedaniel/rei/gui/ContainerScreenOverlay.java

@@ -4,8 +4,8 @@ import com.google.common.collect.Lists;
 import com.mojang.blaze3d.platform.GlStateManager;
 import me.shedaniel.cloth.api.ClientUtils;
 import me.shedaniel.rei.RoughlyEnoughItemsCore;
+import me.shedaniel.rei.api.ClientHelper;
 import me.shedaniel.rei.api.DisplayHelper;
-import me.shedaniel.rei.client.ClientHelper;
 import me.shedaniel.rei.client.ScreenHelper;
 import me.shedaniel.rei.client.Weather;
 import me.shedaniel.rei.gui.widget.*;
@@ -21,20 +21,19 @@ import net.minecraft.container.Slot;
 import net.minecraft.item.ItemStack;
 import net.minecraft.sound.SoundEvents;
 import net.minecraft.text.TranslatableTextComponent;
+import net.minecraft.util.ActionResult;
 import net.minecraft.util.Identifier;
 import net.minecraft.util.math.MathHelper;
 import net.minecraft.world.GameMode;
 
 import java.awt.*;
-import java.util.LinkedList;
+import java.util.*;
 import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
 import java.util.stream.Collectors;
 
 public class ContainerScreenOverlay extends AbstractParentElement implements Drawable {
     
-    private static final Identifier CHEST_GUI_TEXTURE = new Identifier("roughlyenoughitems", "textures/gui/recipecontainer.png");
+    private static final Identifier CHEST_GUI_TEXTURE = new Identifier("roughlyenoughitems", "textures/gui" + "/recipecontainer.png");
     private static final List<QueuedTooltip> QUEUED_TOOLTIPS = Lists.newArrayList();
     public static String searchTerm = "";
     private static int page = 0;
@@ -110,7 +109,7 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
             @Override
             public void onPressed() {
                 if (Screen.hasShiftDown()) {
-                    ClientHelper.setCheating(!ClientHelper.isCheating());
+                    ClientHelper.getInstance().setCheating(!ClientHelper.getInstance().isCheating());
                     return;
                 }
                 RoughlyEnoughItemsCore.getConfigManager().openConfigScreen(ScreenHelper.getLastContainerScreen());
@@ -120,7 +119,7 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
             public void render(int mouseX, int mouseY, float delta) {
                 super.render(mouseX, mouseY, delta);
                 GuiLighting.disable();
-                if (ClientHelper.isCheating() && RoughlyEnoughItemsCore.hasOperatorPermission()) {
+                if (ClientHelper.getInstance().isCheating() && RoughlyEnoughItemsCore.hasOperatorPermission()) {
                     if (RoughlyEnoughItemsCore.hasPermissionToUsePackets())
                         fill(getBounds().x, getBounds().y, getBounds().x + 20, getBounds().y + 20, 721354752);
                     else
@@ -135,7 +134,7 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
             public Optional<String> getTooltips() {
                 String tooltips = I18n.translate("text.rei.config_tooltip");
                 tooltips += "\n  ";
-                if (!ClientHelper.isCheating())
+                if (!ClientHelper.getInstance().isCheating())
                     tooltips += "\n" + I18n.translate("text.rei.cheating_disabled");
                 else if (!RoughlyEnoughItemsCore.hasOperatorPermission())
                     tooltips += "\n" + I18n.translate("text.rei.cheating_enabled_no_perms");
@@ -177,7 +176,7 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
             widgets.add(new ButtonWidget(RoughlyEnoughItemsCore.getConfigManager().getConfig().mirrorItemPanel ? window.getScaledWidth() - 80 : 60, 10, 20, 20, "") {
                 @Override
                 public void onPressed() {
-                    MinecraftClient.getInstance().player.sendChatMessage(RoughlyEnoughItemsCore.getConfigManager().getConfig().weatherCommand.replaceAll("\\{weather}", getNextWeather().name().toLowerCase()));
+                    MinecraftClient.getInstance().player.sendChatMessage(RoughlyEnoughItemsCore.getConfigManager().getConfig().weatherCommand.replaceAll("\\{weather}", getNextWeather().name().toLowerCase(Locale.ROOT)));
                 }
                 
                 @Override
@@ -318,7 +317,7 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
     }
     
     private String getCheatModeText() {
-        return I18n.translate(String.format("%s%s", "text.rei.", ClientHelper.isCheating() ? "cheat" : "nocheat"));
+        return I18n.translate(String.format("%s%s", "text.rei.", ClientHelper.getInstance().isCheating() ? "cheat" : "nocheat"));
     }
     
     public Rectangle getRectangle() {
@@ -327,11 +326,11 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
     
     @Override
     public void render(int mouseX, int mouseY, float delta) {
-        List<ItemStack> currentStacks = ClientHelper.getInventoryItemsTypes();
+        List<ItemStack> currentStacks = ClientHelper.getInstance().getInventoryItemsTypes();
         if (RoughlyEnoughItemsCore.getDisplayHelper().getBaseBoundsHandler() != null && RoughlyEnoughItemsCore.getDisplayHelper().getBaseBoundsHandler().shouldRecalculateArea(!RoughlyEnoughItemsCore.getConfigManager().getConfig().mirrorItemPanel, rectangle))
             init(true);
         else if (RoughlyEnoughItemsCore.getConfigManager().isCraftableOnlyEnabled() && (!hasSameListContent(new LinkedList<>(ScreenHelper.inventoryStacks), currentStacks) || (currentStacks.size() != ScreenHelper.inventoryStacks.size()))) {
-            ScreenHelper.inventoryStacks = ClientHelper.getInventoryItemsTypes();
+            ScreenHelper.inventoryStacks = ClientHelper.getInstance().getInventoryItemsTypes();
             DisplayHelper.DisplayBoundsHandler boundsHandler = RoughlyEnoughItemsCore.getDisplayHelper().getResponsibleBoundsHandler(MinecraftClient.getInstance().currentScreen.getClass());
             itemListOverlay.updateList(boundsHandler, boundsHandler.getItemListArea(rectangle), page, searchTerm, true);
         }
@@ -446,7 +445,7 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
     public boolean mouseScrolled(double i, double j, double amount) {
         if (!ScreenHelper.isOverlayVisible())
             return false;
-        if (rectangle.contains(ClientUtils.getMouseLocation())) {
+        if (isInside(ClientUtils.getMouseLocation())) {
             if (amount > 0 && buttonLeft.enabled)
                 buttonLeft.onPressed();
             else if (amount < 0 && buttonRight.enabled)
@@ -467,7 +466,7 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
             for(Element listener : widgets)
                 if (listener.keyPressed(int_1, int_2, int_3))
                     return true;
-        if (ClientHelper.HIDE.matchesKey(int_1, int_2)) {
+        if (ClientHelper.getInstance().getHideKeyBinding().matchesKey(int_1, int_2)) {
             ScreenHelper.toggleOverlayVisible();
             return true;
         }
@@ -478,10 +477,10 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
             if (ScreenHelper.getLastContainerScreenHooks().rei_getHoveredSlot() != null && !ScreenHelper.getLastContainerScreenHooks().rei_getHoveredSlot().getStack().isEmpty())
                 itemStack = ScreenHelper.getLastContainerScreenHooks().rei_getHoveredSlot().getStack();
         if (itemStack != null && !itemStack.isEmpty()) {
-            if (ClientHelper.RECIPE.matchesKey(int_1, int_2))
-                return ClientHelper.executeRecipeKeyBind(itemStack);
-            else if (ClientHelper.USAGE.matchesKey(int_1, int_2))
-                return ClientHelper.executeUsageKeyBind(itemStack);
+            if (ClientHelper.getInstance().getRecipeKeyBinding().matchesKey(int_1, int_2))
+                return ClientHelper.getInstance().executeRecipeKeyBind(itemStack);
+            else if (ClientHelper.getInstance().getUsageKeyBinding().matchesKey(int_1, int_2))
+                return ClientHelper.getInstance().executeUsageKeyBind(itemStack);
         }
         return false;
     }
@@ -515,4 +514,19 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
         return false;
     }
     
+    public boolean isInside(double mouseX, double mouseY) {
+        if (!rectangle.contains(mouseX, mouseY))
+            return false;
+        for(DisplayHelper.DisplayBoundsHandler handler : RoughlyEnoughItemsCore.getDisplayHelper().getSortedBoundsHandlers(MinecraftClient.getInstance().currentScreen.getClass())) {
+            ActionResult in = handler.isInZone(!RoughlyEnoughItemsCore.getConfigManager().getConfig().mirrorItemPanel, mouseX, mouseY);
+            if (in != ActionResult.PASS)
+                return in == ActionResult.SUCCESS;
+        }
+        return true;
+    }
+    
+    public boolean isInside(Point point) {
+        return isInside(point.getX(), point.getY());
+    }
+    
 }

+ 12 - 4
src/main/java/me/shedaniel/rei/gui/RecipeViewingScreen.java

@@ -5,7 +5,6 @@ import com.mojang.blaze3d.platform.GlStateManager;
 import me.shedaniel.cloth.api.ClientUtils;
 import me.shedaniel.rei.RoughlyEnoughItemsCore;
 import me.shedaniel.rei.api.*;
-import me.shedaniel.rei.client.ClientHelper;
 import me.shedaniel.rei.client.ScreenHelper;
 import me.shedaniel.rei.gui.widget.*;
 import net.minecraft.client.MinecraftClient;
@@ -31,9 +30,9 @@ import java.util.function.Supplier;
 
 public class RecipeViewingScreen extends Screen {
     
-    public static final Identifier CHEST_GUI_TEXTURE = new Identifier("roughlyenoughitems", "textures/gui/recipecontainer.png");
+    public static final Identifier CHEST_GUI_TEXTURE = new Identifier("roughlyenoughitems", "textures/gui" + "/recipecontainer.png");
     public static final Color SUB_COLOR = new Color(159, 159, 159);
-    private static final Identifier CREATIVE_INVENTORY_TABS = new Identifier("textures/gui/container/creative_inventory/tabs.png");
+    private static final Identifier CREATIVE_INVENTORY_TABS = new Identifier("textures/gui/container" + "/creative_inventory/tabs.png");
     private final List<Widget> widgets;
     private final List<TabWidget> tabs;
     private final Map<RecipeCategory, List<RecipeDisplay>> categoriesMap;
@@ -94,6 +93,15 @@ public class RecipeViewingScreen extends Screen {
         }
         if (choosePageActivated)
             return recipeChoosePageWidget.keyPressed(int_1, int_2, int_3);
+        else if (ClientHelper.getInstance().getNextPageKeyBinding().matchesKey(int_1, int_2)) {
+            if (recipeNext.enabled)
+                recipeNext.onPressed();
+            return recipeNext.enabled;
+        } else if (ClientHelper.getInstance().getPreviousPageKeyBinding().matchesKey(int_1, int_2)) {
+            if (recipeBack.enabled)
+                recipeBack.onPressed();
+            return recipeBack.enabled;
+        }
         for(Element element : children())
             if (element.keyPressed(int_1, int_2, int_3))
                 return true;
@@ -151,7 +159,7 @@ public class RecipeViewingScreen extends Screen {
             @Override
             public void onLabelClicked() {
                 MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
-                ClientHelper.executeViewAllRecipesKeyBind();
+                ClientHelper.getInstance().executeViewAllRecipesKeyBind();
             }
         });
         widgets.add(categoryNext = new ButtonWidget((int) bounds.getMaxX() - 17, (int) bounds.getY() + 5, 12, 12, new TranslatableTextComponent("text.rei.right_arrow")) {

+ 6 - 1
src/main/java/me/shedaniel/rei/gui/config/ItemListOrderingConfig.java

@@ -4,7 +4,12 @@ import me.shedaniel.rei.client.ItemListOrdering;
 import net.minecraft.client.resource.language.I18n;
 
 public enum ItemListOrderingConfig {
-    REGISTRY_ASCENDING(ItemListOrdering.registry, true), NAME_ASCENDING(ItemListOrdering.name, true), GROUPS_ASCENDING(ItemListOrdering.item_groups, true), REGISTRY_DESCENDING(ItemListOrdering.registry, false), NAME_DESCENDING(ItemListOrdering.name, false), GROUPS_DESCENDING(ItemListOrdering.item_groups, false);
+    REGISTRY_ASCENDING(ItemListOrdering.registry, true),
+    NAME_ASCENDING(ItemListOrdering.name, true),
+    GROUPS_ASCENDING(ItemListOrdering.item_groups, true),
+    REGISTRY_DESCENDING(ItemListOrdering.registry, false),
+    NAME_DESCENDING(ItemListOrdering.name, false),
+    GROUPS_DESCENDING(ItemListOrdering.item_groups, false);
     
     private ItemListOrdering ordering;
     private boolean isAscending;

+ 17 - 20
src/main/java/me/shedaniel/rei/gui/widget/ItemListOverlay.java

@@ -3,10 +3,10 @@ package me.shedaniel.rei.gui.widget;
 import com.google.common.collect.Lists;
 import me.shedaniel.cloth.api.ClientUtils;
 import me.shedaniel.rei.RoughlyEnoughItemsCore;
+import me.shedaniel.rei.api.ClientHelper;
 import me.shedaniel.rei.api.DisplayHelper;
 import me.shedaniel.rei.api.ItemCheatingMode;
 import me.shedaniel.rei.api.RecipeHelper;
-import me.shedaniel.rei.client.ClientHelper;
 import me.shedaniel.rei.client.ItemListOrdering;
 import me.shedaniel.rei.client.ScreenHelper;
 import me.shedaniel.rei.client.SearchArgument;
@@ -28,6 +28,7 @@ import java.awt.*;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Locale;
 import java.util.stream.Collectors;
 
 public class ItemListOverlay extends Widget {
@@ -82,7 +83,7 @@ public class ItemListOverlay extends Widget {
         GuiLighting.disable();
         widgets.forEach(widget -> widget.render(int_1, int_2, float_1));
         ClientPlayerEntity player = minecraft.player;
-        if (rectangle.contains(ClientUtils.getMouseLocation()) && ClientHelper.isCheating() && !player.inventory.getCursorStack().isEmpty() && RoughlyEnoughItemsCore.hasPermissionToUsePackets())
+        if (rectangle.contains(ClientUtils.getMouseLocation()) && ClientHelper.getInstance().isCheating() && !player.inventory.getCursorStack().isEmpty() && RoughlyEnoughItemsCore.hasPermissionToUsePackets())
             ScreenHelper.getLastOverlay().addTooltip(QueuedTooltip.create(I18n.translate("text.rei.delete_items")));
     }
     
@@ -112,14 +113,14 @@ public class ItemListOverlay extends Widget {
                     @Override
                     protected void queueTooltip(ItemStack itemStack, float delta) {
                         ClientPlayerEntity player = minecraft.player;
-                        if (!ClientHelper.isCheating() || player.inventory.getCursorStack().isEmpty())
+                        if (!ClientHelper.getInstance().isCheating() || player.inventory.getCursorStack().isEmpty())
                             super.queueTooltip(itemStack, delta);
                     }
                     
                     @Override
                     public boolean mouseClicked(double mouseX, double mouseY, int button) {
                         if (isHighlighted(mouseX, mouseY)) {
-                            if (ClientHelper.isCheating()) {
+                            if (ClientHelper.getInstance().isCheating()) {
                                 if (getCurrentStack() != null && !getCurrentStack().isEmpty()) {
                                     ItemStack cheatedStack = getCurrentStack().copy();
                                     if (RoughlyEnoughItemsCore.getConfigManager().getConfig().itemCheatingMode == ItemCheatingMode.REI_LIKE)
@@ -128,12 +129,12 @@ public class ItemListOverlay extends Widget {
                                         cheatedStack.setAmount(button != 0 ? 1 : cheatedStack.getMaxAmount());
                                     else
                                         cheatedStack.setAmount(1);
-                                    return ClientHelper.tryCheatingStack(cheatedStack);
+                                    return ClientHelper.getInstance().tryCheatingStack(cheatedStack);
                                 }
                             } else if (button == 0)
-                                return ClientHelper.executeRecipeKeyBind(getCurrentStack().copy());
+                                return ClientHelper.getInstance().executeRecipeKeyBind(getCurrentStack().copy());
                             else if (button == 1)
-                                return ClientHelper.executeUsageKeyBind(getCurrentStack().copy());
+                                return ClientHelper.getInstance().executeUsageKeyBind(getCurrentStack().copy());
                         }
                         return false;
                     }
@@ -161,16 +162,12 @@ public class ItemListOverlay extends Widget {
     }
     
     public boolean canBeFit(int left, int top, Rectangle listArea) {
-        List<DisplayHelper.DisplayBoundsHandler> sortedBoundsHandlers = RoughlyEnoughItemsCore.getDisplayHelper().getSortedBoundsHandlers(minecraft.currentScreen.getClass());
-        ActionResult result = ActionResult.SUCCESS;
-        for(DisplayHelper.DisplayBoundsHandler sortedBoundsHandler : sortedBoundsHandlers) {
+        for(DisplayHelper.DisplayBoundsHandler sortedBoundsHandler : RoughlyEnoughItemsCore.getDisplayHelper().getSortedBoundsHandlers(minecraft.currentScreen.getClass())) {
             ActionResult fit = sortedBoundsHandler.canItemSlotWidgetFit(!RoughlyEnoughItemsCore.getConfigManager().getConfig().mirrorItemPanel, left, top, minecraft.currentScreen, listArea);
-            if (fit != ActionResult.PASS) {
-                result = fit;
-                break;
-            }
+            if (fit != ActionResult.PASS)
+                return fit == ActionResult.SUCCESS;
         }
-        return result == ActionResult.SUCCESS;
+        return true;
     }
     
     @Override
@@ -247,9 +244,9 @@ public class ItemListOverlay extends Widget {
     private boolean filterItem(ItemStack itemStack, SearchArgument... arguments) {
         if (arguments.length == 0)
             return true;
-        String mod = ClientHelper.getModFromItem(itemStack.getItem()).toLowerCase();
-        String tooltips = tryGetItemStackToolTip(itemStack, false).stream().skip(1).collect(Collectors.joining("")).replace(SPACE, EMPTY).toLowerCase();
-        String name = tryGetItemStackName(itemStack).replace(SPACE, EMPTY).toLowerCase();
+        String mod = ClientHelper.getInstance().getModFromItem(itemStack.getItem()).toLowerCase(Locale.ROOT);
+        String tooltips = tryGetItemStackToolTip(itemStack, false).stream().skip(1).collect(Collectors.joining("")).replace(SPACE, EMPTY).toLowerCase(Locale.ROOT);
+        String name = tryGetItemStackName(itemStack).replace(SPACE, EMPTY).toLowerCase(Locale.ROOT);
         for(SearchArgument argument : arguments) {
             if (argument.getArgumentType().equals(SearchArgument.ArgumentType.MOD))
                 if (SearchArgument.getFunction(!argument.isInclude()).apply(mod.indexOf(argument.getText())))
@@ -286,8 +283,8 @@ public class ItemListOverlay extends Widget {
     public boolean mouseClicked(double double_1, double double_2, int int_1) {
         if (rectangle.contains(double_1, double_2)) {
             ClientPlayerEntity player = minecraft.player;
-            if (ClientHelper.isCheating() && !player.inventory.getCursorStack().isEmpty() && RoughlyEnoughItemsCore.hasPermissionToUsePackets()) {
-                ClientHelper.sendDeletePacket();
+            if (ClientHelper.getInstance().isCheating() && !player.inventory.getCursorStack().isEmpty() && RoughlyEnoughItemsCore.hasPermissionToUsePackets()) {
+                ClientHelper.getInstance().sendDeletePacket();
                 return true;
             }
             if (!player.inventory.getCursorStack().isEmpty() && RoughlyEnoughItemsCore.hasPermissionToUsePackets())

+ 8 - 8
src/main/java/me/shedaniel/rei/gui/widget/ItemSlotWidget.java

@@ -4,7 +4,7 @@ import com.google.common.collect.Lists;
 import com.mojang.blaze3d.platform.GlStateManager;
 import me.shedaniel.cloth.api.ClientUtils;
 import me.shedaniel.rei.RoughlyEnoughItemsCore;
-import me.shedaniel.rei.client.ClientHelper;
+import me.shedaniel.rei.api.ClientHelper;
 import me.shedaniel.rei.client.ScreenHelper;
 import net.minecraft.client.gui.Element;
 import net.minecraft.client.render.GuiLighting;
@@ -94,7 +94,7 @@ public class ItemSlotWidget extends HighlightableWidget {
     }
     
     protected List<String> getTooltip(ItemStack itemStack) {
-        final String modString = ClientHelper.getFormattedModFromItem(itemStack.getItem());
+        final String modString = ClientHelper.getInstance().getFormattedModFromItem(itemStack.getItem());
         List<String> toolTip = Lists.newArrayList(ItemListOverlay.tryGetItemStackToolTip(itemStack, true));
         toolTip.addAll(getExtraToolTips(itemStack));
         boolean alreadyHasMod = false;
@@ -137,9 +137,9 @@ public class ItemSlotWidget extends HighlightableWidget {
             return false;
         if (getBounds().contains(mouseX, mouseY))
             if (button == 0)
-                return ClientHelper.executeRecipeKeyBind(getCurrentStack());
+                return ClientHelper.getInstance().executeRecipeKeyBind(getCurrentStack());
             else if (button == 1)
-                return ClientHelper.executeUsageKeyBind(getCurrentStack());
+                return ClientHelper.getInstance().executeUsageKeyBind(getCurrentStack());
         return false;
     }
     
@@ -148,10 +148,10 @@ public class ItemSlotWidget extends HighlightableWidget {
         if (!clickToMoreRecipes)
             return false;
         if (getBounds().contains(ClientUtils.getMouseLocation()))
-            if (ClientHelper.RECIPE.matchesKey(int_1, int_2))
-                return ClientHelper.executeRecipeKeyBind(getCurrentStack());
-            else if (ClientHelper.USAGE.matchesKey(int_1, int_2))
-                return ClientHelper.executeUsageKeyBind(getCurrentStack());
+            if (ClientHelper.getInstance().getRecipeKeyBinding().matchesKey(int_1, int_2))
+                return ClientHelper.getInstance().executeRecipeKeyBind(getCurrentStack());
+            else if (ClientHelper.getInstance().getUsageKeyBinding().matchesKey(int_1, int_2))
+                return ClientHelper.getInstance().executeUsageKeyBind(getCurrentStack());
         return false;
     }
     

+ 0 - 1
src/main/java/me/shedaniel/rei/gui/widget/LabelWidget.java

@@ -1,6 +1,5 @@
 package me.shedaniel.rei.gui.widget;
 
-import net.minecraft.client.font.TextRenderer;
 import net.minecraft.client.gui.Element;
 
 import java.awt.*;

+ 11 - 0
src/main/java/me/shedaniel/rei/gui/widget/SearchFieldWidget.java

@@ -4,6 +4,7 @@ import com.mojang.blaze3d.platform.GlStateManager;
 import net.minecraft.client.audio.PositionedSoundInstance;
 import net.minecraft.client.render.GuiLighting;
 import net.minecraft.sound.SoundEvents;
+import org.lwjgl.glfw.GLFW;
 
 public class SearchFieldWidget extends TextFieldWidget {
     
@@ -50,6 +51,16 @@ public class SearchFieldWidget extends TextFieldWidget {
         return super.mouseClicked(double_1, double_2, int_1);
     }
     
+    @Override
+    public boolean keyPressed(int int_1, int int_2, int int_3) {
+        if (this.isVisible() && this.isFocused())
+            if (int_1 == GLFW.GLFW_KEY_ENTER || int_1 == GLFW.GLFW_KEY_KP_ENTER) {
+                setFocused(false);
+                return true;
+            }
+        return super.keyPressed(int_1, int_2, int_3);
+    }
+    
     @Override
     public void render(int int_1, int int_2, float float_1) {
     }

+ 5 - 10
src/main/java/me/shedaniel/rei/mixin/MixinContainerScreen.java

@@ -9,16 +9,11 @@ import org.spongepowered.asm.mixin.Shadow;
 @Mixin(ContainerScreen.class)
 public class MixinContainerScreen implements ContainerScreenHooks {
     
-    @Shadow
-    protected int left;
-    @Shadow
-    protected int top;
-    @Shadow
-    protected int containerWidth;
-    @Shadow
-    protected int containerHeight;
-    @Shadow
-    protected Slot focusedSlot;
+    @Shadow protected int left;
+    @Shadow protected int top;
+    @Shadow protected int containerWidth;
+    @Shadow protected int containerHeight;
+    @Shadow protected Slot focusedSlot;
     
     @Override
     public int rei_getContainerLeft() {

+ 1 - 2
src/main/java/me/shedaniel/rei/mixin/MixinCreativePlayerInventoryScreen.java

@@ -7,8 +7,7 @@ import org.spongepowered.asm.mixin.Shadow;
 
 @Mixin(CreativePlayerInventoryScreen.class)
 public class MixinCreativePlayerInventoryScreen implements CreativePlayerInventoryScreenHooks {
-    @Shadow
-    private static int selectedTab;
+    @Shadow private static int selectedTab;
     
     @Override
     public int rei_getSelectedTab() {

+ 3 - 8
src/main/java/me/shedaniel/rei/mixin/MixinRecipeBookGui.java

@@ -14,16 +14,11 @@ import java.util.List;
 @Mixin(RecipeBookGui.class)
 public class MixinRecipeBookGui implements RecipeBookGuiHooks {
     
-    @Shadow
-    @Final
-    protected RecipeBookGhostSlots ghostSlots;
+    @Shadow @Final protected RecipeBookGhostSlots ghostSlots;
     
-    @Shadow
-    private TextFieldWidget searchField;
+    @Shadow private TextFieldWidget searchField;
     
-    @Shadow
-    @Final
-    private List<GroupButtonWidget> tabButtons;
+    @Shadow @Final private List<GroupButtonWidget> tabButtons;
     
     public RecipeBookGhostSlots rei_getGhostSlots() {
         return ghostSlots;

+ 1 - 8
src/main/java/me/shedaniel/rei/plugin/DefaultPlugin.java

@@ -104,14 +104,7 @@ public class DefaultPlugin implements REIPluginEntry {
     
     @Override
     public void registerRecipeDisplays(RecipeHelper recipeHelper) {
-        List<Recipe> values = Lists.newLinkedList(recipeHelper.getRecipeManager().values());
-        values.sort((o1, o2) -> {
-            int int_1 = o1.getId().getNamespace().compareTo(o2.getId().getNamespace());
-            if (int_1 == 0)
-                int_1 = o1.getId().getPath().compareTo(o2.getId().getPath());
-            return int_1;
-        });
-        for(Recipe recipe : values)
+        for(Recipe recipe : recipeHelper.getVanillaSortedRecipes())
             if (recipe instanceof ShapelessRecipe)
                 recipeHelper.registerDisplay(CRAFTING, new DefaultShapelessDisplay((ShapelessRecipe) recipe));
             else if (recipe instanceof ShapedRecipe)

+ 2 - 1
src/main/java/me/shedaniel/rei/utils/ClothScreenRegistry.java

@@ -16,6 +16,7 @@ import net.minecraft.client.gui.widget.ButtonWidget;
 import net.minecraft.client.resource.language.I18n;
 
 import java.io.IOException;
+import java.util.Locale;
 import java.util.Optional;
 
 public class ClothScreenRegistry {
@@ -62,7 +63,7 @@ public class ClothScreenRegistry {
         appearance.addOption(new BooleanListEntry("text.rei.config.prefer_visible_recipes", RoughlyEnoughItemsCore.getConfigManager().getConfig().preferVisibleRecipes, RESET, () -> false, bool -> RoughlyEnoughItemsCore.getConfigManager().getConfig().preferVisibleRecipes = bool, () -> getConfigTooltip("prefer_visible_recipes")));
         ConfigScreenBuilder.CategoryBuilder action = builder.addCategory("text.rei.config.action");
         action.addOption(new EnumListEntry<>("text.rei.config.item_cheating_mode", ItemCheatingMode.class, RoughlyEnoughItemsCore.getConfigManager().getConfig().itemCheatingMode, RESET, () -> ItemCheatingMode.REI_LIKE, i -> RoughlyEnoughItemsCore.getConfigManager().getConfig().itemCheatingMode = i, e -> {
-            return I18n.translate("text.rei.config.item_cheating_mode." + e.name().toLowerCase());
+            return I18n.translate("text.rei.config.item_cheating_mode." + e.name().toLowerCase(Locale.ROOT));
         }, () -> getConfigTooltip("item_cheating_mode")));
         action.addOption(new StringListEntry("text.rei.give_command", RoughlyEnoughItemsCore.getConfigManager().getConfig().giveCommand, RESET, () -> "/give {player_name} {item_identifier}{nbt} {count}", s -> RoughlyEnoughItemsCore.getConfigManager().getConfig().giveCommand = s, () -> getConfigTooltip("give_command")));
         action.addOption(new StringListEntry("text.rei.gamemode_command", RoughlyEnoughItemsCore.getConfigManager().getConfig().gamemodeCommand, RESET, () -> "/gamemode {gamemode}", s -> RoughlyEnoughItemsCore.getConfigManager().getConfig().gamemodeCommand = s, () -> getConfigTooltip("gamemode_command")));

+ 2 - 0
src/main/resources/assets/roughlyenoughitems/lang/en_us.json

@@ -3,6 +3,8 @@
   "key.roughlyenoughitems.recipe_keybind": "Show Recipe",
   "key.roughlyenoughitems.hide_keybind": "Hide/Show REI",
   "key.roughlyenoughitems.usage_keybind": "Show Uses",
+  "key.roughlyenoughitems.next_page": "Next Page",
+  "key.roughlyenoughitems.previous_page": "Previous Page",
   "text.rei.config.general": "General",
   "text.rei.config.action": "Action",
   "text.rei.config.cheating": "Cheating:",

+ 1 - 1
src/main/resources/fabric.mod.json

@@ -17,7 +17,7 @@
   "entrypoints": {
     "client": [
       "me.shedaniel.rei.RoughlyEnoughItemsCore",
-      "me.shedaniel.rei.client.ClientHelper",
+      "me.shedaniel.rei.client.ClientHelperImpl",
       "me.shedaniel.rei.client.ScreenHelper"
     ],
     "main": [