Pārlūkot izejas kodu

more work on favorites

shedaniel 5 gadi atpakaļ
vecāks
revīzija
8b43e80208

+ 7 - 6
build.gradle

@@ -1,21 +1,19 @@
 import java.text.SimpleDateFormat
 
 plugins {
-    id 'fabric-loom' version '0.2.5-SNAPSHOT'
+    id 'fabric-loom' version '0.2.6-SNAPSHOT'
     id 'maven-publish'
     id 'net.minecrell.licenser' version '0.4.1'
 }
 
-sourceCompatibility = 1.8
-targetCompatibility = 1.8
+sourceCompatibility = targetCompatibility = 1.8
 
 archivesBaseName = "RoughlyEnoughItems"
 group = "me.shedaniel"
 
-def ENV = System.getenv()
 def forceVersion = ""
 
-version = forceVersion != "" ? forceVersion : (((String) project.mod_version).contains("unstable") ? (project.mod_version + "." + buildTime()) : project.mod_version)
+version = forceVersion != "" ? forceVersion : ((project.mod_version as String).contains("unstable") ? (project.mod_version + "." + buildTime()) : project.mod_version)
 
 def includeDep = true
 
@@ -48,7 +46,7 @@ processResources {
 
 dependencies {
     minecraft "com.mojang:minecraft:${project.minecraft_version}"
-    mappings "net.fabricmc:yarn:${project.yarn_version}"
+    mappings "net.fabricmc:yarn:${project.yarn_version}:v2"
     modApi "net.fabricmc:fabric-loader:${project.fabricloader_version}"
     modApi "net.fabricmc.fabric-api:fabric-api:${project.fabric_api}"
     modApi("me.shedaniel.cloth:cloth-events:${cloth_events_version}") {
@@ -73,6 +71,9 @@ dependencies {
     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") {
+        transitive = false
+    }
 }
 
 task sourcesJar(type: Jar, dependsOn: classes) {

+ 1 - 1
gradle.properties

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

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

@@ -8,6 +8,7 @@ package me.shedaniel.rei.api;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import me.shedaniel.math.api.Rectangle;
+import me.shedaniel.rei.api.annotations.Internal;
 import me.shedaniel.rei.gui.widget.QueuedTooltip;
 import me.shedaniel.rei.impl.EmptyEntryStack;
 import me.shedaniel.rei.impl.FluidEntryStack;
@@ -52,6 +53,8 @@ public interface EntryStack {
         return new ItemEntryStack(new ItemStack(item));
     }
     
+    @Internal
+    @Deprecated
     static EntryStack readFromJson(JsonElement jsonElement) {
         try {
             JsonObject obj = jsonElement.getAsJsonObject();
@@ -71,6 +74,8 @@ public interface EntryStack {
         }
     }
     
+    @Internal
+    @Deprecated
     @Nullable
     default JsonElement toJson() {
         try {
@@ -85,7 +90,6 @@ public interface EntryStack {
                     obj2.addProperty("type", "fluid");
                     obj2.addProperty("id", getIdentifier().get().toString());
                     return obj2;
-                case RENDER:
                 case EMPTY:
                     JsonObject obj3 = new JsonObject();
                     obj3.addProperty("type", "empty");

+ 11 - 4
src/main/java/me/shedaniel/rei/gui/widget/ClickableLabelWidget.java

@@ -58,10 +58,17 @@ public abstract class ClickableLabelWidget extends LabelWidget {
             color = getHoveredColor();
         Point pos = getPosition();
         int width = font.getStringWidth(getText());
-        if (isHasShadows())
-            font.drawWithShadow(getText(), pos.x - width / 2, pos.y, color);
-        else
-            font.draw(getText(), pos.x - width / 2, pos.y, color);
+        if (isCentered()) {
+            if (isHasShadows())
+                font.drawWithShadow(getText(), pos.x - width / 2, pos.y, color);
+            else
+                font.draw(getText(), pos.x - width / 2, pos.y, color);
+        } else {
+            if (isHasShadows())
+                font.drawWithShadow(getText(), pos.x, pos.y, color);
+            else
+                font.draw(getText(), pos.x, pos.y, color);
+        }
         if (isClickable() && getTooltips().isPresent())
             if (!focused && containsMouse(mouseX, mouseY))
                 ScreenHelper.getLastOverlay().addTooltip(QueuedTooltip.create(getTooltips().get().split("\n")));

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

@@ -10,6 +10,7 @@ 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.Point;
 import me.shedaniel.math.api.Rectangle;
 import me.shedaniel.math.impl.PointHelper;
 import me.shedaniel.rei.RoughlyEnoughItemsCore;
@@ -50,7 +51,8 @@ public class EntryListWidget extends WidgetWithBounds {
     private 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) return group.getIndex();
+            if (group != null)
+                return group.getIndex();
         }
         return Integer.MAX_VALUE;
     });
@@ -64,17 +66,25 @@ public class EntryListWidget extends WidgetWithBounds {
     private List<EntryStack> allStacks = null;
     private List<EntryStack> favorites = null;
     private List<EntryListEntry> entries = Collections.emptyList();
+    private List<Widget> widgets = Collections.emptyList();
     @SuppressWarnings("deprecation")
     private List<SearchArgument.SearchArguments> lastSearchArguments = Collections.emptyList();
     private boolean draggingScrollBar = false;
     
     protected final int getSlotsHeightNumberForFavorites() {
-        if (favorites.isEmpty()) return 0;
-        return MathHelper.ceil(2 + favorites.size() / (innerBounds.width / 18f));
+        if (favorites.isEmpty())
+            return 0;
+        if (ConfigObject.getInstance().isEntryListWidgetScrolled())
+            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--;
+        return height + pagesToFit + 1;
     }
     
     protected final int getScrollNumberForFavorites() {
-        if (favorites.isEmpty()) return 0;
+        if (favorites.isEmpty())
+            return 0;
         return (innerBounds.width / 18) * getSlotsHeightNumberForFavorites();
     }
     
@@ -145,14 +155,16 @@ public class EntryListWidget extends WidgetWithBounds {
     }
     
     public int getTotalPages() {
-        if (ConfigObject.getInstance().isEntryListWidgetScrolled()) return 1;
-        return MathHelper.ceil(allStacks.size() / (float) entries.size());
+        if (ConfigObject.getInstance().isEntryListWidgetScrolled())
+            return 1;
+        return MathHelper.ceil((allStacks.size() + getScrollNumberForFavorites()) / (float) entries.size());
     }
     
     @Override
     public void render(int mouseX, int mouseY, float delta) {
         if (ConfigObject.getInstance().isEntryListWidgetScrolled()) {
-            for (EntryListEntry entry : entries) entry.clearStacks();
+            for(EntryListEntry entry : entries)
+                entry.clearStacks();
             ScissorsHandler.INSTANCE.scissor(bounds);
             int sizeForFavorites = getSlotsHeightNumberForFavorites();
             int skip = Math.max(0, MathHelper.floor(scroll / 18f) - sizeForFavorites);
@@ -160,9 +172,9 @@ public class EntryListWidget extends WidgetWithBounds {
             int i = nextIndex;
             blockedCount = 0;
             if (sizeForFavorites > 0) {
-                drawString(font, I18n.translate("text.rei.favorites"), innerBounds.x + 2, (int) (innerBounds.y + 6 - scroll), -1);
+                drawString(font, I18n.translate("text.rei.favorites"), innerBounds.x + 2, (int) (innerBounds.y + 8 - scroll), -1);
                 nextIndex += innerBounds.width / 18;
-                for (int i1 = 0; i1 < favorites.size(); i1++) {
+                for(int i1 = 0; i1 < favorites.size(); i1++) {
                     EntryStack stack = favorites.get(i1);
                     back1:
                     while (true) {
@@ -186,7 +198,7 @@ public class EntryListWidget extends WidgetWithBounds {
             }
             int offset = sizeForFavorites > 0 ? -12 : 0;
             back:
-            for (; i < allStacks.size(); i++) {
+            for(; i < allStacks.size(); i++) {
                 EntryStack stack = allStacks.get(i);
                 while (true) {
                     EntryListEntry entry = entries.get(nextIndex);
@@ -209,8 +221,10 @@ public class EntryListWidget extends WidgetWithBounds {
             ScissorsHandler.INSTANCE.removeLastScissor();
             renderScrollbar();
         } else {
-            for (EntryListEntry widget : entries) {
-                widget.isFavorites = false;
+            for(EntryListEntry widget : entries) {
+                widget.render(mouseX, mouseY, delta);
+            }
+            for(Widget widget : widgets) {
                 widget.render(mouseX, mouseY, delta);
             }
         }
@@ -236,7 +250,8 @@ public class EntryListWidget extends WidgetWithBounds {
                 if (ConfigObject.getInstance().doesSnapToRows()) {
                     double nearestRow = Math.round(to / 18.0) * 18.0;
                     scrollTo(nearestRow, false);
-                } else scrollTo(to, false);
+                } else
+                    scrollTo(to, false);
             }
         }
         return super.mouseDragged(mouseX, mouseY, int_1, double_3, double_4);
@@ -306,7 +321,7 @@ public class EntryListWidget extends WidgetWithBounds {
     @Override
     public boolean keyPressed(int int_1, int int_2, int int_3) {
         if (containsMouse(PointHelper.fromMouse()))
-            for (Widget widget : entries)
+            for(Widget widget : widgets)
                 if (widget.keyPressed(int_1, int_2, int_3))
                     return true;
         return false;
@@ -318,7 +333,8 @@ public class EntryListWidget extends WidgetWithBounds {
             updateSearch(searchTerm);
         else if (allStacks == null || favorites == null)
             updateSearch("");
-        else updateEntriesPosition();
+        else
+            updateEntriesPosition();
     }
     
     public void updateEntriesPosition() {
@@ -328,20 +344,38 @@ public class EntryListWidget extends WidgetWithBounds {
             List<EntryListEntry> entries = Lists.newLinkedList();
             int width = innerBounds.width / 18;
             int height = innerBounds.height / 18;
-            for (int currentY = 0; currentY < height; currentY++) {
-                for (int currentX = 0; currentX < width; currentX++) {
+            for(int currentY = 0; currentY < height; currentY++) {
+                for(int currentX = 0; currentX < width; currentX++) {
                     if (notSteppingOnExclusionZones(currentX * 18 + innerBounds.x, currentY * 18 + innerBounds.y, innerBounds)) {
                         entries.add((EntryListEntry) new EntryListEntry(currentX * 18 + innerBounds.x, currentY * 18 + innerBounds.y).noBackground());
                     }
                 }
             }
             page = Math.max(Math.min(page, getTotalPages() - 1), 0);
-            List<EntryStack> subList = allStacks.stream().skip(page * entries.size()).limit(entries.size()).collect(Collectors.toList());
-            for (int i = 0; i < subList.size(); i++) {
+            int numberForFavorites = getScrollNumberForFavorites();
+            List<EntryStack> subList = allStacks.stream().skip(Math.max(0, page * entries.size() - numberForFavorites)).limit(Math.max(0, entries.size() - Math.max(0, numberForFavorites - page * entries.size()))).collect(Collectors.toList());
+            for(int i = 0; i < subList.size(); i++) {
                 EntryStack stack = subList.get(i);
-                entries.get(i).clearStacks().entry(stack);
+                entries.get(i + Math.max(0, numberForFavorites - page * entries.size())).clearStacks().entry(stack);
+                entries.get(i + Math.max(0, numberForFavorites - page * entries.size())).isFavorites = false;
             }
             this.entries = entries;
+            this.widgets = Lists.newLinkedList(entries);
+            if (numberForFavorites > 0) {
+                int skippedFavorites = page * (entries.size() - width);
+                int j = 0;
+                if (skippedFavorites < favorites.size()) {
+                    widgets.add(new LabelWidget(new Point(innerBounds.x + 2, innerBounds.y + 6), I18n.translate("text.rei.favorites")).leftAligned());
+                    j += width;
+                }
+                List<EntryStack> subFavoritesList = favorites.stream().skip(skippedFavorites).limit(Math.max(0, entries.size() - width)).collect(Collectors.toList());
+                for(int i = 0; i < subFavoritesList.size(); i++) {
+                    EntryStack stack = subFavoritesList.get(i);
+                    entries.get(j).clearStacks().entry(stack);
+                    entries.get(j).isFavorites = true;
+                    j++;
+                }
+            }
         } else {
             page = 0;
             int width = innerBounds.width / 18;
@@ -351,7 +385,7 @@ public class EntryListWidget extends WidgetWithBounds {
             int currentX = 0;
             int currentY = 0;
             List<EntryListEntry> entries = Lists.newLinkedList();
-            for (int i = 0; i < slotsToPrepare; i++) {
+            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());
@@ -362,6 +396,7 @@ public class EntryListWidget extends WidgetWithBounds {
                 }
             }
             this.entries = entries;
+            this.widgets = Collections.unmodifiableList(entries);
         }
     }
     
@@ -384,9 +419,10 @@ public class EntryListWidget extends WidgetWithBounds {
     
     @SuppressWarnings("rawtypes")
     private boolean notSteppingOnExclusionZones(int left, int top, Rectangle listArea) {
-        for (DisplayHelper.DisplayBoundsHandler sortedBoundsHandler : DisplayHelper.getInstance().getSortedBoundsHandlers(minecraft.currentScreen.getClass())) {
+        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;
+            if (fit != ActionResult.PASS)
+                return fit == ActionResult.SUCCESS;
         }
         return true;
     }
@@ -398,38 +434,43 @@ public class EntryListWidget extends WidgetWithBounds {
             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 : EntryRegistry.getInstance().getStacksList()) {
+            for(EntryStack stack : EntryRegistry.getInstance().getStacksList()) {
                 if (lastSearchArguments.isEmpty() || canSearchTermsBeAppliedTo(stack, lastSearchArguments)) {
                     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));
+                    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);
+            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);
             allStacks = list;
         }
         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()) {
+            for(EntryStack stack : ConfigManager.getInstance().getFavorites()) {
                 if (lastSearchArguments.isEmpty() || canSearchTermsBeAppliedTo(stack, lastSearchArguments)) {
                     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));
+                    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);
+            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();
+        } else
+            favorites = Collections.emptyList();
         updateEntriesPosition();
     }
     
@@ -440,12 +481,14 @@ public class EntryListWidget extends WidgetWithBounds {
     
     @SuppressWarnings("deprecation")
     private boolean canSearchTermsBeAppliedTo(EntryStack stack, List<SearchArgument.SearchArguments> searchArguments) {
-        if (searchArguments.isEmpty()) return true;
+        if (searchArguments.isEmpty())
+            return true;
         String mod = null, name = null, tooltip = null, tags[] = null;
-        for (SearchArgument.SearchArguments arguments : searchArguments) {
+        for(SearchArgument.SearchArguments arguments : searchArguments) {
             boolean applicable = true;
-            for (SearchArgument argument : arguments.getArguments()) {
-                if (argument.getArgumentType() == SearchArgument.ArgumentType.ALWAYS) return true;
+            for(SearchArgument argument : arguments.getArguments()) {
+                if (argument.getArgumentType() == SearchArgument.ArgumentType.ALWAYS)
+                    return true;
                 else if (argument.getArgumentType() == SearchArgument.ArgumentType.MOD) {
                     if (mod == null)
                         mod = stack.getIdentifier().map(Identifier::getNamespace).orElse("").replace(SPACE, EMPTY).toLowerCase(Locale.ROOT);
@@ -472,16 +515,19 @@ public class EntryListWidget extends WidgetWithBounds {
                         if (stack.getType() == EntryStack.Type.ITEM) {
                             Identifier[] tagsFor = minecraft.getNetworkHandler().getTagManager().items().getTagsFor(stack.getItem()).toArray(new Identifier[0]);
                             tags = new String[tagsFor.length];
-                            for (int i = 0; i < tagsFor.length; i++) tags[i] = tagsFor[i].toString();
+                            for(int i = 0; i < tagsFor.length; i++)
+                                tags[i] = tagsFor[i].toString();
                         } else if (stack.getType() == EntryStack.Type.FLUID) {
                             Identifier[] tagsFor = minecraft.getNetworkHandler().getTagManager().fluids().getTagsFor(stack.getFluid()).toArray(new Identifier[0]);
                             tags = new String[tagsFor.length];
-                            for (int i = 0; i < tagsFor.length; i++) tags[i] = tagsFor[i].toString();
-                        } else tags = new String[0];
+                            for(int i = 0; i < tagsFor.length; i++)
+                                tags[i] = tagsFor[i].toString();
+                        } else
+                            tags = new String[0];
                     }
                     if (tags != null && tags.length > 0) {
                         boolean a = false;
-                        for (String tag : tags)
+                        for(String tag : tags)
                             if (argument.getFunction(argument.isInclude()).apply(tag))
                                 a = true;
                         if (!a) {
@@ -494,7 +540,8 @@ public class EntryListWidget extends WidgetWithBounds {
                     }
                 }
             }
-            if (applicable) return true;
+            if (applicable)
+                return true;
         }
         return false;
     }
@@ -502,12 +549,13 @@ public class EntryListWidget extends WidgetWithBounds {
     @SuppressWarnings("deprecation")
     private List<SearchArgument.SearchArguments> processSearchTerm(String searchTerm) {
         List<SearchArgument.SearchArguments> searchArguments = Lists.newArrayList();
-        for (String split : StringUtils.splitByWholeSeparatorPreserveAllTokens(searchTerm.toLowerCase(Locale.ROOT), "|")) {
+        for(String split : StringUtils.splitByWholeSeparatorPreserveAllTokens(searchTerm.toLowerCase(Locale.ROOT), "|")) {
             String[] terms = StringUtils.split(split);
-            if (terms.length == 0) searchArguments.add(SearchArgument.SearchArguments.ALWAYS);
+            if (terms.length == 0)
+                searchArguments.add(SearchArgument.SearchArguments.ALWAYS);
             else {
                 SearchArgument[] arguments = new SearchArgument[terms.length];
-                for (int i = 0; i < terms.length; i++) {
+                for(int i = 0; i < terms.length; i++) {
                     String term = terms[i];
                     if (term.startsWith("-@") || term.startsWith("@-")) {
                         arguments[i] = new SearchArgument(SearchArgument.ArgumentType.MOD, term.substring(2), false);
@@ -535,7 +583,7 @@ public class EntryListWidget extends WidgetWithBounds {
     
     @Override
     public List<? extends Widget> children() {
-        return entries;
+        return widgets;
     }
     
     @Override
@@ -559,7 +607,7 @@ public class EntryListWidget extends WidgetWithBounds {
             }
             if (!player.inventory.getCursorStack().isEmpty() && RoughlyEnoughItemsCore.hasPermissionToUsePackets())
                 return false;
-            for (Widget widget : children())
+            for(Widget widget : children())
                 if (widget.mouseClicked(double_1, double_2, int_1))
                     return true;
         }
@@ -582,7 +630,8 @@ public class EntryListWidget extends WidgetWithBounds {
         
         @Override
         protected void drawHighlighted(int mouseX, int mouseY, float delta) {
-            if (getCurrentEntry().getType() != EntryStack.Type.EMPTY) super.drawHighlighted(mouseX, mouseY, delta);
+            if (getCurrentEntry().getType() != EntryStack.Type.EMPTY)
+                super.drawHighlighted(mouseX, mouseY, delta);
         }
         
         private String getLocalizedName(InputUtil.KeyCode value) {

+ 17 - 4
src/main/java/me/shedaniel/rei/gui/widget/LabelWidget.java

@@ -45,6 +45,11 @@ public class LabelWidget extends WidgetWithBounds {
         return this;
     }
     
+    public LabelWidget leftAligned() {
+        setCentered(false);
+        return this;
+    }
+    
     public boolean isHasShadows() {
         return hasShadows;
     }
@@ -93,7 +98,9 @@ public class LabelWidget extends WidgetWithBounds {
     public Rectangle getBounds() {
         int width = font.getStringWidth(text);
         Point pos = getPosition();
-        return new Rectangle(pos.x - width / 2 - 1, pos.y - 5, width + 2, 14);
+        if (isCentered())
+            return new Rectangle(pos.x - width / 2 - 1, pos.y - 5, width + 2, 14);
+        return new Rectangle(pos.x - 1, pos.y - 5, width + 2, 14);
     }
     
     @Override
@@ -105,9 +112,15 @@ public class LabelWidget extends WidgetWithBounds {
     public void render(int mouseX, int mouseY, float delta) {
         int width = font.getStringWidth(text);
         Point pos = getPosition();
-        if (hasShadows)
-            font.drawWithShadow(text, pos.x - width / 2, pos.y, defaultColor);
-        else font.draw(text, pos.x - width / 2, pos.y, defaultColor);
+        if (isCentered()) {
+            if (hasShadows)
+                font.drawWithShadow(text, pos.x - width / 2, pos.y, defaultColor);
+            else font.draw(text, pos.x - width / 2, pos.y, defaultColor);
+        } else {
+            if (hasShadows)
+                font.drawWithShadow(text, pos.x, pos.y, defaultColor);
+            else font.draw(text, pos.x, pos.y, defaultColor);
+        }
     }
     
 }

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

@@ -71,7 +71,7 @@ public class ConfigManagerImpl implements ConfigManager {
             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, null))
+            KeyCodeEntry entry = ConfigEntryBuilder.create().startKeyCodeField(i13n, getUnsafely(field, config, InputUtil.UNKNOWN_KEYCODE))
                     .setDefaultValue(() -> getUnsafely(field, defaults))
                     .setSaveConsumer(newValue -> setUnsafely(field, config, newValue))
                     .build();
@@ -80,7 +80,7 @@ public class ConfigManagerImpl implements ConfigManager {
             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, null))
+            KeyCodeEntry entry = ConfigEntryBuilder.create().startKeyCodeField(i13n, getUnsafely(field, config, InputUtil.UNKNOWN_KEYCODE))
                     .setDefaultValue(() -> getUnsafely(field, defaults))
                     .setSaveConsumer(newValue -> setUnsafely(field, config, newValue))
                     .build();

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

@@ -216,7 +216,7 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData {
     
     @Override
     public InputUtil.KeyCode getFavoriteKeybind() {
-        return general.favoriteKeybind;
+        return general.favoriteKeybind == null ? InputUtil.UNKNOWN_KEYCODE : general.favoriteKeybind;
     }
     
     public static class General {

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

@@ -94,21 +94,28 @@ public class DefaultPlugin implements REIPluginV0 {
             return;
         }
         for (Item item : Registry.ITEM) {
-            entryRegistry.registerEntry(EntryStack.create(item));
+            ItemStack[] stacks = null;
             try {
+                stacks = entryRegistry.getAllStacksFromItem(item);
                 for (ItemStack stack : entryRegistry.getAllStacksFromItem(item)) {
                     entryRegistry.registerEntry(EntryStack.create(stack));
                 }
-            } catch (Exception e) {
+            } catch (Exception ignored) {
             }
+            if (stacks != null) {
+                for (ItemStack stack : entryRegistry.getAllStacksFromItem(item)) {
+                    entryRegistry.registerEntry(EntryStack.create(stack));
+                }
+            } else entryRegistry.registerEntry(EntryStack.create(item));
         }
+        EntryStack stack = EntryStack.create(Items.ENCHANTED_BOOK);
         for (Enchantment enchantment : Registry.ENCHANTMENT) {
             for (int i = enchantment.getMinimumLevel(); i <= enchantment.getMaximumLevel(); i++) {
                 Map<Enchantment, Integer> map = new HashMap<>();
                 map.put(enchantment, i);
                 ItemStack itemStack = new ItemStack(Items.ENCHANTED_BOOK);
                 EnchantmentHelper.set(map, itemStack);
-                entryRegistry.registerEntriesAfter(EntryStack.create(Items.ENCHANTED_BOOK), EntryStack.create(itemStack));
+                entryRegistry.registerEntriesAfter(stack, EntryStack.create(itemStack));
             }
         }
         for (Fluid fluid : Registry.FLUID) {