Browse Source

Merge pull request #86 from shedaniel/1.14-dev

REi v2.9 (WIP)
Daniel She 6 years ago
parent
commit
7bcf60309a
40 changed files with 1158 additions and 244 deletions
  1. 10 0
      CHANGELOG.md
  2. 1 1
      gradle.properties
  3. 38 0
      src/main/java/com/zeitheron/hammercore/client/utils/Scissors.java
  4. 8 0
      src/main/java/me/shedaniel/rei/api/ClientHelper.java
  5. 9 0
      src/main/java/me/shedaniel/rei/api/RecipeCategory.java
  6. 52 0
      src/main/java/me/shedaniel/rei/api/Renderable.java
  7. 13 0
      src/main/java/me/shedaniel/rei/api/Renderer.java
  8. 25 3
      src/main/java/me/shedaniel/rei/client/ClientHelperImpl.java
  9. 2 0
      src/main/java/me/shedaniel/rei/client/ConfigObject.java
  10. 16 0
      src/main/java/me/shedaniel/rei/client/RecipeScreenType.java
  11. 8 14
      src/main/java/me/shedaniel/rei/gui/ContainerScreenOverlay.java
  12. 133 0
      src/main/java/me/shedaniel/rei/gui/PreRecipeViewingScreen.java
  13. 7 8
      src/main/java/me/shedaniel/rei/gui/RecipeViewingScreen.java
  14. 270 0
      src/main/java/me/shedaniel/rei/gui/VillagerRecipeViewingScreen.java
  15. 13 0
      src/main/java/me/shedaniel/rei/gui/renderables/EmptyRenderer.java
  16. 69 0
      src/main/java/me/shedaniel/rei/gui/renderables/ItemStackRenderer.java
  17. 13 0
      src/main/java/me/shedaniel/rei/gui/renderables/RecipeRenderer.java
  18. 87 0
      src/main/java/me/shedaniel/rei/gui/renderables/SimpleRecipeRenderer.java
  19. 2 2
      src/main/java/me/shedaniel/rei/gui/widget/ButtonWidget.java
  20. 5 0
      src/main/java/me/shedaniel/rei/gui/widget/CategoryBaseWidget.java
  21. 10 2
      src/main/java/me/shedaniel/rei/gui/widget/ClickableLabelWidget.java
  22. 6 6
      src/main/java/me/shedaniel/rei/gui/widget/ItemListOverlay.java
  23. 10 143
      src/main/java/me/shedaniel/rei/gui/widget/ItemSlotWidget.java
  24. 12 2
      src/main/java/me/shedaniel/rei/gui/widget/RecipeBaseWidget.java
  25. 26 0
      src/main/java/me/shedaniel/rei/gui/widget/SlotBaseWidget.java
  26. 203 0
      src/main/java/me/shedaniel/rei/gui/widget/SlotWidget.java
  27. 19 24
      src/main/java/me/shedaniel/rei/gui/widget/TabWidget.java
  28. 11 4
      src/main/java/me/shedaniel/rei/plugin/DefaultBlastingCategory.java
  29. 7 7
      src/main/java/me/shedaniel/rei/plugin/DefaultBrewingCategory.java
  30. 3 3
      src/main/java/me/shedaniel/rei/plugin/DefaultCampfireCategory.java
  31. 4 4
      src/main/java/me/shedaniel/rei/plugin/DefaultCraftingCategory.java
  32. 31 8
      src/main/java/me/shedaniel/rei/plugin/DefaultPlugin.java
  33. 11 4
      src/main/java/me/shedaniel/rei/plugin/DefaultSmeltingCategory.java
  34. 11 4
      src/main/java/me/shedaniel/rei/plugin/DefaultSmokingCategory.java
  35. 3 3
      src/main/java/me/shedaniel/rei/plugin/DefaultStoneCuttingCategory.java
  36. 2 0
      src/main/java/me/shedaniel/rei/utils/ClothScreenRegistry.java
  37. 7 1
      src/main/resources/assets/roughlyenoughitems/lang/en_us.json
  38. BIN
      src/main/resources/assets/roughlyenoughitems/textures/gui/recipecontainer.png
  39. BIN
      src/main/resources/assets/roughlyenoughitems/textures/gui/screenshot.png
  40. 1 1
      src/main/resources/fabric.mod.json

+ 10 - 0
CHANGELOG.md

@@ -1,4 +1,14 @@
 View full changelog [here](https://github.com/shedaniel/RoughlyEnoughItems/blob/1.14/CHANGELOG.md).
 View full changelog [here](https://github.com/shedaniel/RoughlyEnoughItems/blob/1.14/CHANGELOG.md).
+## v2.9-beta+build.106 (BETA)
+- Using: [HammerLib](https://minecraft.curseforge.com/projects/hammer-lib) as a simple opengl scissors api
+- New: Mod Name of category new shows in category tooltips
+- New: Renderer API
+- New: Villager Trading Like Recipe Screen. ![image](https://cdn.discordapp.com/attachments/432055962233470988/576077676305973275/unknown.png)
+- New: Recipe Screen Selection Screen
+- Removed: All `Locale.ROOT` usage
+- Fixed: Button Width Rendering issues when the width is an odd number
+- Changed: Mod Name changed from `RoughlyEnoughItems` to `Roughly Enough Items`
+- Changed: Lots of internal refractors
 ## v2.8.2+build.104
 ## v2.8.2+build.104
 - Fixed [#81](https://github.com/shedaniel/RoughlyEnoughItems/issues/81): Scrolling unaffected by exclusion zones.
 - 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 [#82](https://github.com/shedaniel/RoughlyEnoughItems/issues/82): Close search after pressing "Enter"

+ 1 - 1
gradle.properties

@@ -1,4 +1,4 @@
-mod_version=2.8.2+build.105
+mod_version=2.9-beta+build.106
 minecraft_version=1.14
 minecraft_version=1.14
 yarn_version=1.14+build.5
 yarn_version=1.14+build.5
 fabricloader_version=0.4.6+build.141
 fabricloader_version=0.4.6+build.141

+ 38 - 0
src/main/java/com/zeitheron/hammercore/client/utils/Scissors.java

@@ -0,0 +1,38 @@
+package com.zeitheron.hammercore.client.utils;
+
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.util.Window;
+import org.lwjgl.opengl.GL11;
+
+/**
+ * This is originally the part of Hammer Lib, repacked in REI with permission.
+ * Adapted GL scissor for minecraft pixel resolution and adjusts (0;0) as left-top corner.
+ *
+ * @author Zeitheron
+ */
+public class Scissors {
+    public static void begin() {
+        GL11.glEnable(GL11.GL_SCISSOR_TEST);
+    }
+    
+    public static void scissor(int x, int y, int width, int height) {
+        Window window = MinecraftClient.getInstance().window;
+        
+        int sw = window.getWidth();
+        int sh = window.getHeight();
+        float dw = window.getScaledWidth();
+        float dh = window.getScaledHeight();
+        
+        x = Math.round(sw * (x / dw));
+        y = Math.round(sh * (y / dh));
+        
+        width = Math.round(sw * (width / dw));
+        height = Math.round(sh * (height / dh));
+        
+        GL11.glScissor(x, sh - height - y, width, height);
+    }
+    
+    public static void end() {
+        GL11.glDisable(GL11.GL_SCISSOR_TEST);
+    }
+}

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

@@ -5,8 +5,10 @@ import net.fabricmc.api.ClientModInitializer;
 import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding;
 import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding;
 import net.minecraft.item.Item;
 import net.minecraft.item.Item;
 import net.minecraft.item.ItemStack;
 import net.minecraft.item.ItemStack;
+import net.minecraft.util.Identifier;
 
 
 import java.util.List;
 import java.util.List;
+import java.util.Map;
 
 
 public interface ClientHelper extends ClientModInitializer {
 public interface ClientHelper extends ClientModInitializer {
     static ClientHelper getInstance() {
     static ClientHelper getInstance() {
@@ -19,6 +21,8 @@ public interface ClientHelper extends ClientModInitializer {
     
     
     List<ItemStack> getInventoryItemsTypes();
     List<ItemStack> getInventoryItemsTypes();
     
     
+    void openRecipeViewingScreen(Map<RecipeCategory, List<RecipeDisplay>> map);
+    
     void registerFabricKeyBinds();
     void registerFabricKeyBinds();
     
     
     boolean tryCheatingStack(ItemStack stack);
     boolean tryCheatingStack(ItemStack stack);
@@ -33,6 +37,10 @@ public interface ClientHelper extends ClientModInitializer {
     
     
     String getFormattedModFromItem(Item item);
     String getFormattedModFromItem(Item item);
     
     
+    String getFormattedModFromIdentifier(Identifier identifier);
+    
+    String getModFromIdentifier(Identifier identifier);
+    
     FabricKeyBinding getRecipeKeyBinding();
     FabricKeyBinding getRecipeKeyBinding();
     
     
     FabricKeyBinding getUsageKeyBinding();
     FabricKeyBinding getUsageKeyBinding();

+ 9 - 0
src/main/java/me/shedaniel/rei/api/RecipeCategory.java

@@ -1,6 +1,7 @@
 package me.shedaniel.rei.api;
 package me.shedaniel.rei.api;
 
 
 import me.shedaniel.rei.gui.RecipeViewingScreen;
 import me.shedaniel.rei.gui.RecipeViewingScreen;
+import me.shedaniel.rei.gui.renderables.RecipeRenderer;
 import me.shedaniel.rei.gui.widget.CategoryBaseWidget;
 import me.shedaniel.rei.gui.widget.CategoryBaseWidget;
 import me.shedaniel.rei.gui.widget.RecipeBaseWidget;
 import me.shedaniel.rei.gui.widget.RecipeBaseWidget;
 import me.shedaniel.rei.gui.widget.Widget;
 import me.shedaniel.rei.gui.widget.Widget;
@@ -20,8 +21,16 @@ public interface RecipeCategory<T extends RecipeDisplay> {
     
     
     ItemStack getCategoryIcon();
     ItemStack getCategoryIcon();
     
     
+    default Renderer getIcon() {
+        return Renderable.fromItemStackSupplier(this::getCategoryIcon);
+    }
+    
     String getCategoryName();
     String getCategoryName();
     
     
+    default RecipeRenderer getSimpleRenderer(T recipe) {
+        return Renderable.fromRecipe(recipe::getInput, recipe::getOutput);
+    }
+    
     default List<Widget> setupDisplay(Supplier<T> recipeDisplaySupplier, Rectangle bounds) {
     default List<Widget> setupDisplay(Supplier<T> recipeDisplaySupplier, Rectangle bounds) {
         return Collections.singletonList(new RecipeBaseWidget(bounds));
         return Collections.singletonList(new RecipeBaseWidget(bounds));
     }
     }

+ 52 - 0
src/main/java/me/shedaniel/rei/api/Renderable.java

@@ -0,0 +1,52 @@
+package me.shedaniel.rei.api;
+
+import me.shedaniel.rei.gui.renderables.EmptyRenderer;
+import me.shedaniel.rei.gui.renderables.ItemStackRenderer;
+import me.shedaniel.rei.gui.renderables.SimpleRecipeRenderer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.math.MathHelper;
+
+import java.util.List;
+import java.util.function.Supplier;
+
+public interface Renderable {
+    
+    static ItemStackRenderer fromItemStackSupplier(Supplier<ItemStack> supplier) {
+        return new ItemStackRenderer() {
+            @Override
+            public ItemStack getItemStack() {
+                return supplier.get();
+            }
+        };
+    }
+    
+    static ItemStackRenderer fromItemStack(ItemStack stack) {
+        return new ItemStackRenderer() {
+            @Override
+            public ItemStack getItemStack() {
+                return stack;
+            }
+        };
+    }
+    
+    static EmptyRenderer empty() {
+        return EmptyRenderer.INSTANCE;
+    }
+    
+    static SimpleRecipeRenderer fromRecipe(Supplier<List<List<ItemStack>>> input, Supplier<List<ItemStack>> output) {
+        return new SimpleRecipeRenderer(input, output);
+    }
+    
+    static ItemStackRenderer fromItemStacks(List<ItemStack> stacks) {
+        return new ItemStackRenderer() {
+            @Override
+            public ItemStack getItemStack() {
+                if (stacks.isEmpty())
+                    return ItemStack.EMPTY;
+                return stacks.get(MathHelper.floor((System.currentTimeMillis() / 500 % (double) stacks.size()) / 1f));
+            }
+        };
+    }
+    
+    void render(int x, int y, double mouseX, double mouseY, float delta);
+}

+ 13 - 0
src/main/java/me/shedaniel/rei/api/Renderer.java

@@ -0,0 +1,13 @@
+package me.shedaniel.rei.api;
+
+import net.minecraft.client.gui.DrawableHelper;
+
+public abstract class Renderer extends DrawableHelper implements Renderable {
+    public int getBlitOffset() {
+        return this.blitOffset;
+    }
+    
+    public void setBlitOffset(int offset) {
+        this.blitOffset = offset;
+    }
+}

+ 25 - 3
src/main/java/me/shedaniel/rei/client/ClientHelperImpl.java

@@ -9,7 +9,9 @@ import me.shedaniel.rei.api.ClientHelper;
 import me.shedaniel.rei.api.RecipeCategory;
 import me.shedaniel.rei.api.RecipeCategory;
 import me.shedaniel.rei.api.RecipeDisplay;
 import me.shedaniel.rei.api.RecipeDisplay;
 import me.shedaniel.rei.api.RecipeHelper;
 import me.shedaniel.rei.api.RecipeHelper;
+import me.shedaniel.rei.gui.PreRecipeViewingScreen;
 import me.shedaniel.rei.gui.RecipeViewingScreen;
 import me.shedaniel.rei.gui.RecipeViewingScreen;
+import me.shedaniel.rei.gui.VillagerRecipeViewingScreen;
 import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding;
 import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding;
 import net.fabricmc.fabric.api.network.ClientSidePacketRegistry;
 import net.fabricmc.fabric.api.network.ClientSidePacketRegistry;
 import net.fabricmc.fabric.impl.client.keybinding.KeyBindingRegistryImpl;
 import net.fabricmc.fabric.impl.client.keybinding.KeyBindingRegistryImpl;
@@ -53,6 +55,14 @@ public class ClientHelperImpl implements ClientHelper {
         return "§9§o" + mod;
         return "§9§o" + mod;
     }
     }
     
     
+    @Override
+    public String getFormattedModFromIdentifier(Identifier identifier) {
+        String mod = getModFromIdentifier(identifier);
+        if (mod.equalsIgnoreCase(""))
+            return "";
+        return "§9§o" + mod;
+    }
+    
     @Override
     @Override
     public FabricKeyBinding getRecipeKeyBinding() {
     public FabricKeyBinding getRecipeKeyBinding() {
         return recipe;
         return recipe;
@@ -78,12 +88,14 @@ public class ClientHelperImpl implements ClientHelper {
         return nextPage;
         return nextPage;
     }
     }
     
     
+    @Override
     public String getModFromItem(Item item) {
     public String getModFromItem(Item item) {
         if (item.equals(Items.AIR))
         if (item.equals(Items.AIR))
             return "";
             return "";
         return getModFromIdentifier(Registry.ITEM.getId(item));
         return getModFromIdentifier(Registry.ITEM.getId(item));
     }
     }
     
     
+    @Override
     public String getModFromIdentifier(Identifier identifier) {
     public String getModFromIdentifier(Identifier identifier) {
         if (identifier == null)
         if (identifier == null)
             return "";
             return "";
@@ -147,7 +159,7 @@ public class ClientHelperImpl implements ClientHelper {
     public boolean executeRecipeKeyBind(ItemStack stack) {
     public boolean executeRecipeKeyBind(ItemStack stack) {
         Map<RecipeCategory, List<RecipeDisplay>> map = RecipeHelper.getInstance().getRecipesFor(stack);
         Map<RecipeCategory, List<RecipeDisplay>> map = RecipeHelper.getInstance().getRecipesFor(stack);
         if (map.keySet().size() > 0)
         if (map.keySet().size() > 0)
-            MinecraftClient.getInstance().openScreen(new RecipeViewingScreen(MinecraftClient.getInstance().window, map));
+            openRecipeViewingScreen(map);
         return map.keySet().size() > 0;
         return map.keySet().size() > 0;
     }
     }
     
     
@@ -155,7 +167,7 @@ public class ClientHelperImpl implements ClientHelper {
     public boolean executeUsageKeyBind(ItemStack stack) {
     public boolean executeUsageKeyBind(ItemStack stack) {
         Map<RecipeCategory, List<RecipeDisplay>> map = RecipeHelper.getInstance().getUsagesFor(stack);
         Map<RecipeCategory, List<RecipeDisplay>> map = RecipeHelper.getInstance().getUsagesFor(stack);
         if (map.keySet().size() > 0)
         if (map.keySet().size() > 0)
-            MinecraftClient.getInstance().openScreen(new RecipeViewingScreen(MinecraftClient.getInstance().window, map));
+            openRecipeViewingScreen(map);
         return map.keySet().size() > 0;
         return map.keySet().size() > 0;
     }
     }
     
     
@@ -174,10 +186,20 @@ public class ClientHelperImpl implements ClientHelper {
     public boolean executeViewAllRecipesKeyBind() {
     public boolean executeViewAllRecipesKeyBind() {
         Map<RecipeCategory, List<RecipeDisplay>> map = RecipeHelper.getInstance().getAllRecipes();
         Map<RecipeCategory, List<RecipeDisplay>> map = RecipeHelper.getInstance().getAllRecipes();
         if (map.keySet().size() > 0)
         if (map.keySet().size() > 0)
-            MinecraftClient.getInstance().openScreen(new RecipeViewingScreen(MinecraftClient.getInstance().window, map));
+            openRecipeViewingScreen(map);
         return map.keySet().size() > 0;
         return map.keySet().size() > 0;
     }
     }
     
     
+    @Override
+    public void openRecipeViewingScreen(Map<RecipeCategory, List<RecipeDisplay>> map) {
+        if (RoughlyEnoughItemsCore.getConfigManager().getConfig().screenType == RecipeScreenType.VILLAGER)
+            MinecraftClient.getInstance().openScreen(new VillagerRecipeViewingScreen(map));
+        else if (RoughlyEnoughItemsCore.getConfigManager().getConfig().screenType == RecipeScreenType.UNSET)
+            MinecraftClient.getInstance().openScreen(new PreRecipeViewingScreen(map));
+        else
+            MinecraftClient.getInstance().openScreen(new RecipeViewingScreen(map));
+    }
+    
     @Override
     @Override
     public void onInitializeClient() {
     public void onInitializeClient() {
         ClientHelperImpl.instance = (ClientHelperImpl) this;
         ClientHelperImpl.instance = (ClientHelperImpl) this;

+ 2 - 0
src/main/java/me/shedaniel/rei/client/ConfigObject.java

@@ -48,6 +48,8 @@ public class ConfigObject {
     
     
     public boolean lightGrayRecipeBorder = false;
     public boolean lightGrayRecipeBorder = false;
     
     
+    public RecipeScreenType screenType = RecipeScreenType.UNSET;
+    
     @Comment(
     @Comment(
             "The location of choose page dialog, will automatically be set to your last location so there is no need to change this.")
             "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);
     public RelativePoint choosePageDialogPoint = new RelativePoint(.5, .5);

+ 16 - 0
src/main/java/me/shedaniel/rei/client/RecipeScreenType.java

@@ -0,0 +1,16 @@
+package me.shedaniel.rei.client;
+
+import net.minecraft.client.resource.language.I18n;
+
+import java.util.Locale;
+
+public enum RecipeScreenType {
+    UNSET,
+    ORIGINAL,
+    VILLAGER;
+    
+    @Override
+    public String toString() {
+        return I18n.translate("text.rei.config.recipe_screen_type." + name().toLowerCase());
+    }
+}

+ 8 - 14
src/main/java/me/shedaniel/rei/gui/ContainerScreenOverlay.java

@@ -27,13 +27,15 @@ import net.minecraft.util.math.MathHelper;
 import net.minecraft.world.GameMode;
 import net.minecraft.world.GameMode;
 
 
 import java.awt.*;
 import java.awt.*;
-import java.util.*;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 
 
 public class ContainerScreenOverlay extends AbstractParentElement implements Drawable {
 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();
     private static final List<QueuedTooltip> QUEUED_TOOLTIPS = Lists.newArrayList();
     public static String searchTerm = "";
     public static String searchTerm = "";
     private static int page = 0;
     private static int page = 0;
@@ -43,7 +45,6 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
     private Window window;
     private Window window;
     private CraftableToggleButtonWidget toggleButtonWidget;
     private CraftableToggleButtonWidget toggleButtonWidget;
     private ButtonWidget buttonLeft, buttonRight;
     private ButtonWidget buttonLeft, buttonRight;
-    private int lastLeft;
     
     
     public static ItemListOverlay getItemListOverlay() {
     public static ItemListOverlay getItemListOverlay() {
         return itemListOverlay;
         return itemListOverlay;
@@ -59,7 +60,6 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
         this.window = MinecraftClient.getInstance().window;
         this.window = MinecraftClient.getInstance().window;
         DisplayHelper.DisplayBoundsHandler boundsHandler = RoughlyEnoughItemsCore.getDisplayHelper().getResponsibleBoundsHandler(MinecraftClient.getInstance().currentScreen.getClass());
         DisplayHelper.DisplayBoundsHandler boundsHandler = RoughlyEnoughItemsCore.getDisplayHelper().getResponsibleBoundsHandler(MinecraftClient.getInstance().currentScreen.getClass());
         this.rectangle = RoughlyEnoughItemsCore.getConfigManager().getConfig().mirrorItemPanel ? boundsHandler.getLeftBounds(MinecraftClient.getInstance().currentScreen) : boundsHandler.getRightBounds(MinecraftClient.getInstance().currentScreen);
         this.rectangle = RoughlyEnoughItemsCore.getConfigManager().getConfig().mirrorItemPanel ? boundsHandler.getLeftBounds(MinecraftClient.getInstance().currentScreen) : boundsHandler.getRightBounds(MinecraftClient.getInstance().currentScreen);
-        this.lastLeft = getLeft();
         widgets.add(itemListOverlay = new ItemListOverlay(page));
         widgets.add(itemListOverlay = new ItemListOverlay(page));
         itemListOverlay.updateList(boundsHandler, boundsHandler.getItemListArea(rectangle), page, searchTerm, false);
         itemListOverlay.updateList(boundsHandler, boundsHandler.getItemListArea(rectangle), page, searchTerm, false);
         
         
@@ -306,6 +306,10 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
             RecipeViewingScreen widget = (RecipeViewingScreen) MinecraftClient.getInstance().currentScreen;
             RecipeViewingScreen widget = (RecipeViewingScreen) MinecraftClient.getInstance().currentScreen;
             return new Rectangle(widget.getBounds().x, window.getScaledHeight() - 22, widget.getBounds().width - widthRemoved, 18);
             return new Rectangle(widget.getBounds().x, window.getScaledHeight() - 22, widget.getBounds().width - widthRemoved, 18);
         }
         }
+        if (MinecraftClient.getInstance().currentScreen instanceof VillagerRecipeViewingScreen) {
+            VillagerRecipeViewingScreen widget = (VillagerRecipeViewingScreen) MinecraftClient.getInstance().currentScreen;
+            return new Rectangle(widget.bounds.x, window.getScaledHeight() - 22, widget.bounds.width - widthRemoved, 18);
+        }
         return new Rectangle(ScreenHelper.getLastContainerScreenHooks().rei_getContainerLeft(), window.getScaledHeight() - 22, ScreenHelper.getLastContainerScreenHooks().rei_getContainerWidth() - widthRemoved, 18);
         return new Rectangle(ScreenHelper.getLastContainerScreenHooks().rei_getContainerLeft(), window.getScaledHeight() - 22, ScreenHelper.getLastContainerScreenHooks().rei_getContainerWidth() - widthRemoved, 18);
     }
     }
     
     
@@ -427,16 +431,6 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
         GuiLighting.disable();
         GuiLighting.disable();
     }
     }
     
     
-    private int getLeft() {
-        if (MinecraftClient.getInstance().currentScreen instanceof RecipeViewingScreen) {
-            RecipeViewingScreen widget = (RecipeViewingScreen) MinecraftClient.getInstance().currentScreen;
-            return widget.getBounds().x;
-        }
-        if (MinecraftClient.getInstance().player.getRecipeBook().isGuiOpen())
-            return ScreenHelper.getLastContainerScreenHooks().rei_getContainerLeft() - 147 - 30;
-        return ScreenHelper.getLastContainerScreenHooks().rei_getContainerLeft();
-    }
-    
     private int getTotalPage() {
     private int getTotalPage() {
         return itemListOverlay.getTotalPage();
         return itemListOverlay.getTotalPage();
     }
     }

+ 133 - 0
src/main/java/me/shedaniel/rei/gui/PreRecipeViewingScreen.java

@@ -0,0 +1,133 @@
+package me.shedaniel.rei.gui;
+
+import com.google.common.collect.Lists;
+import me.shedaniel.rei.RoughlyEnoughItemsCore;
+import me.shedaniel.rei.api.ClientHelper;
+import me.shedaniel.rei.api.RecipeCategory;
+import me.shedaniel.rei.api.RecipeDisplay;
+import me.shedaniel.rei.client.RecipeScreenType;
+import me.shedaniel.rei.client.ScreenHelper;
+import me.shedaniel.rei.gui.widget.ButtonWidget;
+import me.shedaniel.rei.gui.widget.HighlightableWidget;
+import me.shedaniel.rei.gui.widget.Widget;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.audio.PositionedSoundInstance;
+import net.minecraft.client.gui.Element;
+import net.minecraft.client.gui.Screen;
+import net.minecraft.client.render.GuiLighting;
+import net.minecraft.client.resource.language.I18n;
+import net.minecraft.sound.SoundEvents;
+import net.minecraft.text.TranslatableTextComponent;
+import net.minecraft.util.Identifier;
+
+import java.awt.*;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+public class PreRecipeViewingScreen extends Screen {
+    
+    private static final Identifier IDENTIFIER = new Identifier("roughlyenoughitems", "textures/gui/screenshot.png");
+    private final List<Widget> widgets;
+    private boolean original;
+    private Map<RecipeCategory, List<RecipeDisplay>> map;
+    
+    public PreRecipeViewingScreen(Map<RecipeCategory, List<RecipeDisplay>> map) {
+        super(new TranslatableTextComponent("text.rei.recipe_screen_type.selection"));
+        this.widgets = Lists.newArrayList();
+        this.original = true;
+        this.map = map;
+    }
+    
+    @Override
+    protected void init() {
+        this.children.clear();
+        this.widgets.clear();
+        this.widgets.add(new ButtonWidget(width / 2 - 100, height - 40, 200, 20, I18n.translate("text.rei.select")) {
+            @Override
+            public void onPressed() {
+                RoughlyEnoughItemsCore.getConfigManager().getConfig().screenType = original ? RecipeScreenType.ORIGINAL : RecipeScreenType.VILLAGER;
+                try {
+                    RoughlyEnoughItemsCore.getConfigManager().saveConfig();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+                ClientHelper.getInstance().openRecipeViewingScreen(map);
+            }
+        });
+        this.widgets.add(new ScreenTypeSelection(width / 2 - 200 - 5, height / 2 - 112 / 2 - 10, 0));
+        this.widgets.add(new ScreenTypeSelection(width / 2 + 5, height / 2 - 112 / 2 - 10, 112));
+        this.children.addAll(widgets);
+    }
+    
+    @Override
+    public void render(int int_1, int int_2, float float_1) {
+        this.renderBackground();
+        this.drawCenteredString(this.font, this.title.getFormattedText(), this.width / 2, 20, 16777215);
+        int i = 30;
+        for(String s : this.font.wrapStringToWidthAsList(I18n.translate("text.rei.recipe_screen_type.selection.sub"), width - 30)) {
+            this.drawCenteredString(this.font, "§7" + s, width / 2, i, -1);
+            i += 10;
+        }
+        super.render(int_1, int_2, float_1);
+        this.widgets.forEach(widget -> {
+            GuiLighting.disable();
+            widget.render(int_1, int_2, float_1);
+        });
+    }
+    
+    @Override
+    public boolean keyPressed(int int_1, int int_2, int int_3) {
+        if ((int_1 == 256 || this.minecraft.options.keyInventory.matchesKey(int_1, int_2)) && this.shouldCloseOnEsc()) {
+            MinecraftClient.getInstance().openScreen(ScreenHelper.getLastContainerScreen());
+            ScreenHelper.getLastOverlay().init();
+            return true;
+        }
+        return super.keyPressed(int_1, int_2, int_3);
+    }
+    
+    public class ScreenTypeSelection extends HighlightableWidget {
+        
+        private Rectangle bounds;
+        private int u, v;
+        
+        public ScreenTypeSelection(int x, int y, int v) {
+            this.bounds = new Rectangle(x - 4, y - 4, 208, 120);
+            this.u = 0;
+            this.v = v;
+        }
+        
+        @Override
+        public Rectangle getBounds() {
+            return bounds;
+        }
+        
+        @Override
+        public void render(int i, int i1, float delta) {
+            MinecraftClient.getInstance().getTextureManager().bindTexture(IDENTIFIER);
+            blit(bounds.x + 4, bounds.y + 4, u, v, 200, 112);
+            if (original == (v == 0)) {
+                fillGradient(bounds.x, bounds.y, bounds.x + bounds.width, bounds.y + 2, 0xFFFFFFFF, 0xFFFFFFFF);
+                fillGradient(bounds.x, bounds.y + bounds.height - 2, bounds.x + bounds.width, bounds.y + bounds.height, 0xFFFFFFFF, 0xFFFFFFFF);
+                fillGradient(bounds.x, bounds.y, bounds.x + 2, bounds.y + bounds.height, 0xFFFFFFFF, 0xFFFFFFFF);
+                fillGradient(bounds.x + bounds.width - 2, bounds.y, bounds.x + bounds.width, bounds.y + bounds.height, 0xFFFFFFFF, 0xFFFFFFFF);
+            }
+        }
+        
+        @Override
+        public boolean mouseClicked(double double_1, double double_2, int int_1) {
+            if (isHighlighted(double_1, double_2)) {
+                minecraft.getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
+                original = (v == 0);
+                return true;
+            }
+            return false;
+        }
+        
+        @Override
+        public List<? extends Element> children() {
+            return Collections.emptyList();
+        }
+    }
+}

+ 7 - 8
src/main/java/me/shedaniel/rei/gui/RecipeViewingScreen.java

@@ -43,16 +43,15 @@ public class RecipeViewingScreen extends Screen {
     public int largestWidth, largestHeight;
     public int largestWidth, largestHeight;
     public boolean choosePageActivated;
     public boolean choosePageActivated;
     public RecipeChoosePageWidget recipeChoosePageWidget;
     public RecipeChoosePageWidget recipeChoosePageWidget;
-    private Window window;
     private Rectangle bounds;
     private Rectangle bounds;
     private RecipeCategory selectedCategory;
     private RecipeCategory selectedCategory;
     private ButtonWidget recipeBack, recipeNext, categoryBack, categoryNext;
     private ButtonWidget recipeBack, recipeNext, categoryBack, categoryNext;
     
     
-    public RecipeViewingScreen(Window window, Map<RecipeCategory, List<RecipeDisplay>> categoriesMap) {
+    public RecipeViewingScreen(Map<RecipeCategory, List<RecipeDisplay>> categoriesMap) {
         super(new StringTextComponent(""));
         super(new StringTextComponent(""));
         this.categoryPages = 0;
         this.categoryPages = 0;
-        this.window = window;
         this.widgets = Lists.newArrayList();
         this.widgets = Lists.newArrayList();
+        Window window = MinecraftClient.getInstance().window;
         this.bounds = new Rectangle(window.getScaledWidth() / 2 - guiWidth / 2, window.getScaledHeight() / 2 - guiHeight / 2, 176, 186);
         this.bounds = new Rectangle(window.getScaledWidth() / 2 - guiWidth / 2, window.getScaledHeight() / 2 - guiHeight / 2, 176, 186);
         this.categoriesMap = categoriesMap;
         this.categoriesMap = categoriesMap;
         this.categories = Lists.newArrayList();
         this.categories = Lists.newArrayList();
@@ -119,11 +118,11 @@ public class RecipeViewingScreen extends Screen {
         this.children.clear();
         this.children.clear();
         this.tabs.clear();
         this.tabs.clear();
         this.widgets.clear();
         this.widgets.clear();
-        this.largestWidth = window.getScaledWidth() - 100;
-        this.largestHeight = window.getScaledHeight() - 40;
+        this.largestWidth = width - 100;
+        this.largestHeight = height - 40;
         this.guiWidth = MathHelper.clamp(getCurrentDisplayed().stream().map(display -> selectedCategory.getDisplayWidth(display)).max(Integer::compareTo).orElse(150) + 30, 0, largestWidth);
         this.guiWidth = MathHelper.clamp(getCurrentDisplayed().stream().map(display -> selectedCategory.getDisplayWidth(display)).max(Integer::compareTo).orElse(150) + 30, 0, largestWidth);
         this.guiHeight = MathHelper.floor(MathHelper.clamp((selectedCategory.getDisplayHeight() + 7d) * (getRecipesPerPage() + 1d) + 40d, 186d, (double) largestHeight));
         this.guiHeight = MathHelper.floor(MathHelper.clamp((selectedCategory.getDisplayHeight() + 7d) * (getRecipesPerPage() + 1d) + 40d, 186d, (double) largestHeight));
-        this.bounds = new Rectangle(window.getScaledWidth() / 2 - guiWidth / 2, window.getScaledHeight() / 2 - guiHeight / 2, guiWidth, guiHeight);
+        this.bounds = new Rectangle(width / 2 - guiWidth / 2, height / 2 - guiHeight / 2, guiWidth, guiHeight);
         this.page = MathHelper.clamp(page, 0, getTotalPages(selectedCategory) - 1);
         this.page = MathHelper.clamp(page, 0, getTotalPages(selectedCategory) - 1);
         
         
         widgets.add(categoryBack = new ButtonWidget((int) bounds.getX() + 5, (int) bounds.getY() + 5, 12, 12, new TranslatableTextComponent("text.rei.left_arrow")) {
         widgets.add(categoryBack = new ButtonWidget((int) bounds.getX() + 5, (int) bounds.getY() + 5, 12, 12, new TranslatableTextComponent("text.rei.left_arrow")) {
@@ -235,7 +234,7 @@ public class RecipeViewingScreen extends Screen {
             int j = i + categoryPages * 6;
             int j = i + categoryPages * 6;
             if (categories.size() > j) {
             if (categories.size() > j) {
                 TabWidget tab;
                 TabWidget tab;
-                tabs.add(tab = new TabWidget(i, this, new Rectangle(bounds.x + 4 + 28 * i, bounds.y - 28, 28, 28)) {
+                tabs.add(tab = new TabWidget(i, new Rectangle(bounds.x + 4 + 28 * i, bounds.y - 28, 28, 28)) {
                     @Override
                     @Override
                     public boolean mouseClicked(double mouseX, double mouseY, int button) {
                     public boolean mouseClicked(double mouseX, double mouseY, int button) {
                         if (getBounds().contains(mouseX, mouseY)) {
                         if (getBounds().contains(mouseX, mouseY)) {
@@ -250,7 +249,7 @@ public class RecipeViewingScreen extends Screen {
                         return false;
                         return false;
                     }
                     }
                 });
                 });
-                tab.setItem(categories.get(j).getCategoryIcon(), categories.get(j).getCategoryName(), tab.getId() + categoryPages * 6 == categories.indexOf(selectedCategory));
+                tab.setRenderer(categories.get(j), categories.get(j).getIcon(), categories.get(j).getCategoryName(), tab.getId() + categoryPages * 6 == categories.indexOf(selectedCategory));
             }
             }
         }
         }
         Optional<ButtonAreaSupplier> supplier = RecipeHelper.getInstance().getSpeedCraftButtonArea(selectedCategory);
         Optional<ButtonAreaSupplier> supplier = RecipeHelper.getInstance().getSpeedCraftButtonArea(selectedCategory);

+ 270 - 0
src/main/java/me/shedaniel/rei/gui/VillagerRecipeViewingScreen.java

@@ -0,0 +1,270 @@
+package me.shedaniel.rei.gui;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.mojang.blaze3d.platform.GlStateManager;
+import com.zeitheron.hammercore.client.utils.Scissors;
+import me.shedaniel.rei.api.*;
+import me.shedaniel.rei.client.ScreenHelper;
+import me.shedaniel.rei.gui.renderables.RecipeRenderer;
+import me.shedaniel.rei.gui.widget.*;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.audio.PositionedSoundInstance;
+import net.minecraft.client.gui.Element;
+import net.minecraft.client.gui.Screen;
+import net.minecraft.client.render.GuiLighting;
+import net.minecraft.client.resource.language.I18n;
+import net.minecraft.sound.SoundEvents;
+import net.minecraft.text.StringTextComponent;
+import net.minecraft.util.math.MathHelper;
+import org.lwjgl.BufferUtils;
+import org.lwjgl.glfw.GLFW;
+import org.lwjgl.opengl.GL11;
+
+import java.awt.*;
+import java.nio.IntBuffer;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import static me.shedaniel.rei.gui.RecipeViewingScreen.getSpeedCraftFunctionalByCategory;
+
+public class VillagerRecipeViewingScreen extends Screen {
+    
+    private final Map<RecipeCategory, List<RecipeDisplay>> categoryMap;
+    private final List<RecipeCategory> categories;
+    private final List<Widget> widgets;
+    private final List<ButtonWidget> buttonWidgets;
+    private final List<Renderer> recipeRenderers;
+    private final List<TabWidget> tabs;
+    public Rectangle bounds, scrollListBounds;
+    private int selectedCategoryIndex, selectedRecipeIndex;
+    private double scroll;
+    private int tabsPage;
+    private static final int TABS_PER_PAGE = 9;
+    
+    public VillagerRecipeViewingScreen(Map<RecipeCategory, List<RecipeDisplay>> map) {
+        super(new StringTextComponent(""));
+        this.widgets = Lists.newArrayList();
+        this.categoryMap = Maps.newLinkedHashMap();
+        this.selectedCategoryIndex = 0;
+        this.selectedRecipeIndex = 0;
+        this.scroll = 0;
+        this.tabsPage = 0;
+        this.categories = Lists.newArrayList();
+        this.buttonWidgets = Lists.newArrayList();
+        this.tabs = Lists.newArrayList();
+        this.recipeRenderers = Lists.newArrayList();
+        RecipeHelper.getInstance().getAllCategories().forEach(category -> {
+            if (map.containsKey(category)) {
+                categories.add(category);
+                categoryMap.put(category, map.get(category));
+            }
+        });
+    }
+    
+    @Override
+    protected void init() {
+        super.init();
+        this.children.clear();
+        this.widgets.clear();
+        this.buttonWidgets.clear();
+        this.recipeRenderers.clear();
+        this.tabs.clear();
+        int largestWidth = width - 100;
+        int largestHeight = height - 40;
+        RecipeCategory category = categories.get(selectedCategoryIndex);
+        RecipeDisplay display = categoryMap.get(category).get(selectedRecipeIndex);
+        int guiWidth = MathHelper.clamp(category.getDisplayWidth(display) + 30, 0, largestWidth) + 100;
+        int guiHeight = MathHelper.clamp(category.getDisplayHeight() + 40, 166, largestHeight);
+        this.bounds = new Rectangle(width / 2 - guiWidth / 2, height / 2 - guiHeight / 2, guiWidth, guiHeight);
+        this.widgets.add(new CategoryBaseWidget(bounds));
+        this.scrollListBounds = new Rectangle(bounds.x + 4, bounds.y + 17, 97 + 5, guiHeight - 17 - 7);
+        this.widgets.add(new SlotBaseWidget(scrollListBounds));
+        
+        Rectangle recipeBounds = new Rectangle(bounds.x + 100 + (guiWidth - 100) / 2 - category.getDisplayWidth(display) / 2, bounds.y + bounds.height / 2 - category.getDisplayHeight() / 2, category.getDisplayWidth(display), category.getDisplayHeight());
+        this.widgets.addAll(category.setupDisplay(() -> display, recipeBounds));
+        Optional<ButtonAreaSupplier> supplier = RecipeHelper.getInstance().getSpeedCraftButtonArea(category);
+        final SpeedCraftFunctional functional = getSpeedCraftFunctionalByCategory(ScreenHelper.getLastContainerScreen(), category);
+        if (supplier.isPresent())
+            this.widgets.add(new SpeedCraftingButtonWidget(supplier.get().get(recipeBounds), supplier.get().getButtonText(), functional, () -> display));
+        
+        int index = 0;
+        for(RecipeDisplay recipeDisplay : categoryMap.get(category)) {
+            int finalIndex = index;
+            RecipeRenderer recipeRenderer;
+            recipeRenderers.add(recipeRenderer = category.getSimpleRenderer(recipeDisplay));
+            buttonWidgets.add(new ButtonWidget(bounds.x + 5, 0, recipeRenderer.getWidth(), recipeRenderer.getHeight(), "") {
+                @Override
+                public void onPressed() {
+                    selectedRecipeIndex = finalIndex;
+                    VillagerRecipeViewingScreen.this.init();
+                }
+            });
+            index++;
+        }
+        for(int i = 0; i < TABS_PER_PAGE; i++) {
+            int j = i + tabsPage * TABS_PER_PAGE;
+            if (categories.size() > j) {
+                TabWidget tab;
+                tabs.add(tab = new TabWidget(i, new Rectangle(bounds.x + bounds.width / 2 - Math.min(categories.size() - tabsPage * TABS_PER_PAGE, TABS_PER_PAGE) * 14 + i * 28, bounds.y - 28, 28, 28)) {
+                    @Override
+                    public boolean mouseClicked(double mouseX, double mouseY, int button) {
+                        if (getBounds().contains(mouseX, mouseY)) {
+                            MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
+                            if (getId() + tabsPage * TABS_PER_PAGE == selectedCategoryIndex)
+                                return false;
+                            selectedCategoryIndex = getId() + tabsPage * TABS_PER_PAGE;
+                            scroll = 0;
+                            VillagerRecipeViewingScreen.this.init();
+                            return true;
+                        }
+                        return false;
+                    }
+                });
+                tab.setRenderer(categories.get(j), categories.get(j).getIcon(), categories.get(j).getCategoryName(), tab.getId() + tabsPage * TABS_PER_PAGE == selectedCategoryIndex);
+            }
+        }
+        
+        this.widgets.add(new ClickableLabelWidget(bounds.x + 4 + scrollListBounds.width / 2, bounds.y + 6, categories.get(selectedCategoryIndex).getCategoryName()) {
+            @Override
+            public void onLabelClicked() {
+                MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
+                ClientHelper.getInstance().executeViewAllRecipesKeyBind();
+            }
+            
+            @Override
+            public Optional<String> getTooltips() {
+                return Optional.ofNullable(I18n.translate("text.rei.view_all_categories"));
+            }
+            
+            @Override
+            public void render(int mouseX, int mouseY, float delta) {
+                GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F);
+                int colour = getDefaultColor();
+                if (clickable && isHovered(mouseX, mouseY))
+                    colour = getHoveredColor();
+                font.draw((isHovered(mouseX, mouseY) ? "§n" : "") + text, x - font.getStringWidth(text) / 2, y, colour);
+                if (clickable && getTooltips().isPresent())
+                    if (!focused && isHighlighted(mouseX, mouseY))
+                        ScreenHelper.getLastOverlay().addTooltip(QueuedTooltip.create(getTooltips().get().split("\n")));
+                    else if (focused)
+                        ScreenHelper.getLastOverlay().addTooltip(QueuedTooltip.create(new Point(x, y), getTooltips().get().split("\n")));
+            }
+    
+            @Override
+            public int getHoveredColor() {
+                return -1;
+            }
+    
+            @Override
+            public int getDefaultColor() {
+                return 4210752;
+            }
+        });
+        this.children.addAll(buttonWidgets);
+        this.widgets.addAll(tabs);
+        this.children.addAll(widgets);
+        this.children.add(ScreenHelper.getLastOverlay(true, false));
+        ScreenHelper.getLastOverlay().init();
+    }
+    
+    @Override
+    public boolean mouseScrolled(double double_1, double double_2, double double_3) {
+        double height = buttonWidgets.stream().map(ButtonWidget::getBounds).collect(Collectors.summingDouble(Rectangle::getHeight));
+        if (scrollListBounds.contains(double_1, double_2) && height > scrollListBounds.height - 2) {
+            if (double_3 > 0)
+                scroll -= 16;
+            else
+                scroll += 16;
+            scroll = MathHelper.clamp(scroll, 0, height - scrollListBounds.height + 2);
+            return true;
+        }
+        return super.mouseScrolled(double_1, double_2, double_3);
+    }
+    
+    @Override
+    public void render(int mouseX, int mouseY, float delta) {
+        this.fillGradient(0, 0, this.width, this.height, -1072689136, -804253680);
+        int yOffset = 0;
+        this.widgets.forEach(widget -> {
+            GuiLighting.disable();
+            widget.render(mouseX, mouseY, delta);
+        });
+        GuiLighting.disable();
+        ScreenHelper.getLastOverlay().render(mouseX, mouseY, delta);
+        GL11.glPushMatrix();
+        Scissors.begin();
+        Scissors.scissor(0, scrollListBounds.y + 1, width, scrollListBounds.height - 2);
+        for(int i = 0; i < buttonWidgets.size(); i++) {
+            ButtonWidget buttonWidget = buttonWidgets.get(i);
+            buttonWidget.getBounds().y = scrollListBounds.y + 1 + yOffset - (int) scroll;
+            if (buttonWidget.getBounds().getMaxY() > scrollListBounds.getMinY() && buttonWidget.getBounds().getMinY() < scrollListBounds.getMaxY()) {
+                GuiLighting.disable();
+                buttonWidget.render(mouseX, mouseY, delta);
+            }
+            yOffset += buttonWidget.getBounds().height;
+        }
+        for(int i = 0; i < buttonWidgets.size(); i++) {
+            if (buttonWidgets.get(i).getBounds().getMaxY() > scrollListBounds.getMinY() && buttonWidgets.get(i).getBounds().getMinY() < scrollListBounds.getMaxY()) {
+                GuiLighting.disable();
+                recipeRenderers.get(i).setBlitOffset(1);
+                recipeRenderers.get(i).render(buttonWidgets.get(i).getBounds().x, buttonWidgets.get(i).getBounds().y, mouseX, mouseY, delta);
+            }
+        }
+        Scissors.end();
+        GL11.glPopMatrix();
+        ScreenHelper.getLastOverlay().lateRender(mouseX, mouseY, delta);
+    }
+    
+    private int getTitleBarHeight() {
+        IntBuffer useless = BufferUtils.createIntBuffer(3), top = BufferUtils.createIntBuffer(1);
+        GLFW.glfwGetWindowFrameSize(minecraft.window.getHandle(), useless, top, useless, useless);
+        System.out.println(top.get(0));
+        return top.get(0) / 3 * 2;
+    }
+    
+    private int getReal(int i) {
+        return (int) (i / ((double) minecraft.window.getScaledWidth() / (double) minecraft.window.getWidth()));
+    }
+    
+    @Override
+    public boolean keyPressed(int int_1, int int_2, int int_3) {
+        if ((int_1 == 256 || this.minecraft.options.keyInventory.matchesKey(int_1, int_2)) && this.shouldCloseOnEsc()) {
+            MinecraftClient.getInstance().openScreen(ScreenHelper.getLastContainerScreen());
+            ScreenHelper.getLastOverlay().init();
+            return true;
+        }
+        if (int_1 == 258) {
+            boolean boolean_1 = !hasShiftDown();
+            if (!this.changeFocus(boolean_1))
+                this.changeFocus(boolean_1);
+            return true;
+        }
+        if (ClientHelper.getInstance().getNextPageKeyBinding().matchesKey(int_1, int_2)) {
+            if (categoryMap.get(categories.get(selectedCategoryIndex)).size() > 1) {
+                selectedCategoryIndex++;
+                if (selectedCategoryIndex >= categoryMap.get(categories.get(selectedCategoryIndex)).size())
+                    selectedCategoryIndex = 0;
+                init();
+                return true;
+            }
+            return false;
+        } else if (ClientHelper.getInstance().getPreviousPageKeyBinding().matchesKey(int_1, int_2)) {
+            if (categoryMap.get(categories.get(selectedCategoryIndex)).size() > 1) {
+                selectedCategoryIndex--;
+                if (selectedCategoryIndex < 0)
+                    selectedCategoryIndex = categoryMap.get(categories.get(selectedCategoryIndex)).size() - 1;
+                init();
+                return true;
+            }
+            return false;
+        }
+        for(Element element : children())
+            if (element.keyPressed(int_1, int_2, int_3))
+                return true;
+        return super.keyPressed(int_1, int_2, int_3);
+    }
+    
+}

+ 13 - 0
src/main/java/me/shedaniel/rei/gui/renderables/EmptyRenderer.java

@@ -0,0 +1,13 @@
+package me.shedaniel.rei.gui.renderables;
+
+import me.shedaniel.rei.api.Renderer;
+
+public class EmptyRenderer extends Renderer {
+    
+    public static final EmptyRenderer INSTANCE = new EmptyRenderer();
+    
+    @Override
+    public void render(int x, int y, double mouseX, double mouseY, float delta) {
+    
+    }
+}

+ 69 - 0
src/main/java/me/shedaniel/rei/gui/renderables/ItemStackRenderer.java

@@ -0,0 +1,69 @@
+package me.shedaniel.rei.gui.renderables;
+
+import com.google.common.collect.Lists;
+import com.mojang.blaze3d.platform.GlStateManager;
+import me.shedaniel.rei.api.ClientHelper;
+import me.shedaniel.rei.api.Renderer;
+import me.shedaniel.rei.client.ScreenHelper;
+import me.shedaniel.rei.gui.widget.ItemListOverlay;
+import me.shedaniel.rei.gui.widget.QueuedTooltip;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.render.GuiLighting;
+import net.minecraft.client.render.item.ItemRenderer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.Identifier;
+
+import java.util.Collections;
+import java.util.List;
+
+public abstract class ItemStackRenderer extends Renderer {
+    
+    public static final Identifier CHEST_GUI_TEXTURE = new Identifier("roughlyenoughitems", "textures/gui/recipecontainer.png");
+    public boolean drawTooltip = false;
+    
+    @Override
+    public void render(int x, int y, double mouseX, double mouseY, float delta) {
+        int l = x - 8, i1 = y - 6;
+        GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F);
+        ItemRenderer itemRenderer = MinecraftClient.getInstance().getItemRenderer();
+        itemRenderer.zOffset = blitOffset;
+        GuiLighting.enableForItems();
+        GlStateManager.colorMask(true, true, true, true);
+        GlStateManager.enableLighting();
+        GlStateManager.enableRescaleNormal();
+        GlStateManager.enableDepthTest();
+        itemRenderer.renderGuiItem(getItemStack(), l, i1);
+        itemRenderer.renderGuiItemOverlay(MinecraftClient.getInstance().textRenderer, getItemStack(), l, i1);
+        itemRenderer.zOffset = 0.0F;
+        this.blitOffset = 0;
+        if (drawTooltip && mouseX >= x - 8 && mouseX <= x + 8 && mouseY >= y - 6 && mouseY <= y + 10)
+            queueTooltip(getItemStack(), delta);
+        this.drawTooltip = false;
+    }
+    
+    protected void queueTooltip(ItemStack itemStack, float delta) {
+        ScreenHelper.getLastOverlay().addTooltip(QueuedTooltip.create(getTooltip(itemStack)));
+    }
+    
+    protected List<String> getTooltip(ItemStack itemStack) {
+        final String modString = ClientHelper.getInstance().getFormattedModFromItem(itemStack.getItem());
+        List<String> toolTip = Lists.newArrayList(ItemListOverlay.tryGetItemStackToolTip(itemStack, true));
+        toolTip.addAll(getExtraToolTips(itemStack));
+        boolean alreadyHasMod = false;
+        for(String s : toolTip)
+            if (s.equalsIgnoreCase(modString)) {
+                alreadyHasMod = true;
+                break;
+            }
+        if (!alreadyHasMod)
+            toolTip.add(modString);
+        return toolTip;
+    }
+    
+    protected List<String> getExtraToolTips(ItemStack stack) {
+        return Collections.emptyList();
+    }
+    
+    public abstract ItemStack getItemStack();
+    
+}

+ 13 - 0
src/main/java/me/shedaniel/rei/gui/renderables/RecipeRenderer.java

@@ -0,0 +1,13 @@
+package me.shedaniel.rei.gui.renderables;
+
+import me.shedaniel.rei.api.Renderer;
+
+public abstract class RecipeRenderer extends Renderer {
+    
+    public abstract int getHeight();
+    
+    public final int getWidth() {
+        return 100;
+    }
+    
+}

+ 87 - 0
src/main/java/me/shedaniel/rei/gui/renderables/SimpleRecipeRenderer.java

@@ -0,0 +1,87 @@
+package me.shedaniel.rei.gui.renderables;
+
+import com.google.common.collect.Lists;
+import me.shedaniel.rei.api.Renderable;
+import me.shedaniel.rei.gui.VillagerRecipeViewingScreen;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.render.GuiLighting;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.Pair;
+import net.minecraft.util.math.MathHelper;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+public class SimpleRecipeRenderer extends RecipeRenderer {
+    
+    private static final Identifier CHEST_GUI_TEXTURE = new Identifier("roughlyenoughitems", "textures/gui/recipecontainer.png");
+    private List<ItemStackRenderer> inputRenderer;
+    private ItemStackRenderer outputRenderer;
+    
+    public SimpleRecipeRenderer(Supplier<List<List<ItemStack>>> input, Supplier<List<ItemStack>> output) {
+        List<Pair<List<ItemStack>, AtomicInteger>> newList = Lists.newArrayList();
+        List<Pair<List<ItemStack>, Integer>> a = input.get().stream().map(stacks -> new Pair<>(stacks, stacks.stream().map(ItemStack::getAmount).max(Integer::compareTo).orElse(1))).collect(Collectors.toList());
+        for(Pair<List<ItemStack>, Integer> pair : a) {
+            Optional<Pair<List<ItemStack>, AtomicInteger>> any = newList.stream().filter(pairr -> pair.getLeft().equals(pairr.getLeft())).findAny();
+            if (any.isPresent()) {
+                any.get().getRight().addAndGet(pair.getRight());
+            } else
+                newList.add(new Pair<>(pair.getLeft(), new AtomicInteger(pair.getRight())));
+        }
+        List<List<ItemStack>> b = Lists.newArrayList();
+        for(Pair<List<ItemStack>, AtomicInteger> pair : newList)
+            b.add(pair.getLeft().stream().map(stack -> {
+                ItemStack s = stack.copy();
+                s.setAmount(pair.getRight().get());
+                return s;
+            }).collect(Collectors.toList()));
+        this.inputRenderer = b.stream().filter(stacks -> !stacks.isEmpty()).map(stacks -> Renderable.fromItemStacks(stacks)).collect(Collectors.toList());
+        this.outputRenderer = Renderable.fromItemStacks(output.get().stream().filter(stack -> !stack.isEmpty()).collect(Collectors.toList()));
+    }
+    
+    @Override
+    public void render(int x, int y, double mouseX, double mouseY, float delta) {
+        int xx = x + 5, yy = y + 5;
+        int j = 0;
+        int itemsPerLine = getItemsPerLine();
+        for(ItemStackRenderer itemStackRenderer : inputRenderer) {
+            itemStackRenderer.setBlitOffset(getBlitOffset() + 50);
+            itemStackRenderer.drawTooltip = MinecraftClient.getInstance().currentScreen instanceof VillagerRecipeViewingScreen;
+            itemStackRenderer.render(xx + 8, yy + 6, mouseX, mouseY, delta);
+            xx += 18;
+            j++;
+            if (j >= getItemsPerLine() - 3) {
+                yy += 18;
+                xx = x + 5;
+                j = 0;
+            }
+        }
+        xx = x + 5 + 18 * (getItemsPerLine() - 3);
+        yy = y + getHeight() / 2 - 8;
+        GuiLighting.disable();
+        MinecraftClient.getInstance().getTextureManager().bindTexture(CHEST_GUI_TEXTURE);
+        blit(xx, yy, 0, 28, 36, 18);
+        xx += 36;
+        outputRenderer.setBlitOffset(getBlitOffset() + 50);
+        outputRenderer.drawTooltip = MinecraftClient.getInstance().currentScreen instanceof VillagerRecipeViewingScreen;
+        outputRenderer.render(xx + 8, yy + 6, mouseX, mouseY, delta);
+    }
+    
+    @Override
+    public int getHeight() {
+        return 10 + getItemsHeight() * 18;
+    }
+    
+    public int getItemsHeight() {
+        return MathHelper.ceil(((float) inputRenderer.size()) / (getItemsPerLine() - 3));
+    }
+    
+    public int getItemsPerLine() {
+        return MathHelper.floor((getWidth() - 10f) / 18f);
+    }
+    
+}

+ 2 - 2
src/main/java/me/shedaniel/rei/gui/widget/ButtonWidget.java

@@ -74,8 +74,8 @@ public abstract class ButtonWidget extends HighlightableWidget {
         this.blit(x + 4, y + height - 4, 4, 62 + textureOffset * 20, width - 8, 4);
         this.blit(x + 4, y + height - 4, 4, 62 + textureOffset * 20, width - 8, 4);
         
         
         for(int i = y + 4; i < y + height - 4; i += 4) {
         for(int i = y + 4; i < y + height - 4; i += 4) {
-            this.blit(x, i, 0, 50 + textureOffset * 20, width / 2, MathHelper.clamp(y + height - 4 - i, 0, 4));
-            this.blit(x + width / 2, i, 200 - width / 2, 50 + textureOffset * 20, width / 2, MathHelper.clamp(y + height - 4 - i, 0, 4));
+            this.blit(x, i, 0, 50 + textureOffset * 20, MathHelper.ceil(width / 2f), MathHelper.clamp(y + height - 4 - i, 0, 4));
+            this.blit(x + MathHelper.ceil(width / 2f), i, 200 - MathHelper.floor(width / 2f), 50 + textureOffset * 20, MathHelper.floor(width / 2f), MathHelper.clamp(y + height - 4 - i, 0, 4));
         }
         }
         
         
         int colour = 14737632;
         int colour = 14737632;

+ 5 - 0
src/main/java/me/shedaniel/rei/gui/widget/CategoryBaseWidget.java

@@ -13,4 +13,9 @@ public class CategoryBaseWidget extends RecipeBaseWidget {
         return 66;
         return 66;
     }
     }
     
     
+    @Override
+    protected boolean isRendering() {
+        return true;
+    }
+    
 }
 }

+ 10 - 2
src/main/java/me/shedaniel/rei/gui/widget/ClickableLabelWidget.java

@@ -22,9 +22,9 @@ public abstract class ClickableLabelWidget extends LabelWidget {
     
     
     @Override
     @Override
     public void render(int mouseX, int mouseY, float delta) {
     public void render(int mouseX, int mouseY, float delta) {
-        int colour = -1;
+        int colour = getDefaultColor();
         if (clickable && isHovered(mouseX, mouseY))
         if (clickable && isHovered(mouseX, mouseY))
-            colour = hoveredColor;
+            colour = getHoveredColor();
         drawCenteredString(font, (isHovered(mouseX, mouseY) ? "§n" : "") + text, x, y, colour);
         drawCenteredString(font, (isHovered(mouseX, mouseY) ? "§n" : "") + text, x, y, colour);
         if (clickable && getTooltips().isPresent())
         if (clickable && getTooltips().isPresent())
             if (!focused && isHighlighted(mouseX, mouseY))
             if (!focused && isHighlighted(mouseX, mouseY))
@@ -33,6 +33,14 @@ public abstract class ClickableLabelWidget extends LabelWidget {
                 ScreenHelper.getLastOverlay().addTooltip(QueuedTooltip.create(new Point(x, y), getTooltips().get().split("\n")));
                 ScreenHelper.getLastOverlay().addTooltip(QueuedTooltip.create(new Point(x, y), getTooltips().get().split("\n")));
     }
     }
     
     
+    public int getDefaultColor() {
+        return -1;
+    }
+    
+    public int getHoveredColor() {
+        return hoveredColor;
+    }
+    
     @Override
     @Override
     public boolean mouseClicked(double mouseX, double mouseY, int button) {
     public boolean mouseClicked(double mouseX, double mouseY, int button) {
         if (button == 0 && clickable && isHighlighted(mouseX, mouseY)) {
         if (button == 0 && clickable && isHighlighted(mouseX, mouseY)) {

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

@@ -108,7 +108,7 @@ public class ItemListOverlay extends Widget {
                 j++;
                 j++;
                 if (j > currentDisplayed.size())
                 if (j > currentDisplayed.size())
                     break;
                     break;
-                widgets.add(new ItemSlotWidget(x, y, Collections.singletonList(currentDisplayed.get(j - 1)), false, true, true) {
+                widgets.add(new SlotWidget(x, y, Collections.singletonList(currentDisplayed.get(j - 1)), false, true, true) {
                     @Override
                     @Override
                     protected void queueTooltip(ItemStack itemStack, float delta) {
                     protected void queueTooltip(ItemStack itemStack, float delta) {
                         ClientPlayerEntity player = minecraft.player;
                         ClientPlayerEntity player = minecraft.player;
@@ -118,10 +118,10 @@ public class ItemListOverlay extends Widget {
                     
                     
                     @Override
                     @Override
                     public boolean mouseClicked(double mouseX, double mouseY, int button) {
                     public boolean mouseClicked(double mouseX, double mouseY, int button) {
-                        if (isHighlighted(mouseX, mouseY)) {
+                        if (isCurrentRendererItem() && isHighlighted(mouseX, mouseY)) {
                             if (ClientHelper.getInstance().isCheating()) {
                             if (ClientHelper.getInstance().isCheating()) {
-                                if (getCurrentStack() != null && !getCurrentStack().isEmpty()) {
-                                    ItemStack cheatedStack = getCurrentStack().copy();
+                                if (getCurrentItemStack() != null && !getCurrentItemStack().isEmpty()) {
+                                    ItemStack cheatedStack = getCurrentItemStack().copy();
                                     if (RoughlyEnoughItemsCore.getConfigManager().getConfig().itemCheatingMode == ItemCheatingMode.REI_LIKE)
                                     if (RoughlyEnoughItemsCore.getConfigManager().getConfig().itemCheatingMode == ItemCheatingMode.REI_LIKE)
                                         cheatedStack.setAmount(button != 1 ? 1 : cheatedStack.getMaxAmount());
                                         cheatedStack.setAmount(button != 1 ? 1 : cheatedStack.getMaxAmount());
                                     else if (RoughlyEnoughItemsCore.getConfigManager().getConfig().itemCheatingMode == ItemCheatingMode.JEI_LIKE)
                                     else if (RoughlyEnoughItemsCore.getConfigManager().getConfig().itemCheatingMode == ItemCheatingMode.JEI_LIKE)
@@ -131,9 +131,9 @@ public class ItemListOverlay extends Widget {
                                     return ClientHelper.getInstance().tryCheatingStack(cheatedStack);
                                     return ClientHelper.getInstance().tryCheatingStack(cheatedStack);
                                 }
                                 }
                             } else if (button == 0)
                             } else if (button == 0)
-                                return ClientHelper.getInstance().executeRecipeKeyBind(getCurrentStack().copy());
+                                return ClientHelper.getInstance().executeRecipeKeyBind(getCurrentItemStack().copy());
                             else if (button == 1)
                             else if (button == 1)
-                                return ClientHelper.getInstance().executeUsageKeyBind(getCurrentStack().copy());
+                                return ClientHelper.getInstance().executeUsageKeyBind(getCurrentItemStack().copy());
                         }
                         }
                         return false;
                         return false;
                     }
                     }

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

@@ -1,158 +1,25 @@
 package me.shedaniel.rei.gui.widget;
 package me.shedaniel.rei.gui.widget;
 
 
-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.client.ScreenHelper;
-import net.minecraft.client.gui.Element;
-import net.minecraft.client.render.GuiLighting;
-import net.minecraft.client.render.item.ItemRenderer;
+import me.shedaniel.rei.api.Renderer;
 import net.minecraft.item.ItemStack;
 import net.minecraft.item.ItemStack;
-import net.minecraft.item.Items;
-import net.minecraft.util.Identifier;
-import net.minecraft.util.math.MathHelper;
 
 
-import java.awt.*;
-import java.util.Collections;
-import java.util.LinkedList;
+import java.util.Collection;
 import java.util.List;
 import java.util.List;
 
 
-public class ItemSlotWidget extends HighlightableWidget {
-    
-    private static final Identifier RECIPE_GUI = new Identifier("roughlyenoughitems", "textures/gui/recipecontainer.png");
-    private List<ItemStack> itemList = new LinkedList<>();
-    private boolean drawBackground, showToolTips, clickToMoreRecipes, drawHighlightedBackground;
-    private int x, y;
-    
+public class ItemSlotWidget extends SlotWidget {
     public ItemSlotWidget(int x, int y, ItemStack itemStack, boolean drawBackground, boolean showToolTips) {
     public ItemSlotWidget(int x, int y, ItemStack itemStack, boolean drawBackground, boolean showToolTips) {
-        this(x, y, Collections.singletonList(itemStack), drawBackground, showToolTips);
-    }
-    
-    public ItemSlotWidget(int x, int y, List<ItemStack> itemList, boolean drawBackground, boolean showToolTips) {
-        this.itemList = itemList;
-        this.drawBackground = drawBackground;
-        this.showToolTips = showToolTips;
-        this.x = x;
-        this.y = y;
-        this.clickToMoreRecipes = false;
-        this.drawHighlightedBackground = true;
-    }
-    
-    public ItemSlotWidget(int x, int y, List<ItemStack> itemList, boolean drawBackground, boolean showToolTips, boolean clickToMoreRecipes) {
-        this(x, y, itemList, drawBackground, showToolTips);
-        this.clickToMoreRecipes = clickToMoreRecipes;
-    }
-    
-    @Override
-    public List<? extends Element> children() {
-        return Collections.emptyList();
-    }
-    
-    public void setDrawHighlightedBackground(boolean drawHighlightedBackground) {
-        this.drawHighlightedBackground = drawHighlightedBackground;
-    }
-    
-    public boolean isDrawBackground() {
-        return drawBackground;
-    }
-    
-    @Override
-    public void render(int mouseX, int mouseY, float delta) {
-        ItemStack itemStack = getCurrentStack().copy();
-        if (drawBackground) {
-            minecraft.getTextureManager().bindTexture(RECIPE_GUI);
-            blit(this.x - 1, this.y - 1, 0, 222, 18, 18);
-        }
-        boolean highlighted = isHighlighted(mouseX, mouseY);
-        if (drawHighlightedBackground && highlighted) {
-            GlStateManager.disableLighting();
-            GlStateManager.disableDepthTest();
-            GlStateManager.colorMask(true, true, true, false);
-            fillGradient(x, y, x + 16, y + 16, -2130706433, -2130706433);
-            GlStateManager.colorMask(true, true, true, true);
-            GlStateManager.enableLighting();
-            GlStateManager.enableDepthTest();
-        }
-        if (!itemStack.isEmpty()) {
-            if (RoughlyEnoughItemsCore.getConfigManager().getConfig().aprilFoolsFish2019 && !highlighted)
-                itemStack = Items.TROPICAL_FISH.getDefaultStack();
-            GuiLighting.enableForItems();
-            ItemRenderer itemRenderer = minecraft.getItemRenderer();
-            itemRenderer.zOffset = 200.0F;
-            itemRenderer.renderGuiItem(itemStack, x, y);
-            itemRenderer.renderGuiItemOverlay(font, itemStack, x, y, getItemCountOverlay(itemStack));
-            itemRenderer.zOffset = 0.0F;
-        }
-        if (!itemStack.isEmpty() && highlighted && showToolTips)
-            queueTooltip(itemStack, delta);
+        super(x, y, itemStack, drawBackground, showToolTips);
     }
     }
     
     
-    protected void queueTooltip(ItemStack itemStack, float delta) {
-        ScreenHelper.getLastOverlay().addTooltip(QueuedTooltip.create(getTooltip(itemStack)));
+    public ItemSlotWidget(int x, int y, Collection<ItemStack> itemList, boolean drawBackground, boolean showToolTips) {
+        super(x, y, itemList, drawBackground, showToolTips);
     }
     }
     
     
-    protected List<String> getTooltip(ItemStack itemStack) {
-        final String modString = ClientHelper.getInstance().getFormattedModFromItem(itemStack.getItem());
-        List<String> toolTip = Lists.newArrayList(ItemListOverlay.tryGetItemStackToolTip(itemStack, true));
-        toolTip.addAll(getExtraToolTips(itemStack));
-        boolean alreadyHasMod = false;
-        for(String s : toolTip)
-            if (s.equalsIgnoreCase(modString)) {
-                alreadyHasMod = true;
-                break;
-            }
-        if (!alreadyHasMod)
-            toolTip.add(modString);
-        return toolTip;
+    public ItemSlotWidget(int x, int y, List<Renderer> renderers, boolean drawBackground, boolean showToolTips) {
+        super(x, y, renderers, drawBackground, showToolTips);
     }
     }
     
     
-    protected List<String> getExtraToolTips(ItemStack stack) {
-        return Collections.emptyList();
-    }
-    
-    protected String getItemCountOverlay(ItemStack currentStack) {
-        return "";
-    }
-    
-    public ItemStack getCurrentStack() {
-        if (itemList.size() == 0)
-            return new ItemStack(Items.AIR);
-        return itemList.get(MathHelper.floor((System.currentTimeMillis() / 500 % (double) itemList.size()) / 1f));
-    }
-    
-    public void setItemList(List<ItemStack> itemList) {
-        this.itemList = itemList;
-    }
-    
-    @Override
-    public Rectangle getBounds() {
-        return new Rectangle(this.x - 1, this.y - 1, 18, 18);
-    }
-    
-    @Override
-    public boolean mouseClicked(double mouseX, double mouseY, int button) {
-        if (!clickToMoreRecipes)
-            return false;
-        if (getBounds().contains(mouseX, mouseY))
-            if (button == 0)
-                return ClientHelper.getInstance().executeRecipeKeyBind(getCurrentStack());
-            else if (button == 1)
-                return ClientHelper.getInstance().executeUsageKeyBind(getCurrentStack());
-        return false;
-    }
-    
-    @Override
-    public boolean keyPressed(int int_1, int int_2, int int_3) {
-        if (!clickToMoreRecipes)
-            return false;
-        if (getBounds().contains(ClientUtils.getMouseLocation()))
-            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;
+    public ItemSlotWidget(int x, int y, List<ItemStack> itemList, boolean drawBackground, boolean showToolTips, boolean clickToMoreRecipes) {
+        super(x, y, itemList, drawBackground, showToolTips, clickToMoreRecipes);
     }
     }
-    
 }
 }

+ 12 - 2
src/main/java/me/shedaniel/rei/gui/widget/RecipeBaseWidget.java

@@ -2,6 +2,7 @@ package me.shedaniel.rei.gui.widget;
 
 
 import com.mojang.blaze3d.platform.GlStateManager;
 import com.mojang.blaze3d.platform.GlStateManager;
 import me.shedaniel.rei.RoughlyEnoughItemsCore;
 import me.shedaniel.rei.RoughlyEnoughItemsCore;
+import me.shedaniel.rei.client.RecipeScreenType;
 import net.minecraft.client.render.GuiLighting;
 import net.minecraft.client.render.GuiLighting;
 import net.minecraft.util.Identifier;
 import net.minecraft.util.Identifier;
 
 
@@ -12,7 +13,6 @@ import java.util.List;
 public class RecipeBaseWidget extends HighlightableWidget {
 public class RecipeBaseWidget extends HighlightableWidget {
     
     
     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 Color INNER_COLOR = new Color(198, 198, 198);
     
     
     private Rectangle bounds;
     private Rectangle bounds;
     
     
@@ -38,6 +38,8 @@ public class RecipeBaseWidget extends HighlightableWidget {
     
     
     @Override
     @Override
     public void render(int mouseX, int mouseY, float delta) {
     public void render(int mouseX, int mouseY, float delta) {
+        if (!isRendering())
+            return;
         GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F);
         GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F);
         GuiLighting.disable();
         GuiLighting.disable();
         minecraft.getTextureManager().bindTexture(CHEST_GUI_TEXTURE);
         minecraft.getTextureManager().bindTexture(CHEST_GUI_TEXTURE);
@@ -61,7 +63,15 @@ public class RecipeBaseWidget extends HighlightableWidget {
             this.blit(x, y + yy, 106, 128 + textureOffset, 4, thisHeight);
             this.blit(x, y + yy, 106, 128 + textureOffset, 4, thisHeight);
             this.blit(x + width - 4, y + yy, 252, 128 + textureOffset, 4, thisHeight);
             this.blit(x + width - 4, y + yy, 252, 128 + textureOffset, 4, thisHeight);
         }
         }
-        fillGradient(x + 4, y + 4, x + width - 4, y + height - 4, INNER_COLOR.getRGB(), INNER_COLOR.getRGB());
+        fillGradient(x + 4, y + 4, x + width - 4, y + height - 4, getInnerColor(), getInnerColor());
+    }
+    
+    protected boolean isRendering() {
+        return RoughlyEnoughItemsCore.getConfigManager().getConfig().screenType != RecipeScreenType.VILLAGER;
+    }
+    
+    protected int getInnerColor() {
+        return -3750202;
     }
     }
     
     
     protected int getTextureOffset() {
     protected int getTextureOffset() {

+ 26 - 0
src/main/java/me/shedaniel/rei/gui/widget/SlotBaseWidget.java

@@ -0,0 +1,26 @@
+package me.shedaniel.rei.gui.widget;
+
+import java.awt.*;
+
+public class SlotBaseWidget extends RecipeBaseWidget {
+    
+    public SlotBaseWidget(Rectangle bounds) {
+        super(bounds);
+    }
+    
+    @Override
+    public int getInnerColor() {
+        return -7631989;
+    }
+    
+    @Override
+    protected int getTextureOffset() {
+        return -66;
+    }
+    
+    @Override
+    protected boolean isRendering() {
+        return true;
+    }
+    
+}

+ 203 - 0
src/main/java/me/shedaniel/rei/gui/widget/SlotWidget.java

@@ -0,0 +1,203 @@
+package me.shedaniel.rei.gui.widget;
+
+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.Renderable;
+import me.shedaniel.rei.api.Renderer;
+import me.shedaniel.rei.client.ScreenHelper;
+import me.shedaniel.rei.gui.renderables.ItemStackRenderer;
+import net.minecraft.client.gui.Element;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.math.MathHelper;
+
+import java.awt.*;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class SlotWidget extends HighlightableWidget {
+    
+    private static final Identifier RECIPE_GUI = new Identifier("roughlyenoughitems", "textures/gui/recipecontainer.png");
+    private List<Renderer> renderers = new LinkedList<>();
+    private boolean drawBackground, showToolTips, clickToMoreRecipes, drawHighlightedBackground;
+    private int x, y;
+    private static final ItemStackRenderer TROPICAL_FISH_RENDERABLE = Renderable.fromItemStack(Items.TROPICAL_FISH.getDefaultStack());
+    
+    public SlotWidget(int x, int y, ItemStack itemStack, boolean drawBackground, boolean showToolTips) {
+        this(x, y, Collections.singletonList(itemStack), drawBackground, showToolTips);
+    }
+    
+    public SlotWidget(int x, int y, Collection<ItemStack> itemList, boolean drawBackground, boolean showToolTips) {
+        this(x, y, itemList.stream().map(Renderable::fromItemStack).collect(Collectors.toList()), drawBackground, showToolTips);
+    }
+    
+    public SlotWidget(int x, int y, List<Renderer> renderers, boolean drawBackground, boolean showToolTips) {
+        this.renderers = renderers;
+        this.drawBackground = drawBackground;
+        this.showToolTips = showToolTips;
+        this.x = x;
+        this.y = y;
+        this.clickToMoreRecipes = false;
+        this.drawHighlightedBackground = true;
+    }
+    
+    public SlotWidget(int x, int y, List<ItemStack> itemList, boolean drawBackground, boolean showToolTips, boolean clickToMoreRecipes) {
+        this(x, y, itemList, drawBackground, showToolTips);
+        this.clickToMoreRecipes = clickToMoreRecipes;
+    }
+    
+    public boolean isShowToolTips() {
+        return showToolTips;
+    }
+    
+    public void setShowToolTips(boolean showToolTips) {
+        this.showToolTips = showToolTips;
+    }
+    
+    public boolean isClickToMoreRecipes() {
+        return clickToMoreRecipes;
+    }
+    
+    public void setClickToMoreRecipes(boolean clickToMoreRecipes) {
+        this.clickToMoreRecipes = clickToMoreRecipes;
+    }
+    
+    public boolean isDrawHighlightedBackground() {
+        return drawHighlightedBackground;
+    }
+    
+    public void setDrawHighlightedBackground(boolean drawHighlightedBackground) {
+        this.drawHighlightedBackground = drawHighlightedBackground;
+    }
+    
+    @Override
+    public List<? extends Element> children() {
+        return Collections.emptyList();
+    }
+    
+    public boolean isDrawBackground() {
+        return drawBackground;
+    }
+    
+    public void setDrawBackground(boolean drawBackground) {
+        this.drawBackground = drawBackground;
+    }
+    
+    @Override
+    public void render(int mouseX, int mouseY, float delta) {
+        Renderer renderer = getCurrentRenderer();
+        if (drawBackground) {
+            minecraft.getTextureManager().bindTexture(RECIPE_GUI);
+            blit(this.x - 1, this.y - 1, 0, 222, 18, 18);
+        }
+        boolean highlighted = isHighlighted(mouseX, mouseY);
+        if (drawHighlightedBackground && highlighted) {
+            GlStateManager.disableLighting();
+            GlStateManager.disableDepthTest();
+            GlStateManager.colorMask(true, true, true, false);
+            fillGradient(x, y, x + 16, y + 16, -2130706433, -2130706433);
+            GlStateManager.colorMask(true, true, true, true);
+            GlStateManager.enableLighting();
+            GlStateManager.enableDepthTest();
+        }
+        if (isCurrentRendererItem() && !getCurrentItemStack().isEmpty()) {
+            if (RoughlyEnoughItemsCore.getConfigManager().getConfig().aprilFoolsFish2019 && !highlighted)
+                renderer = TROPICAL_FISH_RENDERABLE;
+            renderer.setBlitOffset(200);
+            renderer.render(x + 8, y + 6, mouseX, mouseY, delta);
+            if (!getCurrentItemStack().isEmpty() && highlighted && showToolTips)
+                queueTooltip(getCurrentItemStack(), delta);
+        } else {
+            renderer.setBlitOffset(200);
+            renderer.render(x + 8, y + 6, mouseX, mouseY, delta);
+        }
+    }
+    
+    protected void queueTooltip(ItemStack itemStack, float delta) {
+        ScreenHelper.getLastOverlay().addTooltip(QueuedTooltip.create(getTooltip(itemStack)));
+    }
+    
+    protected List<String> getTooltip(ItemStack itemStack) {
+        final String modString = ClientHelper.getInstance().getFormattedModFromItem(itemStack.getItem());
+        List<String> toolTip = Lists.newArrayList(ItemListOverlay.tryGetItemStackToolTip(itemStack, true));
+        toolTip.addAll(getExtraToolTips(itemStack));
+        boolean alreadyHasMod = false;
+        for(String s : toolTip)
+            if (s.equalsIgnoreCase(modString)) {
+                alreadyHasMod = true;
+                break;
+            }
+        if (!alreadyHasMod)
+            toolTip.add(modString);
+        return toolTip;
+    }
+    
+    protected List<String> getExtraToolTips(ItemStack stack) {
+        return Collections.emptyList();
+    }
+    
+    protected String getItemCountOverlay(ItemStack currentStack) {
+        return "";
+    }
+    
+    public ItemStack getCurrentItemStack() {
+        if (getCurrentRenderer() instanceof ItemStackRenderer)
+            return ((ItemStackRenderer) getCurrentRenderer()).getItemStack();
+        return ItemStack.EMPTY;
+    }
+    
+    public Renderer getCurrentRenderer() {
+        if (renderers.size() == 0)
+            return Renderable.empty();
+        return renderers.get(MathHelper.floor((System.currentTimeMillis() / 500 % (double) renderers.size()) / 1f));
+    }
+    
+    public void setItemList(List<ItemStack> itemList) {
+        this.setRenderers(itemList.stream().map(Renderable::fromItemStack).collect(Collectors.toList()));
+    }
+    
+    public void setRenderers(List<Renderer> renderers) {
+        this.renderers = renderers;
+    }
+    
+    @Override
+    public Rectangle getBounds() {
+        return new Rectangle(this.x - 1, this.y - 1, 18, 18);
+    }
+    
+    @Override
+    public boolean mouseClicked(double mouseX, double mouseY, int button) {
+        if (!clickToMoreRecipes)
+            return false;
+        if (isCurrentRendererItem() && getBounds().contains(mouseX, mouseY))
+            if (button == 0)
+                return ClientHelper.getInstance().executeRecipeKeyBind(getCurrentItemStack());
+            else if (button == 1)
+                return ClientHelper.getInstance().executeUsageKeyBind(getCurrentItemStack());
+        return false;
+    }
+    
+    public boolean isCurrentRendererItem() {
+        return getCurrentRenderer() instanceof ItemStackRenderer;
+    }
+    
+    @Override
+    public boolean keyPressed(int int_1, int int_2, int int_3) {
+        if (!clickToMoreRecipes)
+            return false;
+        if (isCurrentRendererItem() && getBounds().contains(ClientUtils.getMouseLocation()))
+            if (ClientHelper.getInstance().getRecipeKeyBinding().matchesKey(int_1, int_2))
+                return ClientHelper.getInstance().executeRecipeKeyBind(getCurrentItemStack());
+            else if (ClientHelper.getInstance().getUsageKeyBinding().matchesKey(int_1, int_2))
+                return ClientHelper.getInstance().executeUsageKeyBind(getCurrentItemStack());
+        return false;
+    }
+    
+}

+ 19 - 24
src/main/java/me/shedaniel/rei/gui/widget/TabWidget.java

@@ -1,11 +1,12 @@
 package me.shedaniel.rei.gui.widget;
 package me.shedaniel.rei.gui.widget;
 
 
 import com.mojang.blaze3d.platform.GlStateManager;
 import com.mojang.blaze3d.platform.GlStateManager;
+import me.shedaniel.rei.api.ClientHelper;
+import me.shedaniel.rei.api.RecipeCategory;
+import me.shedaniel.rei.api.Renderer;
 import me.shedaniel.rei.client.ScreenHelper;
 import me.shedaniel.rei.client.ScreenHelper;
 import me.shedaniel.rei.gui.RecipeViewingScreen;
 import me.shedaniel.rei.gui.RecipeViewingScreen;
 import net.minecraft.client.render.GuiLighting;
 import net.minecraft.client.render.GuiLighting;
-import net.minecraft.client.render.item.ItemRenderer;
-import net.minecraft.item.ItemStack;
 import net.minecraft.util.Identifier;
 import net.minecraft.util.Identifier;
 
 
 import java.awt.*;
 import java.awt.*;
@@ -17,28 +18,26 @@ public class TabWidget extends HighlightableWidget {
     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 boolean shown = false, selected = false;
     public boolean shown = false, selected = false;
-    public ItemStack item;
+    public Renderer renderer;
     public int id;
     public int id;
-    public RecipeViewingScreen recipeViewingWidget;
     public String categoryName;
     public String categoryName;
     public Rectangle bounds;
     public Rectangle bounds;
-    private ItemRenderer itemRenderer;
+    public RecipeCategory category;
     
     
-    public TabWidget(int id, RecipeViewingScreen recipeViewingWidget, Rectangle bounds) {
+    public TabWidget(int id, Rectangle bounds) {
         this.id = id;
         this.id = id;
-        this.recipeViewingWidget = recipeViewingWidget;
         this.bounds = bounds;
         this.bounds = bounds;
-        this.itemRenderer = minecraft.getItemRenderer();
     }
     }
     
     
-    public void setItem(ItemStack item, String categoryName, boolean selected) {
-        if (item == null) {
+    public void setRenderer(RecipeCategory category, Renderer renderable, String categoryName, boolean selected) {
+        if (renderable == null) {
             shown = false;
             shown = false;
-            this.item = null;
+            this.renderer = null;
         } else {
         } else {
             shown = true;
             shown = true;
-            this.item = item;
+            this.renderer = renderable;
         }
         }
+        this.category = category;
         this.selected = selected;
         this.selected = selected;
         this.categoryName = categoryName;
         this.categoryName = categoryName;
     }
     }
@@ -55,8 +54,8 @@ public class TabWidget extends HighlightableWidget {
         return shown;
         return shown;
     }
     }
     
     
-    public ItemStack getItemStack() {
-        return item;
+    public Renderer getRenderer() {
+        return renderer;
     }
     }
     
     
     @Override
     @Override
@@ -67,26 +66,22 @@ public class TabWidget extends HighlightableWidget {
     @Override
     @Override
     public void render(int mouseX, int mouseY, float delta) {
     public void render(int mouseX, int mouseY, float delta) {
         if (shown) {
         if (shown) {
-            int l = (int) this.bounds.getCenterX() - 8, i1 = (int) this.bounds.getCenterY() - 6;
             GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F);
             GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F);
             GuiLighting.disable();
             GuiLighting.disable();
             minecraft.getTextureManager().bindTexture(CHEST_GUI_TEXTURE);
             minecraft.getTextureManager().bindTexture(CHEST_GUI_TEXTURE);
             this.blit(bounds.x, bounds.y + 2, selected ? 28 : 0, 192, 28, (selected ? 30 : 27));
             this.blit(bounds.x, bounds.y + 2, selected ? 28 : 0, 192, 28, (selected ? 30 : 27));
-            this.blitOffset = 100;
-            this.itemRenderer.zOffset = 100.0F;
-            GuiLighting.enableForItems();
-            this.itemRenderer.renderGuiItem(getItemStack(), l, i1);
-            this.itemRenderer.renderGuiItemOverlay(minecraft.textRenderer, getItemStack(), l, i1);
-            GlStateManager.disableLighting();
-            this.itemRenderer.zOffset = 0.0F;
-            this.blitOffset = 0;
+            renderer.setBlitOffset(100);
+            renderer.render((int) bounds.getCenterX(), (int) bounds.getCenterY(), mouseX, mouseY, delta);
             if (isHighlighted(mouseX, mouseY))
             if (isHighlighted(mouseX, mouseY))
                 drawTooltip();
                 drawTooltip();
         }
         }
     }
     }
     
     
     private void drawTooltip() {
     private void drawTooltip() {
-        ScreenHelper.getLastOverlay().addTooltip(QueuedTooltip.create(categoryName));
+        if (this.minecraft.options.advancedItemTooltips)
+            ScreenHelper.getLastOverlay().addTooltip(QueuedTooltip.create(categoryName, "§8" + category.getIdentifier().toString(), ClientHelper.getInstance().getFormattedModFromIdentifier(category.getIdentifier())));
+        else
+            ScreenHelper.getLastOverlay().addTooltip(QueuedTooltip.create(categoryName, ClientHelper.getInstance().getFormattedModFromIdentifier(category.getIdentifier())));
     }
     }
     
     
     @Override
     @Override

+ 11 - 4
src/main/java/me/shedaniel/rei/plugin/DefaultBlastingCategory.java

@@ -2,7 +2,9 @@ package me.shedaniel.rei.plugin;
 
 
 import com.mojang.blaze3d.platform.GlStateManager;
 import com.mojang.blaze3d.platform.GlStateManager;
 import me.shedaniel.rei.api.RecipeCategory;
 import me.shedaniel.rei.api.RecipeCategory;
-import me.shedaniel.rei.gui.widget.ItemSlotWidget;
+import me.shedaniel.rei.api.Renderable;
+import me.shedaniel.rei.gui.renderables.RecipeRenderer;
+import me.shedaniel.rei.gui.widget.SlotWidget;
 import me.shedaniel.rei.gui.widget.RecipeBaseWidget;
 import me.shedaniel.rei.gui.widget.RecipeBaseWidget;
 import me.shedaniel.rei.gui.widget.Widget;
 import me.shedaniel.rei.gui.widget.Widget;
 import net.minecraft.block.Blocks;
 import net.minecraft.block.Blocks;
@@ -38,6 +40,11 @@ public class DefaultBlastingCategory implements RecipeCategory<DefaultBlastingDi
         return I18n.translate("category.rei.blasting");
         return I18n.translate("category.rei.blasting");
     }
     }
     
     
+    @Override
+    public RecipeRenderer getSimpleRenderer(DefaultBlastingDisplay recipe) {
+        return Renderable.fromRecipe(() -> Arrays.asList(recipe.getInput().get(0)), recipe::getOutput);
+    }
+    
     @Override
     @Override
     public List<Widget> setupDisplay(Supplier<DefaultBlastingDisplay> recipeDisplaySupplier, Rectangle bounds) {
     public List<Widget> setupDisplay(Supplier<DefaultBlastingDisplay> recipeDisplaySupplier, Rectangle bounds) {
         final DefaultBlastingDisplay recipeDisplay = recipeDisplaySupplier.get();
         final DefaultBlastingDisplay recipeDisplay = recipeDisplaySupplier.get();
@@ -57,14 +64,14 @@ public class DefaultBlastingCategory implements RecipeCategory<DefaultBlastingDi
             }
             }
         }));
         }));
         List<List<ItemStack>> input = recipeDisplay.getInput();
         List<List<ItemStack>> input = recipeDisplay.getInput();
-        widgets.add(new ItemSlotWidget(startPoint.x + 1, startPoint.y + 1, input.get(0), true, true, true));
-        widgets.add(new ItemSlotWidget(startPoint.x + 1, startPoint.y + 37, recipeDisplay.getFuel(), true, true, true) {
+        widgets.add(new SlotWidget(startPoint.x + 1, startPoint.y + 1, input.get(0), true, true, true));
+        widgets.add(new SlotWidget(startPoint.x + 1, startPoint.y + 37, recipeDisplay.getFuel(), true, true, true) {
             @Override
             @Override
             protected List<String> getExtraToolTips(ItemStack stack) {
             protected List<String> getExtraToolTips(ItemStack stack) {
                 return Arrays.asList(I18n.translate("category.rei.smelting.fuel"));
                 return Arrays.asList(I18n.translate("category.rei.smelting.fuel"));
             }
             }
         });
         });
-        widgets.add(new ItemSlotWidget(startPoint.x + 61, startPoint.y + 19, recipeDisplay.getOutput(), false, true, true));
+        widgets.add(new SlotWidget(startPoint.x + 61, startPoint.y + 19, recipeDisplay.getOutput(), false, true, true));
         return widgets;
         return widgets;
     }
     }
     
     

+ 7 - 7
src/main/java/me/shedaniel/rei/plugin/DefaultBrewingCategory.java

@@ -2,7 +2,7 @@ package me.shedaniel.rei.plugin;
 
 
 import com.mojang.blaze3d.platform.GlStateManager;
 import com.mojang.blaze3d.platform.GlStateManager;
 import me.shedaniel.rei.api.RecipeCategory;
 import me.shedaniel.rei.api.RecipeCategory;
-import me.shedaniel.rei.gui.widget.ItemSlotWidget;
+import me.shedaniel.rei.gui.widget.SlotWidget;
 import me.shedaniel.rei.gui.widget.RecipeBaseWidget;
 import me.shedaniel.rei.gui.widget.RecipeBaseWidget;
 import me.shedaniel.rei.gui.widget.Widget;
 import me.shedaniel.rei.gui.widget.Widget;
 import net.minecraft.block.Blocks;
 import net.minecraft.block.Blocks;
@@ -55,32 +55,32 @@ public class DefaultBrewingCategory implements RecipeCategory<DefaultBrewingDisp
                 blit(startPoint.x + 44, startPoint.y + 28, 103, 163, width, 4);
                 blit(startPoint.x + 44, startPoint.y + 28, 103, 163, width, 4);
             }
             }
         }));
         }));
-        widgets.add(new ItemSlotWidget(startPoint.x + 1, startPoint.y + 1, Arrays.asList(new ItemStack(Items.BLAZE_POWDER)), false, true, true));
-        widgets.add(new ItemSlotWidget(startPoint.x + 63, startPoint.y + 1, recipeDisplay.getInput().get(0), false, true, true) {
+        widgets.add(new SlotWidget(startPoint.x + 1, startPoint.y + 1, Arrays.asList(new ItemStack(Items.BLAZE_POWDER)), false, true, true));
+        widgets.add(new SlotWidget(startPoint.x + 63, startPoint.y + 1, recipeDisplay.getInput().get(0), false, true, true) {
             @Override
             @Override
             protected List<String> getExtraToolTips(ItemStack stack) {
             protected List<String> getExtraToolTips(ItemStack stack) {
                 return Arrays.asList(I18n.translate("category.rei.brewing.input"));
                 return Arrays.asList(I18n.translate("category.rei.brewing.input"));
             }
             }
         });
         });
-        widgets.add(new ItemSlotWidget(startPoint.x + 40, startPoint.y + 1, recipeDisplay.getInput().get(1), false, true, true) {
+        widgets.add(new SlotWidget(startPoint.x + 40, startPoint.y + 1, recipeDisplay.getInput().get(1), false, true, true) {
             @Override
             @Override
             protected List<String> getExtraToolTips(ItemStack stack) {
             protected List<String> getExtraToolTips(ItemStack stack) {
                 return Arrays.asList(I18n.translate("category.rei.brewing.reactant"));
                 return Arrays.asList(I18n.translate("category.rei.brewing.reactant"));
             }
             }
         });
         });
-        widgets.add(new ItemSlotWidget(startPoint.x + 40, startPoint.y + 35, recipeDisplay.getOutput(0), false, true, true) {
+        widgets.add(new SlotWidget(startPoint.x + 40, startPoint.y + 35, recipeDisplay.getOutput(0), false, true, true) {
             @Override
             @Override
             protected List<String> getExtraToolTips(ItemStack stack) {
             protected List<String> getExtraToolTips(ItemStack stack) {
                 return Arrays.asList(I18n.translate("category.rei.brewing.result"));
                 return Arrays.asList(I18n.translate("category.rei.brewing.result"));
             }
             }
         });
         });
-        widgets.add(new ItemSlotWidget(startPoint.x + 63, startPoint.y + 42, recipeDisplay.getOutput(1), false, true, true) {
+        widgets.add(new SlotWidget(startPoint.x + 63, startPoint.y + 42, recipeDisplay.getOutput(1), false, true, true) {
             @Override
             @Override
             protected List<String> getExtraToolTips(ItemStack stack) {
             protected List<String> getExtraToolTips(ItemStack stack) {
                 return Arrays.asList(I18n.translate("category.rei.brewing.result"));
                 return Arrays.asList(I18n.translate("category.rei.brewing.result"));
             }
             }
         });
         });
-        widgets.add(new ItemSlotWidget(startPoint.x + 86, startPoint.y + 35, recipeDisplay.getOutput(2), false, true, true) {
+        widgets.add(new SlotWidget(startPoint.x + 86, startPoint.y + 35, recipeDisplay.getOutput(2), false, true, true) {
             @Override
             @Override
             protected List<String> getExtraToolTips(ItemStack stack) {
             protected List<String> getExtraToolTips(ItemStack stack) {
                 return Arrays.asList(I18n.translate("category.rei.brewing.result"));
                 return Arrays.asList(I18n.translate("category.rei.brewing.result"));

+ 3 - 3
src/main/java/me/shedaniel/rei/plugin/DefaultCampfireCategory.java

@@ -2,7 +2,7 @@ package me.shedaniel.rei.plugin;
 
 
 import com.mojang.blaze3d.platform.GlStateManager;
 import com.mojang.blaze3d.platform.GlStateManager;
 import me.shedaniel.rei.api.RecipeCategory;
 import me.shedaniel.rei.api.RecipeCategory;
-import me.shedaniel.rei.gui.widget.ItemSlotWidget;
+import me.shedaniel.rei.gui.widget.SlotWidget;
 import me.shedaniel.rei.gui.widget.RecipeBaseWidget;
 import me.shedaniel.rei.gui.widget.RecipeBaseWidget;
 import me.shedaniel.rei.gui.widget.Widget;
 import me.shedaniel.rei.gui.widget.Widget;
 import net.minecraft.block.Blocks;
 import net.minecraft.block.Blocks;
@@ -58,8 +58,8 @@ public class DefaultCampfireCategory implements RecipeCategory<DefaultCampfireDi
                 MinecraftClient.getInstance().textRenderer.draw(text, bounds.x + bounds.width - length - 5, startPoint.y + 54 - 8, 4210752);
                 MinecraftClient.getInstance().textRenderer.draw(text, bounds.x + bounds.width - length - 5, startPoint.y + 54 - 8, 4210752);
             }
             }
         }));
         }));
-        widgets.add(new ItemSlotWidget(startPoint.x + 1, startPoint.y + 11, recipeDisplaySupplier.get().getInput().get(0), true, true, true));
-        widgets.add(new ItemSlotWidget(startPoint.x + 61, startPoint.y + 19, recipeDisplaySupplier.get().getOutput(), false, true, true));
+        widgets.add(new SlotWidget(startPoint.x + 1, startPoint.y + 11, recipeDisplaySupplier.get().getInput().get(0), true, true, true));
+        widgets.add(new SlotWidget(startPoint.x + 61, startPoint.y + 19, recipeDisplaySupplier.get().getOutput(), false, true, true));
         return widgets;
         return widgets;
     }
     }
     
     

+ 4 - 4
src/main/java/me/shedaniel/rei/plugin/DefaultCraftingCategory.java

@@ -3,7 +3,7 @@ package me.shedaniel.rei.plugin;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Lists;
 import com.mojang.blaze3d.platform.GlStateManager;
 import com.mojang.blaze3d.platform.GlStateManager;
 import me.shedaniel.rei.api.RecipeCategory;
 import me.shedaniel.rei.api.RecipeCategory;
-import me.shedaniel.rei.gui.widget.ItemSlotWidget;
+import me.shedaniel.rei.gui.widget.SlotWidget;
 import me.shedaniel.rei.gui.widget.RecipeBaseWidget;
 import me.shedaniel.rei.gui.widget.RecipeBaseWidget;
 import me.shedaniel.rei.gui.widget.Widget;
 import me.shedaniel.rei.gui.widget.Widget;
 import net.minecraft.block.Blocks;
 import net.minecraft.block.Blocks;
@@ -52,10 +52,10 @@ public class DefaultCraftingCategory implements RecipeCategory<DefaultCraftingDi
             }
             }
         }));
         }));
         List<List<ItemStack>> input = recipeDisplaySupplier.get().getInput();
         List<List<ItemStack>> input = recipeDisplaySupplier.get().getInput();
-        List<ItemSlotWidget> slots = Lists.newArrayList();
+        List<SlotWidget> slots = Lists.newArrayList();
         for(int y = 0; y < 3; y++)
         for(int y = 0; y < 3; y++)
             for(int x = 0; x < 3; x++)
             for(int x = 0; x < 3; x++)
-                slots.add(new ItemSlotWidget(startPoint.x + 1 + x * 18, startPoint.y + 1 + y * 18, Lists.newArrayList(), true, true, true));
+                slots.add(new SlotWidget(startPoint.x + 1 + x * 18, startPoint.y + 1 + y * 18, Lists.newArrayList(), true, true, true));
         for(int i = 0; i < input.size(); i++) {
         for(int i = 0; i < input.size(); i++) {
             if (recipeDisplaySupplier.get() instanceof DefaultShapedDisplay) {
             if (recipeDisplaySupplier.get() instanceof DefaultShapedDisplay) {
                 if (!input.get(i).isEmpty())
                 if (!input.get(i).isEmpty())
@@ -64,7 +64,7 @@ public class DefaultCraftingCategory implements RecipeCategory<DefaultCraftingDi
                 slots.get(i).setItemList(input.get(i));
                 slots.get(i).setItemList(input.get(i));
         }
         }
         widgets.addAll(slots);
         widgets.addAll(slots);
-        widgets.add(new ItemSlotWidget(startPoint.x + 95, startPoint.y + 19, recipeDisplaySupplier.get().getOutput(), false, true, true) {
+        widgets.add(new SlotWidget(startPoint.x + 95, startPoint.y + 19, recipeDisplaySupplier.get().getOutput(), false, true, true) {
             @Override
             @Override
             protected String getItemCountOverlay(ItemStack currentStack) {
             protected String getItemCountOverlay(ItemStack currentStack) {
                 if (currentStack.getAmount() == 1)
                 if (currentStack.getAmount() == 1)

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

@@ -5,6 +5,7 @@ import me.shedaniel.rei.RoughlyEnoughItemsCore;
 import me.shedaniel.rei.api.*;
 import me.shedaniel.rei.api.*;
 import me.shedaniel.rei.client.ScreenHelper;
 import me.shedaniel.rei.client.ScreenHelper;
 import me.shedaniel.rei.gui.RecipeViewingScreen;
 import me.shedaniel.rei.gui.RecipeViewingScreen;
+import me.shedaniel.rei.gui.VillagerRecipeViewingScreen;
 import me.shedaniel.rei.listeners.ContainerScreenHooks;
 import me.shedaniel.rei.listeners.ContainerScreenHooks;
 import me.shedaniel.rei.listeners.RecipeBookGuiHooks;
 import me.shedaniel.rei.listeners.RecipeBookGuiHooks;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.MinecraftClient;
@@ -41,13 +42,13 @@ import java.util.List;
 
 
 public class DefaultPlugin implements REIPluginEntry {
 public class DefaultPlugin implements REIPluginEntry {
     
     
-    public static final Identifier CRAFTING = new Identifier("roughlyenoughitems", "plugins/crafting");
-    public static final Identifier SMELTING = new Identifier("roughlyenoughitems", "plugins/smelting");
-    public static final Identifier SMOKING = new Identifier("roughlyenoughitems", "plugins/smoking");
-    public static final Identifier BLASTING = new Identifier("roughlyenoughitems", "plugins/blasting");
-    public static final Identifier CAMPFIRE = new Identifier("roughlyenoughitems", "plugins/campfire");
-    public static final Identifier STONE_CUTTING = new Identifier("roughlyenoughitems", "plugins/stone_cutting");
-    public static final Identifier BREWING = new Identifier("roughlyenoughitems", "plugins/brewing");
+    public static final Identifier CRAFTING = new Identifier("minecraft", "plugins/crafting");
+    public static final Identifier SMELTING = new Identifier("minecraft", "plugins/smelting");
+    public static final Identifier SMOKING = new Identifier("minecraft", "plugins/smoking");
+    public static final Identifier BLASTING = new Identifier("minecraft", "plugins/blasting");
+    public static final Identifier CAMPFIRE = new Identifier("minecraft", "plugins/campfire");
+    public static final Identifier STONE_CUTTING = new Identifier("minecraft", "plugins/stone_cutting");
+    public static final Identifier BREWING = new Identifier("minecraft", "plugins/brewing");
     public static final Identifier PLUGIN = new Identifier("roughlyenoughitems", "default_plugin");
     public static final Identifier PLUGIN = new Identifier("roughlyenoughitems", "default_plugin");
     
     
     private static final List<DefaultBrewingDisplay> BREWING_DISPLAYS = Lists.newArrayList();
     private static final List<DefaultBrewingDisplay> BREWING_DISPLAYS = Lists.newArrayList();
@@ -81,7 +82,7 @@ public class DefaultPlugin implements REIPluginEntry {
             }
             }
         });
         });
         Registry.ENCHANTMENT.forEach(enchantment -> {
         Registry.ENCHANTMENT.forEach(enchantment -> {
-            for(int i = enchantment.getMinimumLevel(); i < enchantment.getMaximumLevel(); i++) {
+            for(int i = enchantment.getMinimumLevel(); i <= enchantment.getMaximumLevel(); i++) {
                 Map<Enchantment, Integer> map = new HashMap<>();
                 Map<Enchantment, Integer> map = new HashMap<>();
                 map.put(enchantment, i);
                 map.put(enchantment, i);
                 ItemStack itemStack = new ItemStack(Items.ENCHANTED_BOOK);
                 ItemStack itemStack = new ItemStack(Items.ENCHANTED_BOOK);
@@ -192,6 +193,28 @@ public class DefaultPlugin implements REIPluginEntry {
                 return -1.0f;
                 return -1.0f;
             }
             }
         });
         });
+        displayHelper.registerBoundsHandler(new DisplayHelper.DisplayBoundsHandler<VillagerRecipeViewingScreen>() {
+            @Override
+            public Class getBaseSupportedClass() {
+                return VillagerRecipeViewingScreen.class;
+            }
+            
+            @Override
+            public Rectangle getLeftBounds(VillagerRecipeViewingScreen screen) {
+                return new Rectangle(2, 0, ((VillagerRecipeViewingScreen) screen).bounds.x - 4, MinecraftClient.getInstance().window.getScaledHeight());
+            }
+            
+            @Override
+            public Rectangle getRightBounds(VillagerRecipeViewingScreen screen) {
+                int startX = ((VillagerRecipeViewingScreen) screen).bounds.x + ((VillagerRecipeViewingScreen) screen).bounds.width + 2;
+                return new Rectangle(startX, 0, MinecraftClient.getInstance().window.getScaledWidth() - startX - 2, MinecraftClient.getInstance().window.getScaledHeight());
+            }
+            
+            @Override
+            public float getPriority() {
+                return -1.0f;
+            }
+        });
         displayHelper.registerBoundsHandler(new DisplayHelper.DisplayBoundsHandler<CreativePlayerInventoryScreen>() {
         displayHelper.registerBoundsHandler(new DisplayHelper.DisplayBoundsHandler<CreativePlayerInventoryScreen>() {
             @Override
             @Override
             public Class getBaseSupportedClass() {
             public Class getBaseSupportedClass() {

+ 11 - 4
src/main/java/me/shedaniel/rei/plugin/DefaultSmeltingCategory.java

@@ -2,7 +2,9 @@ package me.shedaniel.rei.plugin;
 
 
 import com.mojang.blaze3d.platform.GlStateManager;
 import com.mojang.blaze3d.platform.GlStateManager;
 import me.shedaniel.rei.api.RecipeCategory;
 import me.shedaniel.rei.api.RecipeCategory;
-import me.shedaniel.rei.gui.widget.ItemSlotWidget;
+import me.shedaniel.rei.api.Renderable;
+import me.shedaniel.rei.gui.renderables.RecipeRenderer;
+import me.shedaniel.rei.gui.widget.SlotWidget;
 import me.shedaniel.rei.gui.widget.RecipeBaseWidget;
 import me.shedaniel.rei.gui.widget.RecipeBaseWidget;
 import me.shedaniel.rei.gui.widget.Widget;
 import me.shedaniel.rei.gui.widget.Widget;
 import net.minecraft.block.Blocks;
 import net.minecraft.block.Blocks;
@@ -38,6 +40,11 @@ public class DefaultSmeltingCategory implements RecipeCategory<DefaultSmeltingDi
         return I18n.translate("category.rei.smelting");
         return I18n.translate("category.rei.smelting");
     }
     }
     
     
+    @Override
+    public RecipeRenderer getSimpleRenderer(DefaultSmeltingDisplay recipe) {
+        return Renderable.fromRecipe(() -> Arrays.asList(recipe.getInput().get(0)), recipe::getOutput);
+    }
+    
     @Override
     @Override
     public List<Widget> setupDisplay(Supplier<DefaultSmeltingDisplay> recipeDisplaySupplier, Rectangle bounds) {
     public List<Widget> setupDisplay(Supplier<DefaultSmeltingDisplay> recipeDisplaySupplier, Rectangle bounds) {
         Point startPoint = new Point((int) bounds.getCenterX() - 41, (int) bounds.getCenterY() - 27);
         Point startPoint = new Point((int) bounds.getCenterX() - 41, (int) bounds.getCenterY() - 27);
@@ -56,14 +63,14 @@ public class DefaultSmeltingCategory implements RecipeCategory<DefaultSmeltingDi
             }
             }
         }));
         }));
         List<List<ItemStack>> input = recipeDisplaySupplier.get().getInput();
         List<List<ItemStack>> input = recipeDisplaySupplier.get().getInput();
-        widgets.add(new ItemSlotWidget(startPoint.x + 1, startPoint.y + 1, input.get(0), true, true, true));
-        widgets.add(new ItemSlotWidget(startPoint.x + 1, startPoint.y + 37, recipeDisplaySupplier.get().getFuel(), true, true, true) {
+        widgets.add(new SlotWidget(startPoint.x + 1, startPoint.y + 1, input.get(0), true, true, true));
+        widgets.add(new SlotWidget(startPoint.x + 1, startPoint.y + 37, recipeDisplaySupplier.get().getFuel(), true, true, true) {
             @Override
             @Override
             protected List<String> getExtraToolTips(ItemStack stack) {
             protected List<String> getExtraToolTips(ItemStack stack) {
                 return Arrays.asList(I18n.translate("category.rei.smelting.fuel"));
                 return Arrays.asList(I18n.translate("category.rei.smelting.fuel"));
             }
             }
         });
         });
-        widgets.add(new ItemSlotWidget(startPoint.x + 61, startPoint.y + 19, recipeDisplaySupplier.get().getOutput(), false, true, true));
+        widgets.add(new SlotWidget(startPoint.x + 61, startPoint.y + 19, recipeDisplaySupplier.get().getOutput(), false, true, true));
         return widgets;
         return widgets;
     }
     }
     
     

+ 11 - 4
src/main/java/me/shedaniel/rei/plugin/DefaultSmokingCategory.java

@@ -2,7 +2,9 @@ package me.shedaniel.rei.plugin;
 
 
 import com.mojang.blaze3d.platform.GlStateManager;
 import com.mojang.blaze3d.platform.GlStateManager;
 import me.shedaniel.rei.api.RecipeCategory;
 import me.shedaniel.rei.api.RecipeCategory;
-import me.shedaniel.rei.gui.widget.ItemSlotWidget;
+import me.shedaniel.rei.api.Renderable;
+import me.shedaniel.rei.gui.renderables.RecipeRenderer;
+import me.shedaniel.rei.gui.widget.SlotWidget;
 import me.shedaniel.rei.gui.widget.RecipeBaseWidget;
 import me.shedaniel.rei.gui.widget.RecipeBaseWidget;
 import me.shedaniel.rei.gui.widget.Widget;
 import me.shedaniel.rei.gui.widget.Widget;
 import net.minecraft.block.Blocks;
 import net.minecraft.block.Blocks;
@@ -38,6 +40,11 @@ public class DefaultSmokingCategory implements RecipeCategory<DefaultSmokingDisp
         return I18n.translate("category.rei.smoking");
         return I18n.translate("category.rei.smoking");
     }
     }
     
     
+    @Override
+    public RecipeRenderer getSimpleRenderer(DefaultSmokingDisplay recipe) {
+        return Renderable.fromRecipe(() -> Arrays.asList(recipe.getInput().get(0)), recipe::getOutput);
+    }
+    
     @Override
     @Override
     public List<Widget> setupDisplay(Supplier<DefaultSmokingDisplay> recipeDisplaySupplier, Rectangle bounds) {
     public List<Widget> setupDisplay(Supplier<DefaultSmokingDisplay> recipeDisplaySupplier, Rectangle bounds) {
         Point startPoint = new Point((int) bounds.getCenterX() - 41, (int) bounds.getCenterY() - 27);
         Point startPoint = new Point((int) bounds.getCenterX() - 41, (int) bounds.getCenterY() - 27);
@@ -56,14 +63,14 @@ public class DefaultSmokingCategory implements RecipeCategory<DefaultSmokingDisp
             }
             }
         }));
         }));
         List<List<ItemStack>> input = recipeDisplaySupplier.get().getInput();
         List<List<ItemStack>> input = recipeDisplaySupplier.get().getInput();
-        widgets.add(new ItemSlotWidget(startPoint.x + 1, startPoint.y + 1, input.get(0), true, true, true));
-        widgets.add(new ItemSlotWidget(startPoint.x + 1, startPoint.y + 37, recipeDisplaySupplier.get().getFuel(), true, true, true) {
+        widgets.add(new SlotWidget(startPoint.x + 1, startPoint.y + 1, input.get(0), true, true, true));
+        widgets.add(new SlotWidget(startPoint.x + 1, startPoint.y + 37, recipeDisplaySupplier.get().getFuel(), true, true, true) {
             @Override
             @Override
             protected List<String> getExtraToolTips(ItemStack stack) {
             protected List<String> getExtraToolTips(ItemStack stack) {
                 return Arrays.asList(I18n.translate("category.rei.smelting.fuel"));
                 return Arrays.asList(I18n.translate("category.rei.smelting.fuel"));
             }
             }
         });
         });
-        widgets.add(new ItemSlotWidget(startPoint.x + 61, startPoint.y + 19, recipeDisplaySupplier.get().getOutput(), false, true, true));
+        widgets.add(new SlotWidget(startPoint.x + 61, startPoint.y + 19, recipeDisplaySupplier.get().getOutput(), false, true, true));
         return widgets;
         return widgets;
     }
     }
     
     

+ 3 - 3
src/main/java/me/shedaniel/rei/plugin/DefaultStoneCuttingCategory.java

@@ -3,7 +3,7 @@ package me.shedaniel.rei.plugin;
 import com.mojang.blaze3d.platform.GlStateManager;
 import com.mojang.blaze3d.platform.GlStateManager;
 import me.shedaniel.rei.api.DisplaySettings;
 import me.shedaniel.rei.api.DisplaySettings;
 import me.shedaniel.rei.api.RecipeCategory;
 import me.shedaniel.rei.api.RecipeCategory;
-import me.shedaniel.rei.gui.widget.ItemSlotWidget;
+import me.shedaniel.rei.gui.widget.SlotWidget;
 import me.shedaniel.rei.gui.widget.RecipeBaseWidget;
 import me.shedaniel.rei.gui.widget.RecipeBaseWidget;
 import me.shedaniel.rei.gui.widget.Widget;
 import me.shedaniel.rei.gui.widget.Widget;
 import net.minecraft.block.Blocks;
 import net.minecraft.block.Blocks;
@@ -51,8 +51,8 @@ public class DefaultStoneCuttingCategory implements RecipeCategory<DefaultStoneC
                 blit(startPoint.x, startPoint.y, 0, 221, 82, 26);
                 blit(startPoint.x, startPoint.y, 0, 221, 82, 26);
             }
             }
         }));
         }));
-        widgets.add(new ItemSlotWidget(startPoint.x + 4, startPoint.y + 5, recipeDisplaySupplier.get().getInput().get(0), true, true, true));
-        widgets.add(new ItemSlotWidget(startPoint.x + 61, startPoint.y + 5, recipeDisplaySupplier.get().getOutput(), false, true, true));
+        widgets.add(new SlotWidget(startPoint.x + 4, startPoint.y + 5, recipeDisplaySupplier.get().getInput().get(0), true, true, true));
+        widgets.add(new SlotWidget(startPoint.x + 61, startPoint.y + 5, recipeDisplaySupplier.get().getOutput(), false, true, true));
         return widgets;
         return widgets;
     }
     }
     
     

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

@@ -8,6 +8,7 @@ import me.shedaniel.cloth.gui.entries.StringListEntry;
 import me.shedaniel.cloth.hooks.ScreenHooks;
 import me.shedaniel.cloth.hooks.ScreenHooks;
 import me.shedaniel.rei.RoughlyEnoughItemsCore;
 import me.shedaniel.rei.RoughlyEnoughItemsCore;
 import me.shedaniel.rei.api.ItemCheatingMode;
 import me.shedaniel.rei.api.ItemCheatingMode;
+import me.shedaniel.rei.client.RecipeScreenType;
 import me.shedaniel.rei.gui.config.ItemListOrderingConfig;
 import me.shedaniel.rei.gui.config.ItemListOrderingConfig;
 import me.shedaniel.rei.gui.credits.CreditsScreen;
 import me.shedaniel.rei.gui.credits.CreditsScreen;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.MinecraftClient;
@@ -46,6 +47,7 @@ public class ClothScreenRegistry {
             }
             }
         });
         });
         ConfigScreenBuilder.CategoryBuilder appearance = builder.addCategory("text.rei.config.appearance");
         ConfigScreenBuilder.CategoryBuilder appearance = builder.addCategory("text.rei.config.appearance");
+        appearance.addOption(new EnumListEntry<>("text.rei.config.recipe_screen_type", RecipeScreenType.class, RoughlyEnoughItemsCore.getConfigManager().getConfig().screenType, RESET, () -> RecipeScreenType.UNSET, bool -> RoughlyEnoughItemsCore.getConfigManager().getConfig().screenType = bool, EnumListEntry.DEFAULT_NAME_PROVIDER, () -> getConfigTooltip("recipe_screen_type")));
         appearance.addOption(new BooleanListEntry("text.rei.config.side_search_box", RoughlyEnoughItemsCore.getConfigManager().getConfig().sideSearchField, RESET, () -> false, bool -> RoughlyEnoughItemsCore.getConfigManager().getConfig().sideSearchField = bool, () -> getConfigTooltip("side_search_box")));
         appearance.addOption(new BooleanListEntry("text.rei.config.side_search_box", RoughlyEnoughItemsCore.getConfigManager().getConfig().sideSearchField, RESET, () -> false, bool -> RoughlyEnoughItemsCore.getConfigManager().getConfig().sideSearchField = bool, () -> getConfigTooltip("side_search_box")));
         appearance.addOption(new EnumListEntry<>("text.rei.config.list_ordering", ItemListOrderingConfig.class, ItemListOrderingConfig.from(RoughlyEnoughItemsCore.getConfigManager().getConfig().itemListOrdering, RoughlyEnoughItemsCore.getConfigManager().getConfig().isAscending), RESET, () -> ItemListOrderingConfig.REGISTRY_ASCENDING, config -> {
         appearance.addOption(new EnumListEntry<>("text.rei.config.list_ordering", ItemListOrderingConfig.class, ItemListOrderingConfig.from(RoughlyEnoughItemsCore.getConfigManager().getConfig().itemListOrdering, RoughlyEnoughItemsCore.getConfigManager().getConfig().isAscending), RESET, () -> ItemListOrderingConfig.REGISTRY_ASCENDING, config -> {
             RoughlyEnoughItemsCore.getConfigManager().getConfig().itemListOrdering = config.getOrdering();
             RoughlyEnoughItemsCore.getConfigManager().getConfig().itemListOrdering = config.getOrdering();

+ 7 - 1
src/main/resources/assets/roughlyenoughitems/lang/en_us.json

@@ -93,6 +93,13 @@
   "text.rei.config.light_gray_recipe_border": "Light Gray Recipe Border:",
   "text.rei.config.light_gray_recipe_border": "Light Gray Recipe Border:",
   "text.rei.config_api_failed": "You arrived here either if Cloth Config API failed or you don't have it installed!\nUpdate / Install the API and report to the bug tracker.",
   "text.rei.config_api_failed": "You arrived here either if Cloth Config API failed or you don't have it installed!\nUpdate / Install the API and report to the bug tracker.",
   "text.rei.back": "Back",
   "text.rei.back": "Back",
+  "text.rei.config.recipe_screen_type": "Screen Type",
+  "text.rei.config.recipe_screen_type.unset": "Not Set",
+  "text.rei.config.recipe_screen_type.original": "Original",
+  "text.rei.config.recipe_screen_type.villager": "Villager",
+  "text.rei.select": "Select",
+  "text.rei.recipe_screen_type.selection": "Recipe Screen Type Selection",
+  "text.rei.recipe_screen_type.selection.sub": "You can always edit this setting again via the config screen.",
 
 
   "_comment": "Config Tooltips",
   "_comment": "Config Tooltips",
   "tooltip.rei.config.side_search_box": "Declares the location of the search field:\nYes: Left / Right\nNo: Center\n \nDefaulted: No",
   "tooltip.rei.config.side_search_box": "Declares the location of the search field:\nYes: Left / Right\nNo: Center\n \nDefaulted: No",
@@ -101,7 +108,6 @@
   "tooltip.rei.config.max_recipes_per_page": "Declares the maximum possible displayed recipes:\nValues: 2 - 99\n \nDefaulted: 3",
   "tooltip.rei.config.max_recipes_per_page": "Declares the maximum possible displayed recipes:\nValues: 2 - 99\n \nDefaulted: 3",
   "tooltip.rei.config.light_gray_recipe_border": "Declares the appearance of the recipe border:\nYes: Light Gray\nNo: Hard Black\n \nDefaulted: No",
   "tooltip.rei.config.light_gray_recipe_border": "Declares the appearance of the recipe border:\nYes: Light Gray\nNo: Hard Black\n \nDefaulted: No",
   "tooltip.rei.config.april_fools.2019": "Forcefully enables the 2019 april fools joke:\nValues: Yes / No\n \nDefaulted: No",
   "tooltip.rei.config.april_fools.2019": "Forcefully enables the 2019 april fools joke:\nValues: Yes / No\n \nDefaulted: No",
-
   "_comment": "Don't change / translate the credit down below if you are doing it :)",
   "_comment": "Don't change / translate the credit down below if you are doing it :)",
   "text.rei.credit.text": "§lRoughly Enough Items\n§7Originally a fork for Almost Enough Items.\n\n§lDevelopers\n  - Originally by ZenDarva\n  - Created by Danielshe\n  - Plugin Support by TehNut\n\n§lLanguage Translation\n  English - Danielshe\n  Simplified Chinese - XuyuEre & Danielshe\n  Traditional Chinese - hugoalh, gxy17886 & Danielshe\n  French - Yanis48\n  German - MelanX\n  Estonian - Madis0\n  Portuguese - thiagokenis\n  LOLCAT - Danielshe\n  Upside Down - Danielshe\n  Brazilian Portuguese - thiagokenis\n  Bulgarian - geniiii\n\n§lLicense\n§7Roughly Enough Items is using MIT."
   "text.rei.credit.text": "§lRoughly Enough Items\n§7Originally a fork for Almost Enough Items.\n\n§lDevelopers\n  - Originally by ZenDarva\n  - Created by Danielshe\n  - Plugin Support by TehNut\n\n§lLanguage Translation\n  English - Danielshe\n  Simplified Chinese - XuyuEre & Danielshe\n  Traditional Chinese - hugoalh, gxy17886 & Danielshe\n  French - Yanis48\n  German - MelanX\n  Estonian - Madis0\n  Portuguese - thiagokenis\n  LOLCAT - Danielshe\n  Upside Down - Danielshe\n  Brazilian Portuguese - thiagokenis\n  Bulgarian - geniiii\n\n§lLicense\n§7Roughly Enough Items is using MIT."
 }
 }

BIN
src/main/resources/assets/roughlyenoughitems/textures/gui/recipecontainer.png


BIN
src/main/resources/assets/roughlyenoughitems/textures/gui/screenshot.png


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

@@ -1,7 +1,7 @@
 {
 {
   "schemaVersion": 1,
   "schemaVersion": 1,
   "id": "roughlyenoughitems",
   "id": "roughlyenoughitems",
-  "name": "RoughlyEnoughItems",
+  "name": "Roughly Enough Items",
   "description": "To allow players to view items and recipes. Version: ${version}",
   "description": "To allow players to view items and recipes. Version: ${version}",
   "version": "${version}",
   "version": "${version}",
   "authors": [
   "authors": [