Browse Source

3.2.22 Favorites

Close #127
shedaniel 5 năm trước cách đây
mục cha
commit
2bd835de6f

+ 8 - 3
build.gradle

@@ -67,11 +67,16 @@ dependencies {
             transitive = false
         }
     }
-    modImplementation "io.github.prospector:modmenu:${modmenu_version}"
+    modCompileOnly ("io.github.prospector:modmenu:${modmenu_version}") {
+        transitive = false
+    }
+    modRuntime ("io.github.prospector:modmenu:${modmenu_version}") {
+        transitive = false
+    }
     compile "org.lwjgl:lwjgl-jemalloc:3.2.1"
     compileOnly "com.google.code.findbugs:jsr305:3.0.2"
-    implementation 'org.jetbrains:annotations:15.0'
-    modRuntime ("com.lettuce.fudge:notenoughcrashes:1.0.12+1.15") {
+    compileOnly 'org.jetbrains:annotations:15.0'
+    modRuntime("com.lettuce.fudge:notenoughcrashes:1.0.12+1.15") {
         transitive = false
     }
 }

+ 1 - 1
gradle.properties

@@ -1,4 +1,4 @@
-mod_version=3.2.22-unstable
+mod_version=3.2.22
 minecraft_version=1.15
 yarn_version=1.15+build.1
 fabricloader_version=0.7.2+build.174

+ 3 - 9
src/main/java/me/shedaniel/rei/REIModMenuEntryPoint.java

@@ -9,8 +9,7 @@ import io.github.prospector.modmenu.api.ModMenuApi;
 import me.shedaniel.rei.api.ConfigManager;
 import net.minecraft.client.gui.screen.Screen;
 
-import java.util.Optional;
-import java.util.function.Supplier;
+import java.util.function.Function;
 
 public class REIModMenuEntryPoint implements ModMenuApi {
     
@@ -19,14 +18,9 @@ public class REIModMenuEntryPoint implements ModMenuApi {
         return "roughlyenoughitems";
     }
     
-    @SuppressWarnings("deprecation")
     @Override
-    public Optional<Supplier<Screen>> getConfigScreen(Screen screen) {
-        return Optional.of(() -> getScreen(screen));
-    }
-    
-    public Screen getScreen(Screen parent) {
-        return ConfigManager.getInstance().getConfigScreen(parent);
+    public Function<Screen, ? extends Screen> getConfigScreenFactory() {
+        return parent -> ConfigManager.getInstance().getConfigScreen(parent);
     }
     
 }

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

@@ -288,7 +288,7 @@ public class RoughlyEnoughItemsCore implements ClientModInitializer {
         });
         ClothClientHooks.SCREEN_MOUSE_SCROLLED.register((minecraftClient, screen, v, v1, v2) -> {
             if (screen instanceof AbstractContainerScreen)
-                if (ScreenHelper.isOverlayVisible() && ScreenHelper.getLastOverlay().isInside(PointHelper.fromMouse()) && ScreenHelper.getLastOverlay().mouseScrolled(v, v1, v2))
+                if (ScreenHelper.isOverlayVisible() && ScreenHelper.getLastOverlay().mouseScrolled(v, v1, v2))
                     return ActionResult.SUCCESS;
             return ActionResult.PASS;
         });

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

@@ -120,6 +120,11 @@ public interface DisplayHelper {
         default Rectangle getItemListArea(Rectangle rectangle) {
             return new Rectangle(rectangle.x + 1, rectangle.y + 2 + (ConfigObject.getInstance().getSearchFieldLocation() == SearchFieldLocation.TOP_SIDE ? 24 : 0) + (ConfigObject.getInstance().isEntryListWidgetScrolled() ? 0 : 22), rectangle.width - 2, rectangle.height - (ConfigObject.getInstance().getSearchFieldLocation() != SearchFieldLocation.CENTER ? 27 + 22 : 27) + (!ConfigObject.getInstance().isEntryListWidgetScrolled() ? 0 : 22));
         }
+    
+        default Rectangle getFavoritesListArea(Rectangle rectangle) {
+            int offset = 31 + (ConfigObject.getInstance().doesShowUtilsButtons() ? 25 : 0);
+            return new Rectangle(rectangle.x + 1, rectangle.y + 2 + offset, rectangle.width - 2, rectangle.height - 5- offset);
+        }
         
         /**
          * Checks if REI should recalculate the overlay bounds

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

@@ -58,7 +58,7 @@ public interface EntryStack {
     static EntryStack readFromJson(JsonElement jsonElement) {
         try {
             JsonObject obj = jsonElement.getAsJsonObject();
-            switch (obj.get("type").getAsString()) {
+            switch (obj.getAsJsonPrimitive("type").getAsString()) {
                 case "stack":
                     return EntryStack.create(ItemStack.fromTag(StringNbtReader.parse(obj.get("nbt").getAsString())));
                 case "fluid":

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

@@ -47,6 +47,7 @@ public class ContainerScreenOverlay extends WidgetWithBounds {
     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 EntryListWidget ENTRY_LIST_WIDGET = new EntryListWidget();
+    private static FavoritesListWidget favoritesListWidget = null;
     private final List<Widget> widgets = Lists.newLinkedList();
     public boolean shouldReInit = false;
     private int tooltipWidth;
@@ -70,7 +71,7 @@ public class ContainerScreenOverlay extends WidgetWithBounds {
         VertexConsumerProvider.Immediate immediate = VertexConsumerProvider.immediate(Tessellator.getInstance().getBuffer());
         matrixStack_1.translate(0.0D, 0.0D, getBlitOffset());
         Matrix4f matrix4f_1 = matrixStack_1.peek().getModel();
-        for (int lineIndex = 0; lineIndex < tooltipLines.size(); lineIndex++) {
+        for(int lineIndex = 0; lineIndex < tooltipLines.size(); lineIndex++) {
             font.draw(tooltipLines.get(lineIndex), x, currentY, -1, true, matrix4f_1, immediate, false, 0, 15728880);
             currentY += lineIndex == 0 ? 12 : 10;
         }
@@ -88,6 +89,11 @@ public class ContainerScreenOverlay extends WidgetWithBounds {
         return ENTRY_LIST_WIDGET;
     }
     
+    @Nullable
+    public static FavoritesListWidget getFavoritesListWidget() {
+        return favoritesListWidget;
+    }
+    
     public void init(boolean useless) {
         init();
     }
@@ -102,6 +108,11 @@ public class ContainerScreenOverlay extends WidgetWithBounds {
         DisplayHelper.DisplayBoundsHandler boundsHandler = DisplayHelper.getInstance().getResponsibleBoundsHandler(MinecraftClient.getInstance().currentScreen.getClass());
         this.rectangle = ConfigObject.getInstance().isLeftHandSidePanel() ? boundsHandler.getLeftBounds(MinecraftClient.getInstance().currentScreen) : boundsHandler.getRightBounds(MinecraftClient.getInstance().currentScreen);
         widgets.add(ENTRY_LIST_WIDGET);
+        if (ConfigObject.getInstance().doDisplayFavoritesOnTheLeft() && ConfigObject.getInstance().isFavoritesEnabled()) {
+            if (favoritesListWidget == null)
+                favoritesListWidget = new FavoritesListWidget();
+            widgets.add(favoritesListWidget);
+        }
         ENTRY_LIST_WIDGET.updateArea(boundsHandler, ScreenHelper.getSearchField() == null ? "" : null);
         if (ScreenHelper.getSearchField() == null) {
             ScreenHelper.setSearchField(new OverlaySearchField(0, 0, 0, 0));
@@ -242,7 +253,7 @@ public class ContainerScreenOverlay extends WidgetWithBounds {
                 }
             });
             int xxx = ConfigObject.getInstance().isLeftHandSidePanel() ? window.getScaledWidth() - 30 : 10;
-            for (Weather weather : Weather.values()) {
+            for(Weather weather : Weather.values()) {
                 widgets.add(new ButtonWidget(new Rectangle(xxx, 35, 20, 20), "") {
                     @Override
                     public void onPressed() {
@@ -416,16 +427,13 @@ public class ContainerScreenOverlay extends WidgetWithBounds {
         if (shouldReInit)
             init();
         else {
-            for (DisplayHelper.DisplayBoundsHandler<?> handler : DisplayHelper.getInstance().getSortedBoundsHandlers(minecraft.currentScreen.getClass())) {
+            for(DisplayHelper.DisplayBoundsHandler<?> handler : DisplayHelper.getInstance().getSortedBoundsHandlers(minecraft.currentScreen.getClass())) {
                 if (handler != null && handler.shouldRecalculateArea(!ConfigObject.getInstance().isLeftHandSidePanel(), rectangle)) {
                     init();
                     break;
                 }
             }
         }
-        //        if (DisplayHelper.getInstance().getBaseBoundsHandler() != null && DisplayHelper.getInstance().getBaseBoundsHandler().shouldRecalculateArea(!ConfigObject.getInstance().isLeftHandSidePanel(), rectangle))
-        //            entryListWidget.updateArea(DisplayHelper.getInstance().getResponsibleBoundsHandler());
-        //        else
         if (ConfigManager.getInstance().isCraftableOnlyEnabled() && ((currentStacks.size() != ScreenHelper.inventoryStacks.size()) || !hasSameListContent(new LinkedList<>(ScreenHelper.inventoryStacks), currentStacks))) {
             ScreenHelper.inventoryStacks = currentStacks;
             ENTRY_LIST_WIDGET.updateSearch(ScreenHelper.getSearchField().getText());
@@ -436,7 +444,7 @@ public class ContainerScreenOverlay extends WidgetWithBounds {
             if (MinecraftClient.getInstance().currentScreen instanceof AbstractContainerScreen) {
                 ContainerScreenHooks hooks = (ContainerScreenHooks) MinecraftClient.getInstance().currentScreen;
                 int left = hooks.rei_getContainerLeft(), top = hooks.rei_getContainerTop();
-                for (Slot slot : ((AbstractContainerScreen<?>) MinecraftClient.getInstance().currentScreen).getContainer().slotList)
+                for(Slot slot : ((AbstractContainerScreen<?>) MinecraftClient.getInstance().currentScreen).getContainer().slotList)
                     if (!slot.hasStack() || !ENTRY_LIST_WIDGET.canLastSearchTermsBeAppliedTo(EntryStack.create(slot.getStack())))
                         fillGradient(left + slot.xPosition, top + slot.yPosition, left + slot.xPosition + 16, top + slot.yPosition + 16, -601874400, -601874400);
             }
@@ -447,7 +455,7 @@ public class ContainerScreenOverlay extends WidgetWithBounds {
         this.renderWidgets(mouseX, mouseY, delta);
         if (MinecraftClient.getInstance().currentScreen instanceof AbstractContainerScreen && ConfigObject.getInstance().areClickableRecipeArrowsEnabled()) {
             ContainerScreenHooks hooks = (ContainerScreenHooks) MinecraftClient.getInstance().currentScreen;
-            for (RecipeHelper.ScreenClickArea area : RecipeHelper.getInstance().getScreenClickAreas())
+            for(RecipeHelper.ScreenClickArea area : RecipeHelper.getInstance().getScreenClickAreas())
                 if (area.getScreenClass().equals(MinecraftClient.getInstance().currentScreen.getClass()))
                     if (area.getRectangle().contains(mouseX - hooks.rei_getContainerLeft(), mouseY - hooks.rei_getContainerTop())) {
                         String collect = CollectionUtils.mapAndJoinToString(area.getCategories(), identifier -> RecipeHelper.getInstance().getCategory(identifier).getCategoryName(), ", ");
@@ -465,7 +473,7 @@ public class ContainerScreenOverlay extends WidgetWithBounds {
         }
         Screen currentScreen = MinecraftClient.getInstance().currentScreen;
         if (!(currentScreen instanceof RecipeViewingScreen) || !((RecipeViewingScreen) currentScreen).choosePageActivated)
-            for (QueuedTooltip queuedTooltip : QUEUED_TOOLTIPS) {
+            for(QueuedTooltip queuedTooltip : QUEUED_TOOLTIPS) {
                 if (queuedTooltip != null)
                     renderTooltip(queuedTooltip);
             }
@@ -525,22 +533,28 @@ public class ContainerScreenOverlay extends WidgetWithBounds {
                 else
                     return false;
                 return true;
-            } else {
-                return ENTRY_LIST_WIDGET.mouseScrolled(i, j, amount);
-            }
+            } else if (ENTRY_LIST_WIDGET.mouseScrolled(i, j, amount))
+                return true;
         }
-        for (Widget widget : widgets)
-            if (widget != ENTRY_LIST_WIDGET && widget.mouseScrolled(i, j, amount))
+        if (isNotInExclusionZones(PointHelper.getMouseX(), PointHelper.getMouseY())) {
+            if (favoritesListWidget != null && favoritesListWidget.mouseScrolled(i, j, amount))
+                return true;
+        }
+        for(Widget widget : widgets)
+            if (widget != ENTRY_LIST_WIDGET && (favoritesListWidget == null || widget != favoritesListWidget) && widget.mouseScrolled(i, j, amount))
                 return true;
         return false;
     }
     
     @Override
     public boolean keyPressed(int int_1, int int_2, int int_3) {
-        if (ScreenHelper.isOverlayVisible())
-            for (Element listener : widgets)
-                if (listener.keyPressed(int_1, int_2, int_3))
+        if (ScreenHelper.isOverlayVisible()) {
+            if (ScreenHelper.getSearchField().keyPressed(int_1, int_2, int_3))
+                return true;
+            for(Element listener : widgets)
+                if (listener != ScreenHelper.getSearchField() && listener.keyPressed(int_1, int_2, int_3))
                     return true;
+        }
         if (ClientHelper.getInstance().getHideKeyBinding().matchesKey(int_1, int_2)) {
             ScreenHelper.toggleOverlayVisible();
             return true;
@@ -571,8 +585,10 @@ public class ContainerScreenOverlay extends WidgetWithBounds {
     public boolean charTyped(char char_1, int int_1) {
         if (!ScreenHelper.isOverlayVisible())
             return false;
-        for (Element listener : widgets)
-            if (listener.charTyped(char_1, int_1))
+        if (ScreenHelper.getSearchField().charTyped(char_1, int_1))
+            return true;
+        for(Element listener : widgets)
+            if (listener != ScreenHelper.getSearchField() && listener.charTyped(char_1, int_1))
                 return true;
         return false;
     }
@@ -588,7 +604,7 @@ public class ContainerScreenOverlay extends WidgetWithBounds {
             return false;
         if (MinecraftClient.getInstance().currentScreen instanceof AbstractContainerScreen && ConfigObject.getInstance().areClickableRecipeArrowsEnabled()) {
             ContainerScreenHooks hooks = (ContainerScreenHooks) MinecraftClient.getInstance().currentScreen;
-            for (RecipeHelper.ScreenClickArea area : RecipeHelper.getInstance().getScreenClickAreas())
+            for(RecipeHelper.ScreenClickArea area : RecipeHelper.getInstance().getScreenClickAreas())
                 if (area.getScreenClass().equals(MinecraftClient.getInstance().currentScreen.getClass()))
                     if (area.getRectangle().contains(double_1 - hooks.rei_getContainerLeft(), double_2 - hooks.rei_getContainerTop())) {
                         ClientHelper.getInstance().executeViewAllRecipesFromCategories(Arrays.asList(area.getCategories()));
@@ -596,7 +612,7 @@ public class ContainerScreenOverlay extends WidgetWithBounds {
                         return true;
                     }
         }
-        for (Element element : widgets)
+        for(Element element : widgets)
             if (element.mouseClicked(double_1, double_2, int_1)) {
                 this.setFocused(element);
                 if (int_1 == 0)
@@ -616,7 +632,7 @@ public class ContainerScreenOverlay extends WidgetWithBounds {
     public boolean isInside(double mouseX, double mouseY) {
         if (!rectangle.contains(mouseX, mouseY))
             return false;
-        for (DisplayHelper.DisplayBoundsHandler<?> handler : DisplayHelper.getInstance().getSortedBoundsHandlers(MinecraftClient.getInstance().currentScreen.getClass())) {
+        for(DisplayHelper.DisplayBoundsHandler<?> handler : DisplayHelper.getInstance().getSortedBoundsHandlers(MinecraftClient.getInstance().currentScreen.getClass())) {
             ActionResult in = handler.isInZone(!ConfigObject.getInstance().isLeftHandSidePanel(), mouseX, mouseY);
             if (in != ActionResult.PASS)
                 return in == ActionResult.SUCCESS;
@@ -625,7 +641,7 @@ public class ContainerScreenOverlay extends WidgetWithBounds {
     }
     
     public boolean isNotInExclusionZones(double mouseX, double mouseY) {
-        for (DisplayHelper.DisplayBoundsHandler<?> handler : DisplayHelper.getInstance().getSortedBoundsHandlers(MinecraftClient.getInstance().currentScreen.getClass())) {
+        for(DisplayHelper.DisplayBoundsHandler<?> handler : DisplayHelper.getInstance().getSortedBoundsHandlers(MinecraftClient.getInstance().currentScreen.getClass())) {
             ActionResult in = handler.isInZone(true, mouseX, mouseY);
             if (in != ActionResult.PASS)
                 return in == ActionResult.SUCCESS;

+ 51 - 34
src/main/java/me/shedaniel/rei/gui/widget/EntryListWidget.java

@@ -21,6 +21,7 @@ import me.shedaniel.rei.gui.config.ItemListOrdering;
 import me.shedaniel.rei.impl.ScreenHelper;
 import me.shedaniel.rei.impl.SearchArgument;
 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.DiffuseLighting;
@@ -43,12 +44,10 @@ import java.util.stream.Collectors;
 
 public class EntryListWidget extends WidgetWithBounds {
     
-    private static final boolean LAZY = true;
-    private static final String SPACE = " ", EMPTY = "";
-    private static final Supplier<Boolean> RENDER_EXTRA_CONFIG = ConfigObject.getInstance()::doesRenderEntryExtraOverlay;
+    static final Supplier<Boolean> RENDER_EXTRA_CONFIG = ConfigObject.getInstance()::doesRenderEntryExtraOverlay;
     @SuppressWarnings("deprecation")
-    private static final Comparator<? super EntryStack> ENTRY_NAME_COMPARER = Comparator.comparing(SearchArgument::tryGetEntryStackName);
-    private static final Comparator<? super EntryStack> ENTRY_GROUP_COMPARER = Comparator.comparingInt(stack -> {
+    static final Comparator<? super EntryStack> ENTRY_NAME_COMPARER = Comparator.comparing(SearchArgument::tryGetEntryStackName);
+    static final Comparator<? super EntryStack> ENTRY_GROUP_COMPARER = Comparator.comparingInt(stack -> {
         if (stack.getType() == EntryStack.Type.ITEM) {
             ItemGroup group = stack.getItem().getGroup();
             if (group != null)
@@ -56,6 +55,8 @@ public class EntryListWidget extends WidgetWithBounds {
         }
         return Integer.MAX_VALUE;
     });
+    private static final boolean LAZY = true;
+    private static final String SPACE = " ", EMPTY = "";
     private static int page;
     protected double target;
     protected double scroll;
@@ -71,6 +72,32 @@ public class EntryListWidget extends WidgetWithBounds {
     private List<SearchArgument.SearchArguments> lastSearchArguments = Collections.emptyList();
     private boolean draggingScrollBar = false;
     
+    @SuppressWarnings("rawtypes")
+    static boolean notSteppingOnExclusionZones(int left, int top, Rectangle listArea) {
+        MinecraftClient instance = MinecraftClient.getInstance();
+        for(DisplayHelper.DisplayBoundsHandler sortedBoundsHandler : DisplayHelper.getInstance().getSortedBoundsHandlers(instance.currentScreen.getClass())) {
+            ActionResult fit = sortedBoundsHandler.canItemSlotWidgetFit(!ConfigObject.getInstance().isLeftHandSidePanel(), left, top, instance.currentScreen, listArea);
+            if (fit != ActionResult.PASS)
+                return fit == ActionResult.SUCCESS;
+            fit = sortedBoundsHandler.canItemSlotWidgetFit(ConfigObject.getInstance().isLeftHandSidePanel(), left, top, instance.currentScreen, listArea);
+            if (fit != ActionResult.PASS)
+                return fit == ActionResult.SUCCESS;
+        }
+        return true;
+    }
+    
+    private static Rectangle updateInnerBounds(Rectangle bounds) {
+        if (ConfigObject.getInstance().isEntryListWidgetScrolled()) {
+            int width = Math.max(MathHelper.floor((bounds.width - 2 - 6) / 18f), 1);
+            if (ConfigObject.getInstance().isLeftHandSidePanel())
+                return new Rectangle(bounds.getCenterX() - width * 9 + 3, bounds.y, width * 18, bounds.height);
+            return new Rectangle(bounds.getCenterX() - width * 9 - 3, bounds.y, width * 18, bounds.height);
+        }
+        int width = Math.max(MathHelper.floor((bounds.width - 2) / 18f), 1);
+        int height = Math.max(MathHelper.floor((bounds.height - 2) / 18f), 1);
+        return new Rectangle(bounds.getCenterX() - width * 9, bounds.getCenterY() - height * 9, width * 18, height * 18);
+    }
+    
     protected final int getSlotsHeightNumberForFavorites() {
         if (favorites.isEmpty())
             return 0;
@@ -78,7 +105,8 @@ public class EntryListWidget extends WidgetWithBounds {
             return MathHelper.ceil(2 + favorites.size() / (innerBounds.width / 18f));
         int height = MathHelper.ceil(favorites.size() / (innerBounds.width / 18f));
         int pagesToFit = MathHelper.ceil(height / (innerBounds.height / 18f - 1));
-        if (height > (innerBounds.height / 18 - 1) && (height) % (innerBounds.height / 18) == (innerBounds.height / 18) - 2) height--;
+        if (height > (innerBounds.height / 18 - 1) && (height) % (innerBounds.height / 18) == (innerBounds.height / 18) - 2)
+            height--;
         return height + pagesToFit + 1;
     }
     
@@ -329,16 +357,19 @@ public class EntryListWidget extends WidgetWithBounds {
     
     public void updateArea(DisplayHelper.DisplayBoundsHandler<?> boundsHandler, @Nullable String searchTerm) {
         this.bounds = boundsHandler.getItemListArea(ScreenHelper.getLastOverlay().getBounds());
+        FavoritesListWidget favoritesListWidget = ContainerScreenOverlay.getFavoritesListWidget();
+        if (favoritesListWidget != null)
+            favoritesListWidget.updateFavoritesBounds(boundsHandler, searchTerm);
         if (searchTerm != null)
             updateSearch(searchTerm);
-        else if (allStacks == null || favorites == null)
+        else if (allStacks == null || favorites == null || (favoritesListWidget != null && favoritesListWidget.favorites == null))
             updateSearch("");
         else
             updateEntriesPosition();
     }
     
     public void updateEntriesPosition() {
-        this.innerBounds = updateInnerBounds();
+        this.innerBounds = updateInnerBounds(bounds);
         if (!ConfigObject.getInstance().isEntryListWidgetScrolled()) {
             page = Math.max(page, 0);
             List<EntryListEntry> entries = Lists.newLinkedList();
@@ -381,7 +412,7 @@ public class EntryListWidget extends WidgetWithBounds {
             int width = innerBounds.width / 18;
             int pageHeight = innerBounds.height / 18;
             int sizeForFavorites = getScrollNumberForFavorites();
-            int slotsToPrepare = allStacks.size() * 2 + sizeForFavorites * 2;
+            int slotsToPrepare = allStacks.size() * 3 + sizeForFavorites * 3;
             int currentX = 0;
             int currentY = 0;
             List<EntryListEntry> entries = Lists.newLinkedList();
@@ -398,6 +429,9 @@ public class EntryListWidget extends WidgetWithBounds {
             this.entries = entries;
             this.widgets = Collections.unmodifiableList(entries);
         }
+        FavoritesListWidget favoritesListWidget = ContainerScreenOverlay.getFavoritesListWidget();
+        if (favoritesListWidget != null)
+            favoritesListWidget.updateEntriesPosition();
     }
     
     @Deprecated
@@ -405,28 +439,6 @@ public class EntryListWidget extends WidgetWithBounds {
         return allStacks;
     }
     
-    private Rectangle updateInnerBounds() {
-        if (ConfigObject.getInstance().isEntryListWidgetScrolled()) {
-            int width = Math.max(MathHelper.floor((bounds.width - 2 - 6) / 18f), 1);
-            if (ConfigObject.getInstance().isLeftHandSidePanel())
-                return new Rectangle(bounds.getCenterX() - width * 9 + 3, bounds.y, width * 18, bounds.height);
-            return new Rectangle(bounds.getCenterX() - width * 9 - 3, bounds.y, width * 18, bounds.height);
-        }
-        int width = Math.max(MathHelper.floor((bounds.width - 2) / 18f), 1);
-        int height = Math.max(MathHelper.floor((bounds.height - 2) / 18f), 1);
-        return new Rectangle(bounds.getCenterX() - width * 9, bounds.getCenterY() - height * 9, width * 18, height * 18);
-    }
-    
-    @SuppressWarnings("rawtypes")
-    private boolean notSteppingOnExclusionZones(int left, int top, Rectangle listArea) {
-        for(DisplayHelper.DisplayBoundsHandler sortedBoundsHandler : DisplayHelper.getInstance().getSortedBoundsHandlers(minecraft.currentScreen.getClass())) {
-            ActionResult fit = sortedBoundsHandler.canItemSlotWidgetFit(!ConfigObject.getInstance().isLeftHandSidePanel(), left, top, minecraft.currentScreen, listArea);
-            if (fit != ActionResult.PASS)
-                return fit == ActionResult.SUCCESS;
-        }
-        return true;
-    }
-    
     @SuppressWarnings("deprecation")
     public void updateSearch(String searchTerm) {
         lastSearchArguments = processSearchTerm(searchTerm);
@@ -435,7 +447,7 @@ public class EntryListWidget extends WidgetWithBounds {
             boolean checkCraftable = ConfigManager.getInstance().isCraftableOnlyEnabled() && !ScreenHelper.inventoryStacks.isEmpty();
             List<EntryStack> workingItems = checkCraftable ? RecipeHelper.getInstance().findCraftableEntriesByItems(CollectionUtils.map(ScreenHelper.inventoryStacks, EntryStack::create)) : null;
             for(EntryStack stack : EntryRegistry.getInstance().getStacksList()) {
-                if (lastSearchArguments.isEmpty() || canSearchTermsBeAppliedTo(stack, lastSearchArguments)) {
+                if (canLastSearchTermsBeAppliedTo(stack)) {
                     if (workingItems != null && CollectionUtils.findFirstOrNullEquals(workingItems, stack) == null)
                         continue;
                     list.add(stack.copy().setting(EntryStack.Settings.RENDER_COUNTS, EntryStack.Settings.FALSE).setting(EntryStack.Settings.Item.RENDER_OVERLAY, RENDER_EXTRA_CONFIG));
@@ -455,7 +467,7 @@ public class EntryListWidget extends WidgetWithBounds {
             boolean checkCraftable = ConfigManager.getInstance().isCraftableOnlyEnabled() && !ScreenHelper.inventoryStacks.isEmpty();
             List<EntryStack> workingItems = checkCraftable ? RecipeHelper.getInstance().findCraftableEntriesByItems(CollectionUtils.map(ScreenHelper.inventoryStacks, EntryStack::create)) : null;
             for(EntryStack stack : ConfigManager.getInstance().getFavorites()) {
-                if (lastSearchArguments.isEmpty() || canSearchTermsBeAppliedTo(stack, lastSearchArguments)) {
+                if (canLastSearchTermsBeAppliedTo(stack)) {
                     if (workingItems != null && CollectionUtils.findFirstOrNullEquals(workingItems, stack) == null)
                         continue;
                     list.add(stack.copy().setting(EntryStack.Settings.RENDER_COUNTS, EntryStack.Settings.FALSE).setting(EntryStack.Settings.Item.RENDER_OVERLAY, RENDER_EXTRA_CONFIG));
@@ -471,12 +483,15 @@ public class EntryListWidget extends WidgetWithBounds {
             favorites = list;
         } else
             favorites = Collections.emptyList();
+        FavoritesListWidget favoritesListWidget = ContainerScreenOverlay.getFavoritesListWidget();
+        if (favoritesListWidget != null)
+            favoritesListWidget.updateSearch(this, searchTerm);
         updateEntriesPosition();
     }
     
     @SuppressWarnings("deprecation")
     public boolean canLastSearchTermsBeAppliedTo(EntryStack stack) {
-        return canSearchTermsBeAppliedTo(stack, lastSearchArguments);
+        return lastSearchArguments.isEmpty() || canSearchTermsBeAppliedTo(stack, lastSearchArguments);
     }
     
     @SuppressWarnings("deprecation")
@@ -684,6 +699,7 @@ public class EntryListWidget extends WidgetWithBounds {
                             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;
                     }
@@ -695,6 +711,7 @@ public class EntryListWidget extends WidgetWithBounds {
                         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;
                 }

+ 412 - 0
src/main/java/me/shedaniel/rei/gui/widget/FavoritesListWidget.java

@@ -0,0 +1,412 @@
+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.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.DiffuseLighting;
+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.*;
+
+public class FavoritesListWidget extends WidgetWithBounds {
+    protected double target;
+    protected double scroll;
+    protected long start;
+    protected long duration;
+    protected int blockedCount;
+    List<EntryStack> favorites = null;
+    private Rectangle bounds, innerBounds;
+    private List<EntryListEntry> entries = Collections.emptyList();
+    private boolean draggingScrollBar = false;
+    
+    private static Rectangle updateInnerBounds(Rectangle bounds) {
+        int width = Math.max(MathHelper.floor((bounds.width - 2 - 6) / 18f), 1);
+        if (!ConfigObject.getInstance().isLeftHandSidePanel())
+            return new Rectangle(bounds.getCenterX() - width * 9 + 3, bounds.y, width * 18, bounds.height);
+        return new Rectangle(bounds.getCenterX() - width * 9 - 3, bounds.y, width * 18, bounds.height);
+    }
+    
+    protected final int getMaxScrollPosition() {
+        return MathHelper.ceil((favorites.size() + blockedCount) / (innerBounds.width / 18f)) * 18;
+    }
+    
+    protected final int getMaxScroll() {
+        return Math.max(0, this.getMaxScrollPosition() - innerBounds.height);
+    }
+    
+    protected final double clamp(double v) {
+        return this.clamp(v, 200.0D);
+    }
+    
+    protected final double clamp(double v, double clampExtension) {
+        return MathHelper.clamp(v, -clampExtension, (double) this.getMaxScroll() + clampExtension);
+    }
+    
+    protected final void offset(double value, boolean animated) {
+        scrollTo(target + value, animated);
+    }
+    
+    protected final void scrollTo(double value, boolean animated) {
+        scrollTo(value, animated, ClothConfigInitializer.getScrollDuration());
+    }
+    
+    protected final void scrollTo(double value, boolean animated, long duration) {
+        target = clamp(value);
+        
+        if (animated) {
+            start = System.currentTimeMillis();
+            this.duration = duration;
+        } else
+            scroll = target;
+    }
+    
+    @Override
+    public boolean mouseScrolled(double double_1, double double_2, double double_3) {
+        if (ConfigObject.getInstance().isEntryListWidgetScrolled() && bounds.contains(double_1, double_2)) {
+            offset(ClothConfigInitializer.getScrollStep() * -double_3, true);
+            return true;
+        }
+        return super.mouseScrolled(double_1, double_2, double_3);
+    }
+    
+    @Override
+    public Rectangle getBounds() {
+        return bounds;
+    }
+    
+    @Override
+    public void render(int mouseX, int mouseY, float delta) {
+        for(EntryListEntry entry : entries)
+            entry.clearStacks();
+        ScissorsHandler.INSTANCE.scissor(bounds);
+        int skip = Math.max(0, MathHelper.floor(scroll / 18f));
+        int nextIndex = skip * innerBounds.width / 18;
+        int i = nextIndex;
+        blockedCount = 0;
+        back:
+        for(; i < favorites.size(); i++) {
+            EntryStack stack = favorites.get(i);
+            while (true) {
+                EntryListEntry entry = entries.get(nextIndex);
+                entry.getBounds().y = (int) (entry.backupY - scroll);
+                if (entry.getBounds().y > bounds.getMaxY())
+                    break back;
+                if (notSteppingOnExclusionZones(entry.getBounds().x, entry.getBounds().y, innerBounds)) {
+                    entry.entry(stack);
+                    entry.render(mouseX, mouseY, delta);
+                    nextIndex++;
+                    break;
+                } else {
+                    blockedCount++;
+                    nextIndex++;
+                }
+            }
+        }
+        updatePosition(delta);
+        ScissorsHandler.INSTANCE.removeLastScissor();
+        renderScrollbar();
+    }
+    
+    private int getScrollbarMinX() {
+        if (!ConfigObject.getInstance().isLeftHandSidePanel())
+            return bounds.x + 1;
+        return bounds.getMaxX() - 7;
+    }
+    
+    @Override
+    public boolean mouseDragged(double mouseX, double mouseY, int int_1, double double_3, double double_4) {
+        if (int_1 == 0 && draggingScrollBar) {
+            float height = getMaxScrollPosition();
+            int actualHeight = innerBounds.height;
+            if (height > actualHeight && mouseY >= innerBounds.y && mouseY <= innerBounds.getMaxY()) {
+                double double_5 = (double) Math.max(1, this.getMaxScroll());
+                int int_2 = innerBounds.height;
+                int int_3 = MathHelper.clamp((int) ((float) (int_2 * int_2) / (float) getMaxScrollPosition()), 32, int_2 - 8);
+                double double_6 = Math.max(1.0D, double_5 / (double) (int_2 - int_3));
+                float to = MathHelper.clamp((float) (scroll + double_4 * double_6), 0, height - innerBounds.height);
+                if (ConfigObject.getInstance().doesSnapToRows()) {
+                    double nearestRow = Math.round(to / 18.0) * 18.0;
+                    scrollTo(nearestRow, false);
+                } else
+                    scrollTo(to, false);
+            }
+        }
+        return super.mouseDragged(mouseX, mouseY, int_1, double_3, double_4);
+    }
+    
+    private void renderScrollbar() {
+        int maxScroll = getMaxScroll();
+        if (maxScroll > 0) {
+            int height = innerBounds.height * innerBounds.height / getMaxScrollPosition();
+            height = MathHelper.clamp(height, 32, innerBounds.height - 8);
+            height -= Math.min((scroll < 0 ? (int) -scroll : scroll > maxScroll ? (int) scroll - maxScroll : 0), height * .95);
+            height = Math.max(10, height);
+            int minY = Math.min(Math.max((int) scroll * (innerBounds.height - height) / maxScroll + innerBounds.y, innerBounds.y), innerBounds.getMaxY() - height);
+            
+            int scrollbarPositionMinX = getScrollbarMinX();
+            int scrollbarPositionMaxX = scrollbarPositionMinX + 6;
+            boolean hovered = (new Rectangle(scrollbarPositionMinX, minY, scrollbarPositionMaxX - scrollbarPositionMinX, height)).contains(PointHelper.fromMouse());
+            float bottomC = (hovered ? .67f : .5f) * (ScreenHelper.isDarkModeEnabled() ? 0.8f : 1f);
+            float topC = (hovered ? .87f : .67f) * (ScreenHelper.isDarkModeEnabled() ? 0.8f : 1f);
+            
+            DiffuseLighting.disable();
+            RenderSystem.disableTexture();
+            RenderSystem.enableBlend();
+            RenderSystem.disableAlphaTest();
+            RenderSystem.blendFuncSeparate(770, 771, 1, 0);
+            RenderSystem.shadeModel(7425);
+            Tessellator tessellator = Tessellator.getInstance();
+            BufferBuilder buffer = tessellator.getBuffer();
+            buffer.begin(7, VertexFormats.POSITION_COLOR);
+            buffer.vertex(scrollbarPositionMinX, minY + height, 0.0D).color(bottomC, bottomC, bottomC, 1).next();
+            buffer.vertex(scrollbarPositionMaxX, minY + height, 0.0D).color(bottomC, bottomC, bottomC, 1).next();
+            buffer.vertex(scrollbarPositionMaxX, minY, 0.0D).color(bottomC, bottomC, bottomC, 1).next();
+            buffer.vertex(scrollbarPositionMinX, minY, 0.0D).color(bottomC, bottomC, bottomC, 1).next();
+            tessellator.draw();
+            buffer.begin(7, VertexFormats.POSITION_COLOR);
+            buffer.vertex(scrollbarPositionMinX, (minY + height - 1), 0.0D).color(topC, topC, topC, 1).next();
+            buffer.vertex((scrollbarPositionMaxX - 1), (minY + height - 1), 0.0D).color(topC, topC, topC, 1).next();
+            buffer.vertex((scrollbarPositionMaxX - 1), minY, 0.0D).color(topC, topC, topC, 1).next();
+            buffer.vertex(scrollbarPositionMinX, minY, 0.0D).color(topC, topC, topC, 1).next();
+            tessellator.draw();
+            RenderSystem.shadeModel(7424);
+            RenderSystem.disableBlend();
+            RenderSystem.enableAlphaTest();
+            RenderSystem.enableTexture();
+        }
+    }
+    
+    private void updatePosition(float delta) {
+        target = clamp(target);
+        if (target < 0) {
+            target -= target * (1 - ClothConfigInitializer.getBounceBackMultiplier()) * delta / 3;
+        } else if (target > getMaxScroll()) {
+            target = (target - getMaxScroll()) * (1 - (1 - ClothConfigInitializer.getBounceBackMultiplier()) * delta / 3) + getMaxScroll();
+        } else if (ConfigObject.getInstance().doesSnapToRows()) {
+            double nearestRow = Math.round(target / 18.0) * 18.0;
+            if (!DynamicNewSmoothScrollingEntryListWidget.Precision.almostEquals(target, nearestRow, DynamicNewSmoothScrollingEntryListWidget.Precision.FLOAT_EPSILON))
+                target += (nearestRow - target) * Math.min(delta / 2.0, 1.0);
+            else
+                target = nearestRow;
+        }
+        if (!DynamicNewSmoothScrollingEntryListWidget.Precision.almostEquals(scroll, target, DynamicNewSmoothScrollingEntryListWidget.Precision.FLOAT_EPSILON))
+            scroll = (float) DynamicNewSmoothScrollingEntryListWidget.Interpolation.expoEase(scroll, target, Math.min((System.currentTimeMillis() - start) / ((double) duration), 1));
+        else
+            scroll = target;
+    }
+    
+    @Override
+    public boolean keyPressed(int int_1, int int_2, int int_3) {
+        if (containsMouse(PointHelper.fromMouse()))
+            for(Widget widget : children())
+                if (widget.keyPressed(int_1, int_2, int_3))
+                    return true;
+        return false;
+    }
+    
+    @SuppressWarnings("rawtypes")
+    public void updateFavoritesBounds(DisplayHelper.DisplayBoundsHandler boundsHandler, @Nullable String searchTerm) {
+        this.bounds = boundsHandler.getFavoritesListArea(!ConfigObject.getInstance().isLeftHandSidePanel() ? boundsHandler.getLeftBounds(MinecraftClient.getInstance().currentScreen) : boundsHandler.getRightBounds(MinecraftClient.getInstance().currentScreen));
+    }
+    
+    @SuppressWarnings("deprecation")
+    public void updateSearch(EntryListWidget listWidget, String searchTerm) {
+        if (ConfigObject.getInstance().isFavoritesEnabled() && ConfigObject.getInstance().doDisplayFavoritesOnTheLeft()) {
+            List<EntryStack> list = Lists.newLinkedList();
+            boolean checkCraftable = ConfigManager.getInstance().isCraftableOnlyEnabled() && !ScreenHelper.inventoryStacks.isEmpty();
+            List<EntryStack> workingItems = checkCraftable ? RecipeHelper.getInstance().findCraftableEntriesByItems(CollectionUtils.map(ScreenHelper.inventoryStacks, EntryStack::create)) : null;
+            for(EntryStack stack : ConfigManager.getInstance().getFavorites()) {
+                if (listWidget.canLastSearchTermsBeAppliedTo(stack)) {
+                    if (workingItems != null && CollectionUtils.findFirstOrNullEquals(workingItems, stack) == null)
+                        continue;
+                    list.add(stack.copy().setting(EntryStack.Settings.RENDER_COUNTS, EntryStack.Settings.FALSE).setting(EntryStack.Settings.Item.RENDER_OVERLAY, RENDER_EXTRA_CONFIG));
+                }
+            }
+            ItemListOrdering ordering = ConfigObject.getInstance().getItemListOrdering();
+            if (ordering == ItemListOrdering.name)
+                list.sort(ENTRY_NAME_COMPARER);
+            if (ordering == ItemListOrdering.item_groups)
+                list.sort(ENTRY_GROUP_COMPARER);
+            if (!ConfigObject.getInstance().isItemListAscending())
+                Collections.reverse(list);
+            favorites = list;
+        } else
+            favorites = Collections.emptyList();
+    }
+    
+    public void updateEntriesPosition() {
+        this.innerBounds = updateInnerBounds(bounds);
+        int width = innerBounds.width / 18;
+        int pageHeight = innerBounds.height / 18;
+        int slotsToPrepare = favorites.size() * 3;
+        int currentX = 0;
+        int currentY = 0;
+        List<EntryListEntry> entries = Lists.newLinkedList();
+        for(int i = 0; i < slotsToPrepare; i++) {
+            int xPos = currentX * 18 + innerBounds.x;
+            int yPos = currentY * 18 + innerBounds.y;
+            entries.add((EntryListEntry) new EntryListEntry(xPos, yPos).noBackground());
+            currentX++;
+            if (currentX >= width) {
+                currentX = 0;
+                currentY++;
+            }
+        }
+        this.entries = entries;
+    }
+    
+    @Override
+    public List<? extends Widget> children() {
+        return entries;
+    }
+    
+    @Override
+    public boolean mouseClicked(double double_1, double double_2, int int_1) {
+        double height = getMaxScroll();
+        int actualHeight = bounds.height;
+        if (height > actualHeight && double_2 >= bounds.y && double_2 <= bounds.getMaxY()) {
+            double scrollbarPositionMinX = getScrollbarMinX();
+            if (double_1 >= scrollbarPositionMinX - 1 & double_1 <= scrollbarPositionMinX + 8) {
+                this.draggingScrollBar = true;
+                return true;
+            }
+        }
+        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 true;
+            }
+            if (!player.inventory.getCursorStack().isEmpty() && RoughlyEnoughItemsCore.hasPermissionToUsePackets())
+                return false;
+            for(Widget widget : children())
+                if (widget.mouseClicked(double_1, double_2, int_1))
+                    return true;
+        }
+        return false;
+    }
+    
+    private class EntryListEntry extends EntryWidget {
+        private int backupY;
+        
+        private EntryListEntry(int x, int y) {
+            super(x, y);
+            this.backupY = y;
+        }
+        
+        @Override
+        public boolean containsMouse(double mouseX, double mouseY) {
+            return super.containsMouse(mouseX, mouseY) && bounds.contains(mouseX, mouseY);
+        }
+        
+        @Override
+        protected void drawHighlighted(int mouseX, int mouseY, float delta) {
+            if (getCurrentEntry().getType() != EntryStack.Type.EMPTY)
+                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, new Object[0]);
+                    string_2 = Objects.equals(string_3, string_1) ? I18n.translate(InputUtil.Type.MOUSE.getName(), new Object[]{int_1 + 1}) : string_3;
+            }
+            
+            return string_2 == null ? I18n.translate(string_1, new Object[0]) : 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()) {
+                        String name = getLocalizedName(ConfigObject.getInstance().getFavoriteKeybind());
+                        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()) {
+                InputUtil.KeyCode keyCode = ConfigObject.getInstance().getFavoriteKeybind();
+                if (int_1 == InputUtil.UNKNOWN_KEYCODE.getKeyCode()) {
+                    if (keyCode.getCategory() == InputUtil.Type.SCANCODE && keyCode.getKeyCode() == 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;
+                    }
+                } else if (keyCode.getCategory() == InputUtil.Type.KEYSYM && keyCode.getKeyCode() == int_1) {
+                    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.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);
+        }
+    }
+}

+ 12 - 23
src/main/java/me/shedaniel/rei/impl/ConfigManagerImpl.java

@@ -13,7 +13,9 @@ import me.sargunvohra.mcmods.autoconfig1u.gui.ConfigScreenProvider;
 import me.sargunvohra.mcmods.autoconfig1u.gui.registry.GuiRegistry;
 import me.sargunvohra.mcmods.autoconfig1u.serializer.JanksonConfigSerializer;
 import me.sargunvohra.mcmods.autoconfig1u.shadowed.blue.endless.jankson.Jankson;
+import me.sargunvohra.mcmods.autoconfig1u.shadowed.blue.endless.jankson.JsonObject;
 import me.sargunvohra.mcmods.autoconfig1u.shadowed.blue.endless.jankson.JsonPrimitive;
+import me.sargunvohra.mcmods.autoconfig1u.shadowed.blue.endless.jankson.impl.SyntaxError;
 import me.shedaniel.cloth.hooks.ScreenHooks;
 import me.shedaniel.clothconfig2.api.AbstractConfigListEntry;
 import me.shedaniel.clothconfig2.api.ConfigEntryBuilder;
@@ -50,40 +52,26 @@ public class ConfigManagerImpl implements ConfigManager {
     public ConfigManagerImpl() {
         this.craftableOnly = false;
         AutoConfig.register(ConfigObjectImpl.class, (definition, configClass) -> {
-            return new JanksonConfigSerializer<ConfigObjectImpl>(definition, configClass, Jankson.builder()
-                    .registerPrimitiveTypeAdapter(InputUtil.KeyCode.class, it -> {
-                        return it instanceof String ? InputUtil.fromName((String) it) : null;
-                    })
-                    .registerSerializer(InputUtil.KeyCode.class, (it, marshaller) -> new JsonPrimitive(it.getName()))
-                    .build());
+            return new JanksonConfigSerializer<ConfigObjectImpl>(definition, configClass, Jankson.builder().registerPrimitiveTypeAdapter(InputUtil.KeyCode.class, it -> {
+                return it instanceof String ? InputUtil.fromName((String) it) : null;
+            }).registerSerializer(InputUtil.KeyCode.class, (it, marshaller) -> new JsonPrimitive(it.getName())).build());
         });
         GuiRegistry guiRegistry = AutoConfig.getGuiRegistry(ConfigObjectImpl.class);
         //noinspection rawtypes
-        guiRegistry.registerAnnotationProvider((i13n, field, config, defaults, guiProvider) -> Collections.singletonList(
-                ConfigEntryBuilder.create().startEnumSelector(i13n, (Class) field.getType(), getUnsafely(field, config, null))
-                        .setDefaultValue(() -> getUnsafely(field, defaults))
-                        .setSaveConsumer(newValue -> setUnsafely(field, config, newValue))
-                        .build()
-        ), field -> field.getType().isEnum(), ConfigObject.UseEnumSelectorInstead.class);
+        guiRegistry.registerAnnotationProvider((i13n, field, config, defaults, guiProvider) -> Collections.singletonList(ConfigEntryBuilder.create().startEnumSelector(i13n, (Class) field.getType(), getUnsafely(field, config, null)).setDefaultValue(() -> getUnsafely(field, defaults)).setSaveConsumer(newValue -> setUnsafely(field, config, newValue)).build()), field -> field.getType().isEnum(), ConfigObject.UseEnumSelectorInstead.class);
         loadFavoredEntries();
         guiRegistry.registerAnnotationProvider((i13n, field, config, defaults, guiProvider) -> {
             @SuppressWarnings("rawtypes") List<AbstractConfigListEntry> entries = new ArrayList<>();
-            for (FabricKeyBinding binding : ClientHelper.getInstance().getREIKeyBindings()) {
+            for(FabricKeyBinding binding : ClientHelper.getInstance().getREIKeyBindings()) {
                 entries.add(ConfigEntryBuilder.create().fillKeybindingField(I18n.translate(binding.getId()) + ":", binding).build());
             }
-            KeyCodeEntry entry = ConfigEntryBuilder.create().startKeyCodeField(i13n, getUnsafely(field, config, InputUtil.UNKNOWN_KEYCODE))
-                    .setDefaultValue(() -> getUnsafely(field, defaults))
-                    .setSaveConsumer(newValue -> setUnsafely(field, config, newValue))
-                    .build();
+            KeyCodeEntry entry = ConfigEntryBuilder.create().startKeyCodeField(i13n, getUnsafely(field, config, InputUtil.UNKNOWN_KEYCODE)).setDefaultValue(() -> getUnsafely(field, defaults)).setSaveConsumer(newValue -> setUnsafely(field, config, newValue)).build();
             entry.setAllowMouse(false);
             entries.add(entry);
             return entries;
         }, field -> field.getType() == InputUtil.KeyCode.class, ConfigObject.AddInFrontKeyCode.class);
         guiRegistry.registerPredicateProvider((i13n, field, config, defaults, guiProvider) -> {
-            KeyCodeEntry entry = ConfigEntryBuilder.create().startKeyCodeField(i13n, getUnsafely(field, config, InputUtil.UNKNOWN_KEYCODE))
-                    .setDefaultValue(() -> getUnsafely(field, defaults))
-                    .setSaveConsumer(newValue -> setUnsafely(field, config, newValue))
-                    .build();
+            KeyCodeEntry entry = ConfigEntryBuilder.create().startKeyCodeField(i13n, getUnsafely(field, config, InputUtil.UNKNOWN_KEYCODE)).setDefaultValue(() -> getUnsafely(field, defaults)).setSaveConsumer(newValue -> setUnsafely(field, config, newValue)).build();
             entry.setAllowMouse(false);
             return Collections.singletonList(entry);
         }, field -> field.getType() == InputUtil.KeyCode.class);
@@ -155,7 +143,8 @@ public class ConfigManagerImpl implements ConfigManager {
                             public void render(int int_1, int int_2, float float_1) {
                                 if (RecipeHelper.getInstance().arePluginsLoading()) {
                                     MinecraftClient.getInstance().openScreen(new ConfigReloadingScreen(MinecraftClient.getInstance().currentScreen));
-                                } else super.render(int_1, int_2, float_1);
+                                } else
+                                    super.render(int_1, int_2, float_1);
                             }
                         });
                     }
@@ -180,7 +169,7 @@ public class ConfigManagerImpl implements ConfigManager {
                 renderDirtBackground(0);
                 List<String> list = minecraft.textRenderer.wrapStringToWidthAsList(I18n.translate("text.rei.config_api_failed"), width - 100);
                 int y = (int) (height / 2 - minecraft.textRenderer.fontHeight * 1.3f / 2 * list.size());
-                for (int i = 0; i < list.size(); i++) {
+                for(int i = 0; i < list.size(); i++) {
                     String s = list.get(i);
                     drawCenteredString(minecraft.textRenderer, s, width / 2, y, -1);
                     y += minecraft.textRenderer.fontHeight;

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

@@ -60,8 +60,8 @@
   "text.rei.choose_page": "Choose Page",
   "text.rei.gamemode_button.tooltip": "Switch Game Mode\n§7Switch to %s mode.\n\n§7Shift-Click to switch in a reverse cycle.",
   "text.rei.weather_button.tooltip": "Switch Weather\n§7Switch to %s.",
-  "text.rei.reload_config": "Reload Config",
-  "text.rei.config.is.reloading": "Config is reloading!",
+  "text.rei.reload_config": "Reload Plugins",
+  "text.rei.config.is.reloading": "Plugins are reloading!",
   "text.rei.enabled": "Yes",
   "text.rei.disabled": "No",
   "text.rei.short_gamemode.survival": "S",