瀏覽代碼

Compact the design and add cooking xp details

shedaniel 5 年之前
父節點
當前提交
2dd53ba21c
共有 21 個文件被更改,包括 404 次插入224 次删除
  1. 1 1
      gradle.properties
  2. 13 2
      src/main/java/me/shedaniel/rei/api/ClientHelper.java
  3. 4 3
      src/main/java/me/shedaniel/rei/api/RecipeCategory.java
  4. 4 4
      src/main/java/me/shedaniel/rei/gui/ContainerScreenOverlay.java
  5. 7 1
      src/main/java/me/shedaniel/rei/gui/PreRecipeViewingScreen.java
  6. 56 19
      src/main/java/me/shedaniel/rei/gui/RecipeViewingScreen.java
  7. 15 8
      src/main/java/me/shedaniel/rei/gui/VillagerRecipeViewingScreen.java
  8. 22 56
      src/main/java/me/shedaniel/rei/gui/widget/EntryListWidget.java
  9. 47 6
      src/main/java/me/shedaniel/rei/gui/widget/EntryWidget.java
  10. 3 86
      src/main/java/me/shedaniel/rei/gui/widget/FavoritesListWidget.java
  11. 7 0
      src/main/java/me/shedaniel/rei/gui/widget/PanelWidget.java
  12. 22 12
      src/main/java/me/shedaniel/rei/impl/ClientHelperImpl.java
  13. 1 1
      src/main/java/me/shedaniel/rei/impl/ConfigObjectImpl.java
  14. 10 3
      src/main/java/me/shedaniel/rei/plugin/DefaultPlugin.java
  15. 12 8
      src/main/java/me/shedaniel/rei/plugin/campfire/DefaultCampfireCategory.java
  16. 17 9
      src/main/java/me/shedaniel/rei/plugin/cooking/DefaultCookingCategory.java
  17. 12 5
      src/main/java/me/shedaniel/rei/plugin/cooking/DefaultCookingDisplay.java
  18. 96 0
      src/main/java/me/shedaniel/rei/plugin/fuel/DefaultFuelCategory.java
  19. 43 0
      src/main/java/me/shedaniel/rei/plugin/fuel/DefaultFuelDisplay.java
  20. 8 0
      src/main/java/me/shedaniel/rei/utils/CollectionUtils.java
  21. 4 0
      src/main/resources/assets/roughlyenoughitems/lang/en_us.json

+ 1 - 1
gradle.properties

@@ -1,4 +1,4 @@
-mod_version=3.3.1
+mod_version=3.3.2
 minecraft_version=1.15.1
 yarn_version=1.15.1+build.1
 fabricloader_version=0.7.2+build.174

+ 13 - 2
src/main/java/me/shedaniel/rei/api/ClientHelper.java

@@ -6,7 +6,6 @@
 package me.shedaniel.rei.api;
 
 import me.shedaniel.rei.impl.ClientHelperImpl;
-import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding;
 import net.minecraft.item.Item;
 import net.minecraft.item.ItemStack;
 import net.minecraft.util.Identifier;
@@ -126,7 +125,19 @@ public interface ClientHelper {
      * @param identifier the identifier to find
      * @return the mod name
      */
-    String getModFromIdentifier(Identifier identifier);
+    default String getModFromIdentifier(Identifier identifier) {
+        if (identifier == null)
+            return "";
+        return getModFromModId(identifier.getNamespace());
+    }
+    
+    /**
+     * Gets the mod from a modid
+     *
+     * @param modid the modid of the mod
+     * @return the mod name
+     */
+    String getModFromModId(String modid);
     
     /**
      * Finds all recipes and open them in a recipe screen.

+ 4 - 3
src/main/java/me/shedaniel/rei/api/RecipeCategory.java

@@ -10,6 +10,7 @@ import me.shedaniel.rei.gui.RecipeViewingScreen;
 import me.shedaniel.rei.gui.entries.RecipeEntry;
 import me.shedaniel.rei.gui.entries.SimpleRecipeEntry;
 import me.shedaniel.rei.gui.widget.CategoryBaseWidget;
+import me.shedaniel.rei.gui.widget.PanelWidget;
 import me.shedaniel.rei.gui.widget.RecipeBaseWidget;
 import me.shedaniel.rei.gui.widget.Widget;
 import me.shedaniel.rei.impl.ScreenHelper;
@@ -77,13 +78,13 @@ public interface RecipeCategory<T extends RecipeDisplay> {
      * @param delta  the delta
      */
     default void drawCategoryBackground(Rectangle bounds, int mouseX, int mouseY, float delta) {
-        new CategoryBaseWidget(bounds).render();
+        PanelWidget.render(bounds, -1);
         if (ScreenHelper.isDarkModeEnabled()) {
             DrawableHelper.fill(bounds.x + 17, bounds.y + 5, bounds.x + bounds.width - 17, bounds.y + 17, 0xFF404040);
-            DrawableHelper.fill(bounds.x + 17, bounds.y + 21, bounds.x + bounds.width - 17, bounds.y + 33, 0xFF404040);
+            DrawableHelper.fill(bounds.x + 17, bounds.y + 19, bounds.x + bounds.width - 17, bounds.y + 31, 0xFF404040);
         } else {
             DrawableHelper.fill(bounds.x + 17, bounds.y + 5, bounds.x + bounds.width - 17, bounds.y + 17, 0xFF9E9E9E);
-            DrawableHelper.fill(bounds.x + 17, bounds.y + 21, bounds.x + bounds.width - 17, bounds.y + 33, 0xFF9E9E9E);
+            DrawableHelper.fill(bounds.x + 17, bounds.y + 19, bounds.x + bounds.width - 17, bounds.y + 31, 0xFF9E9E9E);
         }
     }
     

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

@@ -288,7 +288,8 @@ public class ContainerScreenOverlay extends WidgetWithBounds {
             widgets.add(new ClickableLabelWidget(new Point(rectangle.x + (rectangle.width / 2), rectangle.y + (ConfigObject.getInstance().getSearchFieldLocation() == SearchFieldLocation.TOP_SIDE ? 24 : 0) + 10), "") {
                 @Override
                 public void render(int mouseX, int mouseY, float delta) {
-                    setText(String.format("%s/%s", ENTRY_LIST_WIDGET.getPage() + 1, ENTRY_LIST_WIDGET.getTotalPages()));
+                    clickable(ENTRY_LIST_WIDGET.getTotalPages() > 1);
+                    setText(String.format("%s/%s", ENTRY_LIST_WIDGET.getPage() + 1, Math.max(ENTRY_LIST_WIDGET.getTotalPages(), 1)));
                     super.render(mouseX, mouseY, delta);
                 }
                 
@@ -308,8 +309,7 @@ public class ContainerScreenOverlay extends WidgetWithBounds {
                 public boolean changeFocus(boolean boolean_1) {
                     return false;
                 }
-            }.clickable(ENTRY_LIST_WIDGET.getTotalPages() != 1));
-            buttonLeft.enabled = buttonRight.enabled = ENTRY_LIST_WIDGET.getTotalPages() != 1;
+            });
         }
         if (ConfigObject.getInstance().isCraftableFilterEnabled())
             this.widgets.add(toggleButtonWidget = new CraftableToggleButtonWidget(getCraftableToggleArea()) {
@@ -508,7 +508,7 @@ public class ContainerScreenOverlay extends WidgetWithBounds {
         if (!ScreenHelper.isOverlayVisible())
             return;
         if (!ConfigObject.getInstance().isEntryListWidgetScrolled())
-            buttonLeft.enabled = buttonRight.enabled = ENTRY_LIST_WIDGET.getTotalPages() != 1;
+            buttonLeft.enabled = buttonRight.enabled = ENTRY_LIST_WIDGET.getTotalPages() > 1;
         for (Widget widget : widgets) {
             widget.render(int_1, int_2, float_1);
         }

+ 7 - 1
src/main/java/me/shedaniel/rei/gui/PreRecipeViewingScreen.java

@@ -12,6 +12,7 @@ import me.shedaniel.rei.gui.config.RecipeScreenType;
 import me.shedaniel.rei.gui.widget.ButtonWidget;
 import me.shedaniel.rei.gui.widget.Widget;
 import me.shedaniel.rei.gui.widget.WidgetWithBounds;
+import me.shedaniel.rei.impl.ClientHelperImpl;
 import me.shedaniel.rei.impl.ScreenHelper;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.gui.Element;
@@ -33,6 +34,7 @@ public class PreRecipeViewingScreen extends Screen {
     private final List<Widget> widgets;
     private boolean original;
     private Map<RecipeCategory<?>, List<RecipeDisplay>> map;
+    private EntryStack mainStackToNotice = EntryStack.empty();
     
     public PreRecipeViewingScreen(Map<RecipeCategory<?>, List<RecipeDisplay>> map) {
         super(new TranslatableText("text.rei.recipe_screen_type.selection"));
@@ -41,6 +43,10 @@ public class PreRecipeViewingScreen extends Screen {
         this.map = map;
     }
     
+    public void addMainStackToNotice(EntryStack mainStackToNotice) {
+        this.mainStackToNotice = mainStackToNotice;
+    }
+    
     @Override
     protected void init() {
         this.children.clear();
@@ -50,7 +56,7 @@ public class PreRecipeViewingScreen extends Screen {
             public void onPressed() {
                 ConfigObject.getInstance().setRecipeScreenType(original ? RecipeScreenType.ORIGINAL : RecipeScreenType.VILLAGER);
                 ConfigManager.getInstance().saveConfig();
-                ClientHelper.getInstance().openRecipeViewingScreen(map);
+                ((ClientHelperImpl) ClientHelper.getInstance()).openRecipeViewingScreen(map, mainStackToNotice);
             }
         });
         this.widgets.add(new ScreenTypeSelection(width / 2 - 200 - 5, height / 2 - 112 / 2 - 10, 0));

+ 56 - 19
src/main/java/me/shedaniel/rei/gui/RecipeViewingScreen.java

@@ -49,6 +49,7 @@ public class RecipeViewingScreen extends Screen {
     @Nullable private CategoryBaseWidget workingStationsBaseWidget;
     private RecipeCategory<RecipeDisplay> selectedCategory;
     private ButtonWidget recipeBack, recipeNext, categoryBack, categoryNext;
+    private EntryStack mainStackToNotice = EntryStack.empty();
     
     public RecipeViewingScreen(Map<RecipeCategory<?>, List<RecipeDisplay>> categoriesMap) {
         super(new LiteralText(""));
@@ -56,7 +57,7 @@ public class RecipeViewingScreen extends Screen {
         this.preWidgets = Lists.newArrayList();
         this.widgets = Lists.newArrayList();
         Window window = MinecraftClient.getInstance().getWindow();
-        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, 150);
         this.categoriesMap = categoriesMap;
         this.categories = Lists.newArrayList();
         for (RecipeCategory<?> category : RecipeHelper.getInstance().getAllCategories()) {
@@ -68,6 +69,10 @@ public class RecipeViewingScreen extends Screen {
         this.choosePageActivated = false;
     }
     
+    public void addMainStackToNotice(EntryStack stack) {
+        this.mainStackToNotice = stack;
+    }
+    
     @Nullable
     public CategoryBaseWidget getWorkingStationsBaseWidget() {
         return workingStationsBaseWidget;
@@ -128,10 +133,10 @@ public class RecipeViewingScreen extends Screen {
         this.preWidgets.clear();
         this.widgets.clear();
         this.largestWidth = width - 100;
-        this.largestHeight = height - 40;
+        this.largestHeight = Math.max(height - 36, 100);
         int maxWidthDisplay = CollectionUtils.mapAndMax(getCurrentDisplayed(), display -> selectedCategory.getDisplayWidth(display), (Comparator<Integer>) Comparator.naturalOrder()).orElse(150);
-        this.guiWidth = MathHelper.clamp(maxWidthDisplay + 30, 0, largestWidth);
-        this.guiHeight = MathHelper.floor(MathHelper.clamp((selectedCategory.getDisplayHeight() + 7d) * (getRecipesPerPage() + 1d) + 40d, 186d, largestHeight));
+        this.guiWidth = maxWidthDisplay + 20;
+        this.guiHeight = MathHelper.floor(MathHelper.clamp((double) (selectedCategory.getDisplayHeight() + 4) * (getRecipesPerPage() + 1) + 36, 100, largestHeight));
         this.bounds = new Rectangle(width / 2 - guiWidth / 2, height / 2 - guiHeight / 2, guiWidth, guiHeight);
         this.page = MathHelper.clamp(page, 0, getTotalPages(selectedCategory) - 1);
         
@@ -212,7 +217,7 @@ public class RecipeViewingScreen extends Screen {
         categoryBack.enabled = categories.size() > 1;
         categoryNext.enabled = categories.size() > 1;
         
-        widgets.add(recipeBack = new ButtonWidget(new Rectangle(bounds.getX() + 5, bounds.getY() + 21, 12, 12), I18n.translate("text.rei.left_arrow")) {
+        widgets.add(recipeBack = new ButtonWidget(new Rectangle(bounds.getX() + 5, bounds.getY() + 19, 12, 12), I18n.translate("text.rei.left_arrow")) {
             @Override
             public void onPressed() {
                 page--;
@@ -226,7 +231,7 @@ public class RecipeViewingScreen extends Screen {
                 return Optional.ofNullable(I18n.translate("text.rei.previous_page"));
             }
         });
-        widgets.add(new ClickableLabelWidget(new Point(bounds.getCenterX(), bounds.getY() + 23), "") {
+        widgets.add(new ClickableLabelWidget(new Point(bounds.getCenterX(), bounds.getY() + 21), "") {
             @Override
             public void render(int mouseX, int mouseY, float delta) {
                 setText(String.format("%d/%d", page + 1, getTotalPages(selectedCategory)));
@@ -245,7 +250,7 @@ public class RecipeViewingScreen extends Screen {
                 RecipeViewingScreen.this.init();
             }
         }.clickable(categoriesMap.get(selectedCategory).size() > getRecipesPerPageByHeight()));
-        widgets.add(recipeNext = new ButtonWidget(new Rectangle(bounds.getMaxX() - 17, bounds.getY() + 21, 12, 12), I18n.translate("text.rei.right_arrow")) {
+        widgets.add(recipeNext = new ButtonWidget(new Rectangle(bounds.getMaxX() - 17, bounds.getY() + 19, 12, 12), I18n.translate("text.rei.right_arrow")) {
             @Override
             public void onPressed() {
                 page++;
@@ -289,8 +294,9 @@ public class RecipeViewingScreen extends Screen {
             int finalI = i;
             final Supplier<RecipeDisplay> displaySupplier = () -> currentDisplayed.get(finalI);
             int displayWidth = selectedCategory.getDisplayWidth(displaySupplier.get());
-            final Rectangle displayBounds = new Rectangle(getBounds().getCenterX() - displayWidth / 2, getBounds().y + 40 + recipeHeight * i + 7 * i, displayWidth, recipeHeight);
+            final Rectangle displayBounds = new Rectangle(getBounds().getCenterX() - displayWidth / 2, getBounds().y - 2 + 36 + recipeHeight * i + 4 * i, displayWidth, recipeHeight);
             List<Widget> setupDisplay = selectedCategory.setupDisplay(displaySupplier, displayBounds);
+            transformNotice(setupDisplay, mainStackToNotice);
             this.widgets.addAll(setupDisplay);
             if (supplier.isPresent() && supplier.get().get(displayBounds) != null)
                 this.widgets.add(new AutoCraftingButtonWidget(displayBounds, supplier.get().get(displayBounds), supplier.get().getButtonText(), displaySupplier, setupDisplay, selectedCategory));
@@ -306,20 +312,21 @@ public class RecipeViewingScreen extends Screen {
             int hh = MathHelper.floor((bounds.height - 16) / 18f);
             int actualHeight = Math.min(hh, workingStations.size());
             int innerWidth = MathHelper.ceil(workingStations.size() / ((float) hh));
-            int xx = bounds.x - (10 + innerWidth * 18) + 6;
+            int xx = bounds.x - (8 + innerWidth * 16) + 6;
             int yy = bounds.y + 16;
-            preWidgets.add(workingStationsBaseWidget = new CategoryBaseWidget(new Rectangle(xx - 6, yy - 6, 15 + innerWidth * 18, 11 + actualHeight * 18)));
+            preWidgets.add(workingStationsBaseWidget = new CategoryBaseWidget(new Rectangle(xx - 5, yy - 5, 15 + innerWidth * 16, 10 + actualHeight * 16)));
+            preWidgets.add(new SlotBaseWidget(new Rectangle(xx - 1, yy - 1, innerWidth * 16 + 2, actualHeight * 16 + 2)));
             int index = 0;
             List<String> list = Collections.singletonList(Formatting.YELLOW.toString() + I18n.translate("text.rei.working_station"));
-            xx += (innerWidth - 1) * 18;
+            xx += (innerWidth - 1) * 16;
             for (List<EntryStack> workingStation : workingStations) {
-                preWidgets.add(EntryWidget.create(xx, yy).entries(CollectionUtils.map(workingStation, stack -> stack.copy().setting(EntryStack.Settings.TOOLTIP_APPEND_EXTRA, s -> list))));
+                preWidgets.add(new WorkstationSlotWidget(xx, yy, CollectionUtils.map(workingStation, stack -> stack.copy().setting(EntryStack.Settings.TOOLTIP_APPEND_EXTRA, s -> list))));
                 index++;
-                yy += 18;
+                yy += 16;
                 if (index >= hh) {
                     index = 0;
                     yy = bounds.y + 16;
-                    xx -= 18;
+                    xx -= 16;
                 }
             }
         }
@@ -330,6 +337,36 @@ public class RecipeViewingScreen extends Screen {
         children.addAll(preWidgets);
     }
     
+    static void transformNotice(List<Widget> setupDisplay, EntryStack mainStackToNotice) {
+        if (mainStackToNotice.isEmpty())
+            return;
+        for (Widget widget : setupDisplay) {
+            if (widget instanceof EntryWidget) {
+                EntryWidget entry = (EntryWidget) widget;
+                if (entry.entries().size() > 1) {
+                    EntryStack stack = CollectionUtils.firstOrNullEqualsAll(entry.entries(), mainStackToNotice);
+                    if (stack != null) {
+                        entry.clearStacks();
+                        entry.entry(stack);
+                    }
+                }
+            }
+        }
+    }
+    
+    public static class WorkstationSlotWidget extends EntryWidget {
+        public WorkstationSlotWidget(int x, int y, List<EntryStack> widgets) {
+            super(x, y);
+            entries(widgets);
+            noBackground();
+        }
+        
+        @Override
+        public boolean containsMouse(double mouseX, double mouseY) {
+            return getInnerBounds().contains(mouseX, mouseY);
+        }
+    }
+    
     public List<Widget> getWidgets() {
         return widgets;
     }
@@ -360,12 +397,12 @@ public class RecipeViewingScreen extends Screen {
         if (selectedCategory.getFixedRecipesPerPage() > 0)
             return selectedCategory.getFixedRecipesPerPage() - 1;
         int height = selectedCategory.getDisplayHeight();
-        return MathHelper.clamp(MathHelper.floor(((double) largestHeight - 40d) / ((double) height + 7d)) - 1, 0, Math.min(ConfigObject.getInstance().getMaxRecipePerPage() - 1, selectedCategory.getMaximumRecipePerPage() - 1));
+        return MathHelper.clamp(MathHelper.floor(((double) largestHeight - 36) / ((double) height + 4)) - 1, 0, Math.min(ConfigObject.getInstance().getMaxRecipePerPage() - 1, selectedCategory.getMaximumRecipePerPage() - 1));
     }
     
     private int getRecipesPerPageByHeight() {
         int height = selectedCategory.getDisplayHeight();
-        return MathHelper.clamp(MathHelper.floor(((double) guiHeight - 40d) / ((double) height + 7d)), 0, Math.min(ConfigObject.getInstance().getMaxRecipePerPage() - 1, selectedCategory.getMaximumRecipePerPage() - 1));
+        return MathHelper.clamp(MathHelper.floor(((double) guiHeight - 36) / ((double) height + 4)), 0, Math.min(ConfigObject.getInstance().getMaxRecipePerPage() - 1, selectedCategory.getMaximumRecipePerPage() - 1));
     }
     
     @Override
@@ -377,13 +414,13 @@ public class RecipeViewingScreen extends Screen {
         if (selectedCategory != null)
             selectedCategory.drawCategoryBackground(bounds, mouseX, mouseY, delta);
         else {
-            new CategoryBaseWidget(bounds).render();
+            PanelWidget.render(bounds, -1);
             if (ScreenHelper.isDarkModeEnabled()) {
                 fill(bounds.x + 17, bounds.y + 5, bounds.x + bounds.width - 17, bounds.y + 17, 0xFF404040);
-                fill(bounds.x + 17, bounds.y + 21, bounds.x + bounds.width - 17, bounds.y + 33, 0xFF404040);
+                fill(bounds.x + 17, bounds.y + 19, bounds.x + bounds.width - 17, bounds.y + 30, 0xFF404040);
             } else {
                 fill(bounds.x + 17, bounds.y + 5, bounds.x + bounds.width - 17, bounds.y + 17, 0xFF9E9E9E);
-                fill(bounds.x + 17, bounds.y + 21, bounds.x + bounds.width - 17, bounds.y + 33, 0xFF9E9E9E);
+                fill(bounds.x + 17, bounds.y + 19, bounds.x + bounds.width - 17, bounds.y + 31, 0xFF9E9E9E);
             }
         }
         for (TabWidget tab : tabs) {

+ 15 - 8
src/main/java/me/shedaniel/rei/gui/VillagerRecipeViewingScreen.java

@@ -54,11 +54,12 @@ public class VillagerRecipeViewingScreen extends Screen {
     private double target;
     private long start;
     private long duration;
-    private float scrollBarAlpha = 0;
-    private float scrollBarAlphaFuture = 0;
+    private float scrollBarAlpha;
+    private float scrollBarAlphaFuture;
     private long scrollBarAlphaFutureTime = -1;
-    private boolean draggingScrollBar = false;
+    private boolean draggingScrollBar;
     private int tabsPage;
+    private EntryStack mainStackToNotice = EntryStack.empty();
     
     public VillagerRecipeViewingScreen(Map<RecipeCategory<?>, List<RecipeDisplay>> map) {
         super(new LiteralText(""));
@@ -83,6 +84,10 @@ public class VillagerRecipeViewingScreen extends Screen {
         });
     }
     
+    public void addMainStackToNotice(EntryStack stack) {
+        this.mainStackToNotice = stack;
+    }
+    
     @Override
     protected void init() {
         super.init();
@@ -106,18 +111,19 @@ public class VillagerRecipeViewingScreen extends Screen {
             int w = Math.min(ww, workingStations.size());
             int h = MathHelper.ceil(workingStations.size() / ((float) ww));
             int xx = bounds.x + 16;
-            int yy = bounds.y + bounds.height + 5;
-            widgets.add(new CategoryBaseWidget(new Rectangle(xx - 6, bounds.y + bounds.height - 5, 11 + w * 18, 15 + h * 18)));
+            int yy = bounds.y + bounds.height + 2;
+            widgets.add(new CategoryBaseWidget(new Rectangle(xx - 5, bounds.y + bounds.height - 5, 10 + w * 16, 12 + h * 16)));
+            widgets.add(new SlotBaseWidget(new Rectangle(xx - 1, yy - 1, 2 + w * 16, 2 + h * 16)));
             int index = 0;
             List<String> list = Collections.singletonList(Formatting.YELLOW.toString() + I18n.translate("text.rei.working_station"));
             for (List<EntryStack> workingStation : workingStations) {
-                widgets.add(EntryWidget.create(xx, yy).entries(CollectionUtils.map(workingStation, stack -> stack.copy().setting(EntryStack.Settings.TOOLTIP_APPEND_EXTRA, s -> list))));
+                widgets.add(new RecipeViewingScreen.WorkstationSlotWidget(xx, yy, CollectionUtils.map(workingStation, stack -> stack.copy().setting(EntryStack.Settings.TOOLTIP_APPEND_EXTRA, s -> list))));
                 index++;
-                xx += 18;
+                xx += 16;
                 if (index >= ww) {
                     index = 0;
                     xx = bounds.x + 16;
-                    yy += 18;
+                    yy += 16;
                 }
             }
         }
@@ -128,6 +134,7 @@ public class VillagerRecipeViewingScreen extends Screen {
         
         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());
         List<Widget> setupDisplay = category.setupDisplay(() -> display, recipeBounds);
+        RecipeViewingScreen.transformNotice(setupDisplay, mainStackToNotice);
         this.widgets.addAll(setupDisplay);
         Optional<ButtonAreaSupplier> supplier = RecipeHelper.getInstance().getAutoCraftButtonArea(category);
         if (supplier.isPresent() && supplier.get().get(recipeBounds) != null)

+ 22 - 56
src/main/java/me/shedaniel/rei/gui/widget/EntryListWidget.java

@@ -8,7 +8,6 @@ package me.shedaniel.rei.gui.widget;
 import com.google.common.collect.Lists;
 import com.mojang.blaze3d.systems.RenderSystem;
 import me.shedaniel.clothconfig2.ClothConfigInitializer;
-import me.shedaniel.clothconfig2.api.ModifierKeyCode;
 import me.shedaniel.clothconfig2.api.ScissorsHandler;
 import me.shedaniel.clothconfig2.gui.widget.DynamicNewSmoothScrollingEntryListWidget;
 import me.shedaniel.math.api.Point;
@@ -29,19 +28,19 @@ import net.minecraft.client.render.Tessellator;
 import net.minecraft.client.render.VertexConsumerProvider;
 import net.minecraft.client.render.VertexFormats;
 import net.minecraft.client.resource.language.I18n;
-import net.minecraft.client.sound.PositionedSoundInstance;
-import net.minecraft.client.util.InputUtil;
 import net.minecraft.client.util.math.Matrix4f;
 import net.minecraft.client.util.math.MatrixStack;
 import net.minecraft.item.ItemGroup;
-import net.minecraft.sound.SoundEvents;
 import net.minecraft.util.ActionResult;
 import net.minecraft.util.Identifier;
 import net.minecraft.util.math.MathHelper;
 import org.apache.commons.lang3.StringUtils;
 
 import javax.annotation.Nullable;
-import java.util.*;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Locale;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
@@ -350,6 +349,8 @@ public class EntryListWidget extends WidgetWithBounds {
                 }
             }
         }
+        if (containsMouse(mouseX, mouseY) && ClientHelper.getInstance().isCheating() && !minecraft.player.inventory.getCursorStack().isEmpty() && RoughlyEnoughItemsCore.hasPermissionToUsePackets())
+            ScreenHelper.getLastOverlay().addTooltip(QueuedTooltip.create(I18n.translate("text.rei.delete_items")));
     }
     
     private int getScrollbarMinX() {
@@ -593,6 +594,7 @@ public class EntryListWidget extends WidgetWithBounds {
         if (searchArguments.isEmpty())
             return true;
         String mod = null;
+        String modName = null;
         String name = null;
         String tooltip = null;
         String[] tags = null;
@@ -604,9 +606,17 @@ public class EntryListWidget extends WidgetWithBounds {
                 else if (argument.getArgumentType() == SearchArgument.ArgumentType.MOD) {
                     if (mod == null)
                         mod = stack.getIdentifier().map(Identifier::getNamespace).orElse("").replace(SPACE, EMPTY).toLowerCase(Locale.ROOT);
-                    if (mod != null && !mod.isEmpty() && argument.getFunction(!argument.isInclude()).apply(mod)) {
-                        applicable = false;
-                        break;
+                    if (mod != null && !mod.isEmpty()) {
+                        if (argument.getFunction(!argument.isInclude()).apply(mod)) {
+                            applicable = false;
+                            break;
+                        }
+                        if (modName == null)
+                            modName = ClientHelper.getInstance().getModFromModId(mod).replace(SPACE, EMPTY).toLowerCase(Locale.ROOT);
+                        if (modName != null && !modName.isEmpty() && argument.getFunction(!argument.isInclude()).apply(modName)) {
+                            applicable = false;
+                            break;
+                        }
                     }
                 } else if (argument.getArgumentType() == SearchArgument.ArgumentType.TEXT) {
                     if (name == null)
@@ -717,7 +727,7 @@ public class EntryListWidget extends WidgetWithBounds {
             ClientPlayerEntity player = minecraft.player;
             if (ClientHelper.getInstance().isCheating() && !player.inventory.getCursorStack().isEmpty() && RoughlyEnoughItemsCore.hasPermissionToUsePackets()) {
                 ClientHelper.getInstance().sendDeletePacket();
-                return false;
+                return true;
             }
             if (!player.inventory.getCursorStack().isEmpty() && RoughlyEnoughItemsCore.hasPermissionToUsePackets())
                 return false;
@@ -749,60 +759,16 @@ public class EntryListWidget extends WidgetWithBounds {
                 super.drawHighlighted(mouseX, mouseY, delta);
         }
         
-        private String getLocalizedName(InputUtil.KeyCode value) {
-            String string_1 = value.getName();
-            int int_1 = value.getKeyCode();
-            String string_2 = null;
-            switch (value.getCategory()) {
-                case KEYSYM:
-                    string_2 = InputUtil.getKeycodeName(int_1);
-                    break;
-                case SCANCODE:
-                    string_2 = InputUtil.getScancodeName(int_1);
-                    break;
-                case MOUSE:
-                    String string_3 = I18n.translate(string_1);
-                    string_2 = Objects.equals(string_3, string_1) ? I18n.translate(InputUtil.Type.MOUSE.getName(), int_1 + 1) : string_3;
-            }
-            
-            return string_2 == null ? I18n.translate(string_1) : string_2;
-        }
-        
         @Override
         protected void queueTooltip(int mouseX, int mouseY, float delta) {
             if (!ClientHelper.getInstance().isCheating() || minecraft.player.inventory.getCursorStack().isEmpty()) {
-                QueuedTooltip tooltip = getCurrentTooltip(mouseX, mouseY);
-                if (tooltip != null) {
-                    if (ConfigObject.getInstance().doDisplayFavoritesTooltip() && !ConfigObject.getInstance().getFavoriteKeyCode().isUnknown()) {
-                        String name = ConfigObject.getInstance().getFavoriteKeyCode().getLocalizedName();
-                        if (!isFavorites)
-                            tooltip.getText().addAll(Arrays.asList(I18n.translate("text.rei.favorites_tooltip", name).split("\n")));
-                        else
-                            tooltip.getText().addAll(Arrays.asList(I18n.translate("text.rei.remove_favorites_tooltip", name).split("\n")));
-                    }
-                    ScreenHelper.getLastOverlay().addTooltip(tooltip);
-                }
+                super.queueTooltip(mouseX, mouseY, delta);
             }
         }
         
         @Override
-        public boolean keyPressed(int int_1, int int_2, int int_3) {
-            if (interactable && ConfigObject.getInstance().isFavoritesEnabled() && containsMouse(PointHelper.fromMouse()) && !getCurrentEntry().isEmpty()) {
-                ModifierKeyCode keyCode = ConfigObject.getInstance().getFavoriteKeyCode();
-                if (keyCode.matchesKey(int_1, int_2)) {
-                    if (!isFavorites) {
-                        if (!CollectionUtils.anyMatchEqualsAll(ConfigManager.getInstance().getFavorites(), getCurrentEntry()))
-                            ConfigManager.getInstance().getFavorites().add(getCurrentEntry().copy());
-                    } else {
-                        ConfigManager.getInstance().getFavorites().remove(getCurrentEntry());
-                    }
-                    ContainerScreenOverlay.getEntryListWidget().updateSearch(ScreenHelper.getSearchField().getText());
-                    ConfigManager.getInstance().saveConfig();
-                    minecraft.getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
-                    return true;
-                }
-            }
-            return super.keyPressed(int_1, int_2, int_3);
+        protected boolean reverseFavoritesAction() {
+            return isFavorites;
         }
         
         @Override

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

@@ -6,20 +6,24 @@
 package me.shedaniel.rei.gui.widget;
 
 import com.mojang.blaze3d.systems.RenderSystem;
+import me.shedaniel.clothconfig2.api.ModifierKeyCode;
 import me.shedaniel.math.api.Rectangle;
 import me.shedaniel.math.impl.PointHelper;
 import me.shedaniel.rei.api.ClientHelper;
+import me.shedaniel.rei.api.ConfigManager;
 import me.shedaniel.rei.api.ConfigObject;
 import me.shedaniel.rei.api.EntryStack;
+import me.shedaniel.rei.gui.ContainerScreenOverlay;
 import me.shedaniel.rei.impl.ScreenHelper;
+import me.shedaniel.rei.utils.CollectionUtils;
 import net.minecraft.client.gui.Element;
+import net.minecraft.client.resource.language.I18n;
+import net.minecraft.client.sound.PositionedSoundInstance;
+import net.minecraft.sound.SoundEvents;
 import net.minecraft.util.Identifier;
 import net.minecraft.util.math.MathHelper;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
+import java.util.*;
 
 public class EntryWidget extends WidgetWithBounds {
     
@@ -30,6 +34,7 @@ public class EntryWidget extends WidgetWithBounds {
     protected boolean tooltips = true;
     protected boolean background = true;
     protected boolean interactable = true;
+    protected boolean interactableFavorites = true;
     private Rectangle bounds;
     private List<EntryStack> entryStacks;
     
@@ -48,6 +53,16 @@ public class EntryWidget extends WidgetWithBounds {
     
     public EntryWidget interactable(boolean b) {
         interactable = b;
+        interactableFavorites = interactableFavorites && interactable;
+        return this;
+    }
+    
+    public EntryWidget disableFavoritesInteractions() {
+        return interactableFavorites(false);
+    }
+    
+    public EntryWidget interactableFavorites(boolean b) {
+        interactableFavorites = b && interactable;
         return this;
     }
     
@@ -144,6 +159,13 @@ public class EntryWidget extends WidgetWithBounds {
     protected void queueTooltip(int mouseX, int mouseY, float delta) {
         QueuedTooltip tooltip = getCurrentTooltip(mouseX, mouseY);
         if (tooltip != null) {
+            if (interactableFavorites && ConfigObject.getInstance().doDisplayFavoritesTooltip() && !ConfigObject.getInstance().getFavoriteKeyCode().isUnknown()) {
+                String name = ConfigObject.getInstance().getFavoriteKeyCode().getLocalizedName();
+                if (reverseFavoritesAction())
+                    tooltip.getText().addAll(Arrays.asList(I18n.translate("text.rei.remove_favorites_tooltip", name).split("\n")));
+                else
+                    tooltip.getText().addAll(Arrays.asList(I18n.translate("text.rei.favorites_tooltip", name).split("\n")));
+            }
             ScreenHelper.getLastOverlay().addTooltip(tooltip);
         }
     }
@@ -173,11 +195,16 @@ public class EntryWidget extends WidgetWithBounds {
     public boolean mouseClicked(double mouseX, double mouseY, int button) {
         if (!interactable)
             return false;
-        if (containsMouse(mouseX, mouseY))
+        if (containsMouse(mouseX, mouseY)) {
             if (button == 0)
                 return ClientHelper.getInstance().executeRecipeKeyBind(getCurrentEntry());
             else if (button == 1)
                 return ClientHelper.getInstance().executeUsageKeyBind(getCurrentEntry());
+        }
+        return false;
+    }
+    
+    protected boolean reverseFavoritesAction() {
         return false;
     }
     
@@ -185,11 +212,25 @@ public class EntryWidget extends WidgetWithBounds {
     public boolean keyPressed(int int_1, int int_2, int int_3) {
         if (!interactable)
             return false;
-        if (containsMouse(PointHelper.fromMouse()))
+        if (containsMouse(PointHelper.fromMouse())) {
+            if (interactableFavorites && ConfigObject.getInstance().isFavoritesEnabled() && containsMouse(PointHelper.fromMouse()) && !getCurrentEntry().isEmpty()) {
+                ModifierKeyCode keyCode = ConfigObject.getInstance().getFavoriteKeyCode();
+                if (keyCode.matchesKey(int_1, int_2)) {
+                    if (reverseFavoritesAction())
+                        ConfigManager.getInstance().getFavorites().remove(getCurrentEntry());
+                    else if (!CollectionUtils.anyMatchEqualsAll(ConfigManager.getInstance().getFavorites(), getCurrentEntry()))
+                        ConfigManager.getInstance().getFavorites().add(getCurrentEntry());
+                    ContainerScreenOverlay.getEntryListWidget().updateSearch(ScreenHelper.getSearchField().getText());
+                    ConfigManager.getInstance().saveConfig();
+                    minecraft.getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
+                    return true;
+                }
+            }
             if (ConfigObject.getInstance().getRecipeKeybind().matchesKey(int_1, int_2))
                 return ClientHelper.getInstance().executeRecipeKeyBind(getCurrentEntry());
             else if (ConfigObject.getInstance().getUsageKeybind().matchesKey(int_1, int_2))
                 return ClientHelper.getInstance().executeUsageKeyBind(getCurrentEntry());
+        }
         return false;
     }
 }

+ 3 - 86
src/main/java/me/shedaniel/rei/gui/widget/FavoritesListWidget.java

@@ -8,34 +8,23 @@ package me.shedaniel.rei.gui.widget;
 import com.google.common.collect.Lists;
 import com.mojang.blaze3d.systems.RenderSystem;
 import me.shedaniel.clothconfig2.ClothConfigInitializer;
-import me.shedaniel.clothconfig2.api.ModifierKeyCode;
 import me.shedaniel.clothconfig2.api.ScissorsHandler;
 import me.shedaniel.clothconfig2.gui.widget.DynamicNewSmoothScrollingEntryListWidget;
 import me.shedaniel.math.api.Rectangle;
 import me.shedaniel.math.impl.PointHelper;
-import me.shedaniel.rei.RoughlyEnoughItemsCore;
 import me.shedaniel.rei.api.*;
-import me.shedaniel.rei.gui.ContainerScreenOverlay;
-import me.shedaniel.rei.gui.config.ItemCheatingMode;
 import me.shedaniel.rei.gui.config.ItemListOrdering;
 import me.shedaniel.rei.impl.ScreenHelper;
 import me.shedaniel.rei.utils.CollectionUtils;
 import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.network.ClientPlayerEntity;
 import net.minecraft.client.render.BufferBuilder;
 import net.minecraft.client.render.Tessellator;
 import net.minecraft.client.render.VertexFormats;
-import net.minecraft.client.resource.language.I18n;
-import net.minecraft.client.sound.PositionedSoundInstance;
-import net.minecraft.client.util.InputUtil;
-import net.minecraft.sound.SoundEvents;
 import net.minecraft.util.math.MathHelper;
 
 import javax.annotation.Nullable;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
-import java.util.Objects;
 
 import static me.shedaniel.rei.gui.widget.EntryListWidget.*;
 
@@ -325,13 +314,6 @@ public class FavoritesListWidget extends WidgetWithBounds {
         this.draggingScrollBar = false;
         
         if (containsMouse(double_1, double_2)) {
-            ClientPlayerEntity player = minecraft.player;
-            if (ClientHelper.getInstance().isCheating() && !player.inventory.getCursorStack().isEmpty() && RoughlyEnoughItemsCore.hasPermissionToUsePackets()) {
-                ClientHelper.getInstance().sendDeletePacket();
-                return false;
-            }
-            if (!player.inventory.getCursorStack().isEmpty() && RoughlyEnoughItemsCore.hasPermissionToUsePackets())
-                return false;
             for (Widget widget : children())
                 if (widget.mouseClicked(double_1, double_2, int_1))
                     return true;
@@ -355,78 +337,13 @@ public class FavoritesListWidget extends WidgetWithBounds {
         
         @Override
         protected void drawHighlighted(int mouseX, int mouseY, float delta) {
-            if (getCurrentEntry().getType() != EntryStack.Type.EMPTY)
+            if (!getCurrentEntry().isEmpty())
                 super.drawHighlighted(mouseX, mouseY, delta);
         }
         
-        private String getLocalizedName(InputUtil.KeyCode value) {
-            String string_1 = value.getName();
-            int int_1 = value.getKeyCode();
-            String string_2 = null;
-            switch (value.getCategory()) {
-                case KEYSYM:
-                    string_2 = InputUtil.getKeycodeName(int_1);
-                    break;
-                case SCANCODE:
-                    string_2 = InputUtil.getScancodeName(int_1);
-                    break;
-                case MOUSE:
-                    String string_3 = I18n.translate(string_1);
-                    string_2 = Objects.equals(string_3, string_1) ? I18n.translate(InputUtil.Type.MOUSE.getName(), int_1 + 1) : string_3;
-            }
-            
-            return string_2 == null ? I18n.translate(string_1) : string_2;
-        }
-        
-        @Override
-        protected void queueTooltip(int mouseX, int mouseY, float delta) {
-            if (!ClientHelper.getInstance().isCheating() || minecraft.player.inventory.getCursorStack().isEmpty()) {
-                QueuedTooltip tooltip = getCurrentTooltip(mouseX, mouseY);
-                if (tooltip != null) {
-                    if (ConfigObject.getInstance().doDisplayFavoritesTooltip() && !ConfigObject.getInstance().getFavoriteKeyCode().isUnknown()) {
-                        String name = ConfigObject.getInstance().getFavoriteKeyCode().getLocalizedName();
-                        tooltip.getText().addAll(Arrays.asList(I18n.translate("text.rei.remove_favorites_tooltip", name).split("\n")));
-                    }
-                    ScreenHelper.getLastOverlay().addTooltip(tooltip);
-                }
-            }
-        }
-        
-        @Override
-        public boolean keyPressed(int int_1, int int_2, int int_3) {
-            if (interactable && ConfigObject.getInstance().isFavoritesEnabled() && containsMouse(PointHelper.fromMouse()) && !getCurrentEntry().isEmpty()) {
-                ModifierKeyCode keyCode = ConfigObject.getInstance().getFavoriteKeyCode();
-                if (keyCode.matchesKey(int_1, int_2)) {
-                    ConfigManager.getInstance().getFavorites().remove(getCurrentEntry());
-                    ContainerScreenOverlay.getEntryListWidget().updateSearch(ScreenHelper.getSearchField().getText());
-                    ConfigManager.getInstance().saveConfig();
-                    minecraft.getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
-                    return true;
-                }
-            }
-            return super.keyPressed(int_1, int_2, int_3);
-        }
-        
         @Override
-        public boolean mouseClicked(double mouseX, double mouseY, int button) {
-            if (!interactable)
-                return super.mouseClicked(mouseX, mouseY, button);
-            if (containsMouse(mouseX, mouseY) && ClientHelper.getInstance().isCheating()) {
-                EntryStack entry = getCurrentEntry().copy();
-                if (!entry.isEmpty()) {
-                    if (entry.getType() == EntryStack.Type.ITEM) {
-                        if (ConfigObject.getInstance().getItemCheatingMode() == ItemCheatingMode.REI_LIKE)
-                            entry.setAmount(button != 1 ? 1 : entry.getItemStack().getMaxCount());
-                        else if (ConfigObject.getInstance().getItemCheatingMode() == ItemCheatingMode.JEI_LIKE)
-                            entry.setAmount(button != 0 ? 1 : entry.getItemStack().getMaxCount());
-                        else
-                            entry.setAmount(1);
-                    }
-                    ClientHelper.getInstance().tryCheatingEntry(entry);
-                    return true;
-                }
-            }
-            return super.mouseClicked(mouseX, mouseY, button);
+        protected boolean reverseFavoritesAction() {
+            return true;
         }
     }
 }

+ 7 - 0
src/main/java/me/shedaniel/rei/gui/widget/PanelWidget.java

@@ -20,6 +20,7 @@ public class PanelWidget extends WidgetWithBounds {
     private static final Identifier CHEST_GUI_TEXTURE = new Identifier("roughlyenoughitems", "textures/gui/recipecontainer.png");
     private static final Identifier CHEST_GUI_TEXTURE_DARK = new Identifier("roughlyenoughitems", "textures/gui/recipecontainer_dark.png");
     
+    private static final PanelWidget TEMP = new PanelWidget(new Rectangle());
     private Rectangle bounds;
     private int color = -1;
     
@@ -37,6 +38,12 @@ public class PanelWidget extends WidgetWithBounds {
         return Collections.emptyList();
     }
     
+    public static void render(Rectangle bounds, int color) {
+        TEMP.bounds = bounds;
+        TEMP.color = color;
+        TEMP.render();
+    }
+    
     public void render() {
         render(0, 0, 0);
     }

+ 22 - 12
src/main/java/me/shedaniel/rei/impl/ClientHelperImpl.java

@@ -74,13 +74,12 @@ public class ClientHelperImpl implements ClientHelper, ClientModInitializer {
     }
     
     @Override
-    public String getModFromIdentifier(Identifier identifier) {
-        if (identifier == null)
+    public String getModFromModId(String modid) {
+        if (modid == null)
             return "";
-        Optional<String> any = Optional.ofNullable(modNameCache.getOrDefault(identifier.getNamespace(), null));
-        if (any.isPresent())
-            return any.get();
-        String modid = identifier.getNamespace();
+        String any = modNameCache.getOrDefault(modid, null);
+        if (any != null)
+            return any;
         String s = FabricLoader.getInstance().getModContainer(modid).map(ModContainer::getMetadata).map(ModMetadata::getName).orElse(modid);
         modNameCache.put(modid, s);
         return s;
@@ -138,7 +137,7 @@ public class ClientHelperImpl implements ClientHelper, ClientModInitializer {
     public boolean executeRecipeKeyBind(EntryStack stack) {
         Map<RecipeCategory<?>, List<RecipeDisplay>> map = RecipeHelper.getInstance().getRecipesFor(stack);
         if (map.keySet().size() > 0)
-            openRecipeViewingScreen(map);
+            openRecipeViewingScreen(map, stack);
         return map.keySet().size() > 0;
     }
     
@@ -146,7 +145,7 @@ public class ClientHelperImpl implements ClientHelper, ClientModInitializer {
     public boolean executeUsageKeyBind(EntryStack stack) {
         Map<RecipeCategory<?>, List<RecipeDisplay>> map = RecipeHelper.getInstance().getUsagesFor(stack);
         if (map.keySet().size() > 0)
-            openRecipeViewingScreen(map);
+            openRecipeViewingScreen(map, stack);
         return map.keySet().size() > 0;
     }
     
@@ -199,13 +198,24 @@ public class ClientHelperImpl implements ClientHelper, ClientModInitializer {
     
     @Override
     public void openRecipeViewingScreen(Map<RecipeCategory<?>, List<RecipeDisplay>> map) {
-        Screen screen = null;
-        if (ConfigObject.getInstance().getRecipeScreenType() == RecipeScreenType.VILLAGER)
+        openRecipeViewingScreen(map, null);
+    }
+    
+    public void openRecipeViewingScreen(Map<RecipeCategory<?>, List<RecipeDisplay>> map, EntryStack notice) {
+        Screen screen;
+        if (ConfigObject.getInstance().getRecipeScreenType() == RecipeScreenType.VILLAGER) {
             screen = new VillagerRecipeViewingScreen(map);
-        else if (ConfigObject.getInstance().getRecipeScreenType() == RecipeScreenType.UNSET)
+            if (notice != null)
+                ((VillagerRecipeViewingScreen) screen).addMainStackToNotice(notice);
+        } else if (ConfigObject.getInstance().getRecipeScreenType() == RecipeScreenType.UNSET) {
             screen = new PreRecipeViewingScreen(map);
-        else
+            if (notice != null)
+                ((PreRecipeViewingScreen) screen).addMainStackToNotice(notice);
+        } else {
             screen = new RecipeViewingScreen(map);
+            if (notice != null)
+                ((RecipeViewingScreen) screen).addMainStackToNotice(notice);
+        }
         ScreenHelper.storeRecipeScreen(MinecraftClient.getInstance().currentScreen);
         MinecraftClient.getInstance().openScreen(screen);
     }

+ 1 - 1
src/main/java/me/shedaniel/rei/impl/ConfigObjectImpl.java

@@ -305,7 +305,7 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData {
         @Comment("Declares whether entry list widget is scrolled.") private boolean scrollingEntryListWidget = false;
         private boolean snapToRows = false;
         private boolean displayFavoritesOnTheLeft = true;
-        private boolean displayFavoritesTooltip = true;
+        private boolean displayFavoritesTooltip = false;
         @Comment("Declares whether favorites will be searched.") private boolean searchFavorites = true;
         @UsePercentage(min = 0.5, max = 4.0) private double entrySize = 1.0;
     }

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

@@ -28,6 +28,8 @@ import me.shedaniel.rei.plugin.crafting.DefaultCraftingCategory;
 import me.shedaniel.rei.plugin.crafting.DefaultCustomDisplay;
 import me.shedaniel.rei.plugin.crafting.DefaultShapedDisplay;
 import me.shedaniel.rei.plugin.crafting.DefaultShapelessDisplay;
+import me.shedaniel.rei.plugin.fuel.DefaultFuelCategory;
+import me.shedaniel.rei.plugin.fuel.DefaultFuelDisplay;
 import me.shedaniel.rei.plugin.smelting.DefaultSmeltingDisplay;
 import me.shedaniel.rei.plugin.smoking.DefaultSmokingDisplay;
 import me.shedaniel.rei.plugin.stonecutting.DefaultStoneCuttingCategory;
@@ -38,12 +40,12 @@ import me.shedaniel.rei.plugin.stripping.DummyAxeItem;
 import net.fabricmc.loader.api.SemanticVersion;
 import net.fabricmc.loader.util.version.VersionParsingException;
 import net.minecraft.block.ComposterBlock;
+import net.minecraft.block.entity.AbstractFurnaceBlockEntity;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.gui.screen.ingame.*;
 import net.minecraft.client.gui.screen.recipebook.RecipeBookProvider;
 import net.minecraft.enchantment.Enchantment;
 import net.minecraft.enchantment.EnchantmentHelper;
-import net.minecraft.fluid.EmptyFluid;
 import net.minecraft.fluid.Fluid;
 import net.minecraft.item.*;
 import net.minecraft.potion.PotionUtil;
@@ -66,6 +68,7 @@ public class DefaultPlugin implements REIPluginV0 {
     public static final Identifier BREWING = new Identifier("minecraft", "plugins/brewing");
     public static final Identifier PLUGIN = new Identifier("roughlyenoughitems", "default_plugin");
     public static final Identifier COMPOSTING = new Identifier("minecraft", "plugins/composting");
+    public static final Identifier FUEL = new Identifier("minecraft", "plugins/fuel");
     private static final Identifier DISPLAY_TEXTURE = new Identifier("roughlyenoughitems", "textures/gui/display.png");
     private static final Identifier DISPLAY_TEXTURE_DARK = new Identifier("roughlyenoughitems", "textures/gui/display_dark.png");
     private static final List<DefaultBrewingDisplay> BREWING_DISPLAYS = Lists.newArrayList();
@@ -119,7 +122,7 @@ public class DefaultPlugin implements REIPluginV0 {
         }
         entryRegistry.queueRegisterEntryAfter(stack, enchantments);
         for (Fluid fluid : Registry.FLUID) {
-            if (!(fluid instanceof EmptyFluid))
+            if (!fluid.getDefaultState().isEmpty() && fluid.getDefaultState().isStill())
                 entryRegistry.registerEntry(EntryStack.create(fluid));
         }
     }
@@ -135,6 +138,7 @@ public class DefaultPlugin implements REIPluginV0 {
         recipeHelper.registerCategory(new DefaultCookingCategory(BLASTING, EntryStack.create(Items.BLAST_FURNACE), "category.rei.blasting"));
         recipeHelper.registerCategory(new DefaultCampfireCategory());
         recipeHelper.registerCategory(new DefaultStoneCuttingCategory());
+        recipeHelper.registerCategory(new DefaultFuelCategory());
         recipeHelper.registerCategory(new DefaultBrewingCategory());
         recipeHelper.registerCategory(new DefaultCompostingCategory());
         recipeHelper.registerCategory(new DefaultStrippingCategory());
@@ -155,6 +159,9 @@ public class DefaultPlugin implements REIPluginV0 {
         for (DefaultBrewingDisplay display : BREWING_DISPLAYS) {
             recipeHelper.registerDisplay(BREWING, display);
         }
+        for (Map.Entry<Item, Integer> entry : AbstractFurnaceBlockEntity.createFuelTimeMap().entrySet()) {
+            recipeHelper.registerDisplay(FUEL, new DefaultFuelDisplay(EntryStack.create(entry.getKey()), entry.getValue()));
+        }
         List<EntryStack> arrowStack = Collections.singletonList(EntryStack.create(Items.ARROW));
         for (EntryStack entry : EntryRegistry.getInstance().getStacksList()) {
             if (entry.getItem() == Items.LINGERING_POTION) {
@@ -312,7 +319,7 @@ public class DefaultPlugin implements REIPluginV0 {
         recipeHelper.registerWorkingStations(BREWING, EntryStack.create(Items.BREWING_STAND));
         recipeHelper.registerWorkingStations(STONE_CUTTING, EntryStack.create(Items.STONECUTTER));
         recipeHelper.registerWorkingStations(COMPOSTING, EntryStack.create(Items.COMPOSTER));
-        recipeHelper.registerAutoCraftButtonArea(CAMPFIRE, bounds -> new Rectangle(bounds.x + 6, bounds.y + 6, 10, 10));
+        recipeHelper.removeAutoCraftButton(FUEL);
         recipeHelper.removeAutoCraftButton(COMPOSTING);
         recipeHelper.registerScreenClickArea(new Rectangle(88, 32, 28, 23), CraftingTableScreen.class, CRAFTING);
         recipeHelper.registerScreenClickArea(new Rectangle(137, 29, 10, 13), InventoryScreen.class, CRAFTING);

+ 12 - 8
src/main/java/me/shedaniel/rei/plugin/campfire/DefaultCampfireCategory.java

@@ -45,24 +45,28 @@ public class DefaultCampfireCategory implements RecipeCategory<DefaultCampfireDi
 
     @Override
     public List<Widget> setupDisplay(Supplier<DefaultCampfireDisplay> recipeDisplaySupplier, Rectangle bounds) {
-        Point startPoint = new Point(bounds.getCenterX() - 41, bounds.getCenterY() - 27);
+        Point startPoint = new Point(bounds.getCenterX() - 41, bounds.y + 10);
         List<Widget> widgets = new LinkedList<>(Collections.singletonList(new RecipeBaseWidget(bounds) {
             @Override
             public void render(int mouseX, int mouseY, float delta) {
                 super.render(mouseX, mouseY, delta);
                 MinecraftClient.getInstance().getTextureManager().bindTexture(DefaultPlugin.getDisplayTexture());
-                blit(startPoint.x, startPoint.y, 0, 167, 82, 54);
+                blit(startPoint.x, startPoint.y, 0, 177, 82, 34);
                 int height = MathHelper.ceil((System.currentTimeMillis() / 250 % 14d) / 1f);
-                blit(startPoint.x + 2, startPoint.y + 31 + (14 - height), 82, 77 + (14 - height), 14, height);
+                blit(startPoint.x + 1, startPoint.y + 31 + (3 - height), 82, 77 + (14 - height), 14, height);
                 String text = I18n.translate("category.rei.campfire.time", MathHelper.floor(recipeDisplaySupplier.get().getCookTime() / 20d));
                 int length = MinecraftClient.getInstance().textRenderer.getStringWidth(text);
-                MinecraftClient.getInstance().textRenderer.draw(text, bounds.x + bounds.width - length - 5, startPoint.y + 54 - 8, ScreenHelper.isDarkModeEnabled() ? 0xFFBBBBBB : 0xFF404040);
+                MinecraftClient.getInstance().textRenderer.draw(text, bounds.x + bounds.width - length - 5, bounds.y + 5, ScreenHelper.isDarkModeEnabled() ? 0xFFBBBBBB : 0xFF404040);
             }
         }));
-        widgets.add(new RecipeArrowWidget(startPoint.x + 24, startPoint.y + 18, true));
-        widgets.add(EntryWidget.create(startPoint.x + 1, startPoint.y + 11).entries(recipeDisplaySupplier.get().getInputEntries().get(0)));
-        widgets.add(EntryWidget.create(startPoint.x + 61, startPoint.y + 19).entries(recipeDisplaySupplier.get().getOutputEntries()).noBackground());
+        widgets.add(new RecipeArrowWidget(startPoint.x + 24, startPoint.y + 8, true));
+        widgets.add(EntryWidget.create(startPoint.x + 1, startPoint.y + 1).entries(recipeDisplaySupplier.get().getInputEntries().get(0)));
+        widgets.add(EntryWidget.create(startPoint.x + 61, startPoint.y + 9).entries(recipeDisplaySupplier.get().getOutputEntries()).noBackground());
         return widgets;
     }
-
+    
+    @Override
+    public int getDisplayHeight() {
+        return 49;
+    }
 }

+ 17 - 9
src/main/java/me/shedaniel/rei/plugin/cooking/DefaultCookingCategory.java

@@ -17,6 +17,7 @@ import me.shedaniel.rei.gui.widget.EntryWidget;
 import me.shedaniel.rei.gui.widget.RecipeArrowWidget;
 import me.shedaniel.rei.gui.widget.RecipeBaseWidget;
 import me.shedaniel.rei.gui.widget.Widget;
+import me.shedaniel.rei.impl.ScreenHelper;
 import me.shedaniel.rei.plugin.DefaultPlugin;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.gui.DrawableHelper;
@@ -52,22 +53,24 @@ public class DefaultCookingCategory implements TransferRecipeCategory<DefaultCoo
 
     @Override
     public List<Widget> setupDisplay(Supplier<DefaultCookingDisplay> recipeDisplaySupplier, Rectangle bounds) {
-        Point startPoint = new Point(bounds.getCenterX() - 41, bounds.getCenterY() - 27);
+        Point startPoint = new Point(bounds.getCenterX() - 41, bounds.y + 10);
         List<Widget> widgets = new LinkedList<>(Collections.singletonList(new RecipeBaseWidget(bounds) {
             @Override
             public void render(int mouseX, int mouseY, float delta) {
                 super.render(mouseX, mouseY, delta);
                 MinecraftClient.getInstance().getTextureManager().bindTexture(DefaultPlugin.getDisplayTexture());
-                blit(startPoint.x, startPoint.y, 0, 54, 82, 54);
+                blit(startPoint.x, startPoint.y, 0, 177, 82, 34);
                 int height = MathHelper.ceil((System.currentTimeMillis() / 250 % 14d) / 1f);
-                blit(startPoint.x + 2, startPoint.y + 21 + (14 - height), 82, 77 + (14 - height), 14, height);
+                blit(startPoint.x + 1, startPoint.y + 31 + (3 - height), 82, 77 + (14 - height), 14, height);
+                String text = I18n.translate("category.rei.cooking.xp", recipeDisplaySupplier.get().getXp());
+                int length = MinecraftClient.getInstance().textRenderer.getStringWidth(text);
+                MinecraftClient.getInstance().textRenderer.draw(text, bounds.x + bounds.width - length - 5, bounds.y + 5, ScreenHelper.isDarkModeEnabled() ? 0xFFBBBBBB : 0xFF404040);
+    
             }
         }));
-        widgets.add(new RecipeArrowWidget(startPoint.x + 24, startPoint.y + 18, true));
-        List<List<EntryStack>> input = recipeDisplaySupplier.get().getInputEntries();
-        widgets.add(EntryWidget.create(startPoint.x + 1, startPoint.y + 1).entries(input.get(0)));
-        widgets.add(EntryWidget.create(startPoint.x + 1, startPoint.y + 37).entries(input.get(1)));
-        widgets.add(EntryWidget.create(startPoint.x + 61, startPoint.y + 19).entries(recipeDisplaySupplier.get().getOutputEntries()).noBackground());
+        widgets.add(new RecipeArrowWidget(startPoint.x + 24, startPoint.y + 8, true));
+        widgets.add(EntryWidget.create(startPoint.x + 1, startPoint.y + 1).entries(recipeDisplaySupplier.get().getInputEntries().get(0)));
+        widgets.add(EntryWidget.create(startPoint.x + 61, startPoint.y + 9).entries(recipeDisplaySupplier.get().getOutputEntries()).noBackground());
         return widgets;
     }
 
@@ -75,7 +78,12 @@ public class DefaultCookingCategory implements TransferRecipeCategory<DefaultCoo
     public RecipeEntry getSimpleRenderer(DefaultCookingDisplay recipe) {
         return SimpleRecipeEntry.create(Collections.singletonList(recipe.getInputEntries().get(0)), recipe.getOutputEntries());
     }
-
+    
+    @Override
+    public int getDisplayHeight() {
+        return 49;
+    }
+    
     @Override
     public Identifier getIdentifier() {
         return identifier;

+ 12 - 5
src/main/java/me/shedaniel/rei/plugin/cooking/DefaultCookingDisplay.java

@@ -27,7 +27,13 @@ import java.util.stream.Collectors;
 public abstract class DefaultCookingDisplay implements TransferRecipeDisplay {
     private AbstractCookingRecipe recipe;
     private List<List<EntryStack>> input;
+    private static List<EntryStack> fuel;
     private List<EntryStack> output;
+    private float xp;
+    
+    static {
+        fuel = FurnaceBlockEntity.createFuelTimeMap().keySet().stream().map(Item::getStackForRender).map(EntryStack::create).map(e -> e.setting(EntryStack.Settings.TOOLTIP_APPEND_EXTRA, stack -> Collections.singletonList(Formatting.YELLOW.toString() + I18n.translate("category.rei.smelting.fuel")))).collect(Collectors.toList());
+    }
     
     public DefaultCookingDisplay(AbstractCookingRecipe recipe) {
         this.recipe = recipe;
@@ -38,8 +44,9 @@ public abstract class DefaultCookingDisplay implements TransferRecipeDisplay {
             }
             return entries;
         }).collect(Collectors.toList());
-        this.input.add(FurnaceBlockEntity.createFuelTimeMap().keySet().stream().map(Item::getStackForRender).map(EntryStack::create).map(e -> e.setting(EntryStack.Settings.TOOLTIP_APPEND_EXTRA, stack -> Collections.singletonList(Formatting.YELLOW.toString() + I18n.translate("category.rei.smelting.fuel")))).collect(Collectors.toList()));
+        this.input.add(fuel);
         this.output = Collections.singletonList(EntryStack.create(recipe.getOutput()));
+        xp = recipe.getExperience();
     }
     
     @Override
@@ -57,15 +64,15 @@ public abstract class DefaultCookingDisplay implements TransferRecipeDisplay {
         return output;
     }
     
-    public List<EntryStack> getFuel() {
-        return input.get(1);
-    }
-    
     @Override
     public List<List<EntryStack>> getRequiredEntries() {
         return input;
     }
     
+    public float getXp() {
+        return xp;
+    }
+    
     @Deprecated
     public Optional<AbstractCookingRecipe> getOptionalRecipe() {
         return Optional.ofNullable(recipe);

+ 96 - 0
src/main/java/me/shedaniel/rei/plugin/fuel/DefaultFuelCategory.java

@@ -0,0 +1,96 @@
+/*
+ * Roughly Enough Items by Danielshe.
+ * Licensed under the MIT License.
+ */
+
+package me.shedaniel.rei.plugin.fuel;
+
+import me.shedaniel.math.api.Point;
+import me.shedaniel.math.api.Rectangle;
+import me.shedaniel.rei.api.EntryStack;
+import me.shedaniel.rei.api.RecipeCategory;
+import me.shedaniel.rei.gui.entries.RecipeEntry;
+import me.shedaniel.rei.gui.widget.EntryWidget;
+import me.shedaniel.rei.gui.widget.QueuedTooltip;
+import me.shedaniel.rei.gui.widget.RecipeBaseWidget;
+import me.shedaniel.rei.gui.widget.Widget;
+import me.shedaniel.rei.impl.ScreenHelper;
+import me.shedaniel.rei.plugin.DefaultPlugin;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.resource.language.I18n;
+import net.minecraft.item.Items;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.math.MathHelper;
+
+import javax.annotation.Nullable;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.function.Supplier;
+
+public class DefaultFuelCategory implements RecipeCategory<DefaultFuelDisplay> {
+    @Override
+    public Identifier getIdentifier() {
+        return DefaultPlugin.FUEL;
+    }
+    
+    @Override
+    public String getCategoryName() {
+        return I18n.translate("category.rei.fuel");
+    }
+    
+    @Override
+    public int getDisplayHeight() {
+        return 49;
+    }
+    
+    @Override
+    public EntryStack getLogo() {
+        return EntryStack.create(Items.COAL);
+    }
+    
+    @Override
+    public List<Widget> setupDisplay(Supplier<DefaultFuelDisplay> recipeDisplaySupplier, Rectangle bounds) {
+        Point startPoint = new Point(bounds.getCenterX() - 41, bounds.getCenterY() - 17);
+        List<Widget> widgets = new LinkedList<>(Collections.singletonList(new RecipeBaseWidget(bounds) {
+            @Override
+            public void render(int mouseX, int mouseY, float delta) {
+                super.render(mouseX, mouseY, delta);
+                MinecraftClient.getInstance().getTextureManager().bindTexture(DefaultPlugin.getDisplayTexture());
+                blit(bounds.x + 5, startPoint.y, 0, 73, 18, 34);
+                int height = MathHelper.ceil((System.currentTimeMillis() / 250 % 14d) / 1f);
+                blit(bounds.x + 6, startPoint.y + 12 + (3 - height), 82, 77 + (14 - height), 14, height);
+                minecraft.textRenderer.draw(I18n.translate("category.rei.fuel.time", recipeDisplaySupplier.get().getFuelTime()), bounds.x + 26, bounds.getMaxY() - 15, ScreenHelper.isDarkModeEnabled() ? 0xFFBBBBBB : 0xFF404040);
+            }
+        }));
+        widgets.add(EntryWidget.create(bounds.x + 6, startPoint.y + 18).entries(recipeDisplaySupplier.get().getInputEntries().get(0)));
+        return widgets;
+    }
+    
+    @Override
+    public RecipeEntry getSimpleRenderer(DefaultFuelDisplay recipe) {
+        EntryWidget widget = EntryWidget.create(0, 0).entries(recipe.getInputEntries().get(0)).noBackground().noHighlight();
+        return new RecipeEntry() {
+            @Override
+            public int getHeight() {
+                return 22;
+            }
+            
+            @Nullable
+            @Override
+            public QueuedTooltip getTooltip(int mouseX, int mouseY) {
+                if (widget.containsMouse(mouseX, mouseY))
+                    return widget.getCurrentTooltip(mouseX, mouseY);
+                return null;
+            }
+            
+            @Override
+            public void render(Rectangle bounds, int mouseX, int mouseY, float delta) {
+                widget.setZ(getZ() + 50);
+                widget.getBounds().setLocation(bounds.x + 4, bounds.y + 2);
+                widget.render(mouseX, mouseY, delta);
+                MinecraftClient.getInstance().textRenderer.drawWithShadow(I18n.translate("category.rei.fuel.time_short", recipe.getFuelTime()), bounds.x + 25, bounds.y + 8, -1);
+            }
+        };
+    }
+}

+ 43 - 0
src/main/java/me/shedaniel/rei/plugin/fuel/DefaultFuelDisplay.java

@@ -0,0 +1,43 @@
+/*
+ * Roughly Enough Items by Danielshe.
+ * Licensed under the MIT License.
+ */
+
+package me.shedaniel.rei.plugin.fuel;
+
+import me.shedaniel.rei.api.EntryStack;
+import me.shedaniel.rei.api.RecipeDisplay;
+import me.shedaniel.rei.plugin.DefaultPlugin;
+import net.minecraft.util.Identifier;
+
+import java.util.Collections;
+import java.util.List;
+
+public class DefaultFuelDisplay implements RecipeDisplay {
+    private EntryStack fuel;
+    private int fuelTime;
+    
+    public DefaultFuelDisplay(EntryStack fuel, int fuelTime) {
+        this.fuel = fuel;
+        this.fuelTime = fuelTime;
+    }
+    
+    @Override
+    public List<List<EntryStack>> getInputEntries() {
+        return Collections.singletonList(Collections.singletonList(fuel));
+    }
+    
+    @Override
+    public List<EntryStack> getOutputEntries() {
+        return Collections.emptyList();
+    }
+    
+    @Override
+    public Identifier getRecipeCategory() {
+        return DefaultPlugin.FUEL;
+    }
+    
+    public int getFuelTime() {
+        return fuelTime;
+    }
+}

+ 8 - 0
src/main/java/me/shedaniel/rei/utils/CollectionUtils.java

@@ -46,6 +46,14 @@ public class CollectionUtils {
         return false;
     }
     
+    public static EntryStack firstOrNullEqualsAll(List<EntryStack> list, EntryStack stack) {
+        for (EntryStack t : list) {
+            if (t.equalsAll(stack))
+                return t;
+        }
+        return null;
+    }
+    
     public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
         List<T> l = new LinkedList<>();
         for (T t : list) {

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

@@ -10,6 +10,10 @@
   "category.rei.crafting": "Crafting",
   "category.rei.smelting": "Smelting",
   "category.rei.smelting.fuel": "Fuel",
+  "category.rei.fuel": "Fuel",
+  "category.rei.fuel.time": "Burn Time: %d ticks",
+  "category.rei.fuel.time_short": "%d ticks",
+  "category.rei.cooking.xp": "%f XP",
   "category.rei.smoking": "Smoking",
   "category.rei.blasting": "Blasting",
   "category.rei.campfire": "Campfire",