From 4d0713f7735926d2f2d979da1616f95e3a95ba4a Mon Sep 17 00:00:00 2001 From: Tommy Date: Sun, 26 Apr 2026 23:54:05 -0400 Subject: [PATCH 1/5] Fix group entries of shared stacks. --- .../jei/render/BookmarkListBatchRenderer.java | 10 ++++--- .../render/IngredientListBatchRenderer.java | 26 ++++++++++++------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/main/java/mezz/jei/render/BookmarkListBatchRenderer.java b/src/main/java/mezz/jei/render/BookmarkListBatchRenderer.java index c367ea80..b5616320 100644 --- a/src/main/java/mezz/jei/render/BookmarkListBatchRenderer.java +++ b/src/main/java/mezz/jei/render/BookmarkListBatchRenderer.java @@ -188,7 +188,7 @@ public void setCollapsed(int startIndex, List collapsedL // Flatten: BookmarkItem → sub-elements (expanded) or single slot (collapsed). // itemToGroupIndex preserves the bookmark's group index for sub-elements that don't carry it themselves. List displayItems = new ArrayList<>(); - Map itemToCollapsed = new HashMap<>(); + List displayItemGroups = new ArrayList<>(); Map itemToGroupIndex = new HashMap<>(); for (IIngredientListElement element : collapsedList) { @@ -200,16 +200,18 @@ public void setCollapsed(int startIndex, List collapsedL if (isBookmarkItemExpanded(bookmarkItem)) { for (IIngredientListElement subElement : collapsed.getIngredients()) { displayItems.add(subElement); - itemToCollapsed.put(subElement, collapsed); + displayItemGroups.add(collapsed); itemToGroupIndex.put(subElement, groupIndex); expandedElementToBookmark.put(subElement, bookmarkItem); } } else { displayItems.add(element); + displayItemGroups.add(null); itemToGroupIndex.put(element, groupIndex); } } else { displayItems.add(element); + displayItemGroups.add(null); itemToGroupIndex.put(element, element.getGroupIndex()); } } @@ -270,10 +272,10 @@ public void setCollapsed(int startIndex, List collapsedL collapsedRendererToBookmark.put(renderer, bookmarkItem); } else { set(ingredientListSlot, displayItem); - CollapsedGroupIngredient parentCollapsed = itemToCollapsed.get(displayItem); + CollapsedGroupIngredient parentCollapsed = displayItemGroups.get(i); if (parentCollapsed != null) { collapsedStackIndexed.put(slotIndex, parentCollapsed); - expandedElementToGroup.put(displayItem, parentCollapsed); + expandedElementToGroup.put(ingredientListSlot, parentCollapsed); expandedGroupSlots.computeIfAbsent(parentCollapsed, k -> new ArrayList<>()) .add(new Rectangle(ingredientListSlot.getArea())); } diff --git a/src/main/java/mezz/jei/render/IngredientListBatchRenderer.java b/src/main/java/mezz/jei/render/IngredientListBatchRenderer.java index d1d2aa14..a616b878 100644 --- a/src/main/java/mezz/jei/render/IngredientListBatchRenderer.java +++ b/src/main/java/mezz/jei/render/IngredientListBatchRenderer.java @@ -38,7 +38,7 @@ public class IngredientListBatchRenderer { protected final List renderOther = new ArrayList<>(); protected final List renderCollapsed = new ArrayList<>(); protected final Map collapsedStackIndexed = new HashMap<>(); - protected final Map, CollapsedGroupIngredient> expandedElementToGroup = new HashMap<>(); + protected final Map expandedElementToGroup = new HashMap<>(); // Per-group list of individual slot rectangles (used for per-slot fill + edge-detection border). protected final Map> expandedGroupSlots = new HashMap<>(); @@ -159,7 +159,7 @@ public void setCollapsed(final int startIndex, List coll // This ensures expanded groups don't break pagination — firstItemIndex is an index into // the flattened view, which matches what collapsedSize() now returns. List displayItems = new ArrayList<>(); - Map itemToCollapsed = new HashMap<>(); + List displayItemGroups = new ArrayList<>(); for (IIngredientListElement obj : collapsedList) { if (obj instanceof CollapsedGroupIngredient) { CollapsedGroupIngredient collapsed = (CollapsedGroupIngredient) obj; @@ -168,23 +168,26 @@ public void setCollapsed(final int startIndex, List coll if (filterIngredients.size() == 1) { // Expanded but filtered to a single item: treat as a plain slot, no group border. displayItems.add(filterIngredients.get(0)); + displayItemGroups.add(null); } else { // Expanded: add each ingredient individually, track which belong to this group for (IIngredientListElement element : filterIngredients) { displayItems.add(element); - itemToCollapsed.put(element, collapsed); + displayItemGroups.add(collapsed); } } } else if (collapsed.size() == 1) { // Single-item group: render as a plain ingredient slot without collapsed visuals. - // Not tracked in itemToCollapsed so clicks/hover treat it as a normal item. displayItems.add(collapsed.getDisplayIngredients().get(0)); + displayItemGroups.add(null); } else { // Collapsed: add the CollapsedStack itself as a single display item displayItems.add(collapsed); + displayItemGroups.add(null); } } else { displayItems.add(obj); + displayItemGroups.add(null); } } @@ -211,10 +214,10 @@ public void setCollapsed(final int startIndex, List coll collapsedStackIndexed.put(slotIndex, collapsed); } else { set(ingredientListSlot, displayItem); - CollapsedGroupIngredient parentCollapsed = itemToCollapsed.get(displayItem); + CollapsedGroupIngredient parentCollapsed = displayItemGroups.get(i); if (parentCollapsed != null) { collapsedStackIndexed.put(slotIndex, parentCollapsed); - expandedElementToGroup.put(displayItem, parentCollapsed); + expandedElementToGroup.put(ingredientListSlot, parentCollapsed); expandedGroupSlots.computeIfAbsent(parentCollapsed, k -> new ArrayList<>()) .add(new Rectangle(ingredientListSlot.getArea())); } @@ -336,11 +339,14 @@ public IngredientRenderer getHovered(int mouseX, int mouseY) { @Nullable public CollapsedGroupIngredient getExpandedCollapsedGroupAt(int mouseX, int mouseY) { - IngredientRenderer hovered = getHovered(mouseX, mouseY); - if (hovered == null) { - return null; + for (List row : slots) { + for (IngredientListSlot slot : row) { + if (slot.isMouseOver(mouseX, mouseY)) { + return expandedElementToGroup.get(slot); + } + } } - return expandedElementToGroup.get(hovered.getElement()); + return null; } public void renderExpandedGroupOutlines() { From afe02457c31f5cd1bdd9fbbe03faae84557b2163 Mon Sep 17 00:00:00 2001 From: Tommy Date: Tue, 5 May 2026 23:04:26 -0400 Subject: [PATCH 2/5] CountUtil overload, backwards compatible. --- src/main/java/mezz/jei/util/CountUtil.java | 37 ++++++++++++++++------ 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/src/main/java/mezz/jei/util/CountUtil.java b/src/main/java/mezz/jei/util/CountUtil.java index 9ba106b7..33cbdc8b 100644 --- a/src/main/java/mezz/jei/util/CountUtil.java +++ b/src/main/java/mezz/jei/util/CountUtil.java @@ -40,7 +40,9 @@ public static void renderCountString(FontRenderer font, long count, int xPositio } /** - * Renders string as it would be if it was a count on an itemstack + * Renders string as it would be if it was a count on an itemstack. + * Legacy method overload: uses a bool instead of a float for scale. + * * @param font The font renderer * @param count The count * @param xPosition X coordinate @@ -50,23 +52,40 @@ public static void renderCountString(FontRenderer font, long count, int xPositio * (mimics the way item stack counts are rendered), * or to use xPosition and yPosition as absolute screen coordinates * @param scale True to scale down by 1/2 in the screen-space + * @see #renderStringAsCount(FontRenderer, String, int, int, int, boolean, float) */ public static void renderStringAsCount(FontRenderer font, String count, int xPosition, int yPosition, int color, boolean relative, boolean scale) { + renderStringAsCount(font, count, xPosition, yPosition, color, relative, scale ? 0.5f : 1.0f); + } + + /** + * Renders string as it would be if it was a count on an itemstack + * @param font The font renderer + * @param count The count + * @param xPosition X coordinate + * @param yPosition Y coordinate + * @param color Color of rendered string + * @param relative Whether or not to render relative to the xPosition and yPosition + * (mimics the way item stack counts are rendered), + * or to use xPosition and yPosition as absolute screen coordinates + * @param scale Value to scale the count by + */ + public static void renderStringAsCount(FontRenderer font, String count, int xPosition, int yPosition, int color, boolean relative, float scale) { GlStateManager.pushMatrix(); GlStateManager.disableLighting(); GlStateManager.disableDepth(); GlStateManager.disableBlend(); - if (scale) { - GlStateManager.scale(0.5F, 0.5F, 1.0F); + if (scale != 1.0f) { + GlStateManager.scale(scale, scale, 1.0F); } - int x = scale ? - (relative ? xPosition + 16 : xPosition) * 2 - font.getStringWidth(count) : - (relative ? xPosition + 17 : xPosition) - font.getStringWidth(count); - int y = scale ? - (relative ? yPosition + 16 : yPosition) * 2 - 8 : - relative ? yPosition + 9 : yPosition; + int x = scale != 1.0f + ? (int)((relative ? xPosition + 16 : xPosition) / scale) - font.getStringWidth(count) + : (relative ? xPosition + 17 : xPosition) - font.getStringWidth(count); + int y = scale != 1.0f + ? (int)((relative ? yPosition + 16 : yPosition) / scale) - 8 + : (relative ? yPosition + 9 : yPosition); font.drawStringWithShadow(count, x, y, color); From b09b3ec76afd968bd68dac4f472efa47239e10f6 Mon Sep 17 00:00:00 2001 From: Tommy Date: Tue, 5 May 2026 23:05:14 -0400 Subject: [PATCH 3/5] Use countutil in badge render + comment cleanup --- .../jei/render/CollapsedGroupRenderer.java | 71 ++++++------------- 1 file changed, 22 insertions(+), 49 deletions(-) diff --git a/src/main/java/mezz/jei/render/CollapsedGroupRenderer.java b/src/main/java/mezz/jei/render/CollapsedGroupRenderer.java index 7924b704..d62b4f68 100644 --- a/src/main/java/mezz/jei/render/CollapsedGroupRenderer.java +++ b/src/main/java/mezz/jei/render/CollapsedGroupRenderer.java @@ -6,6 +6,7 @@ import mezz.jei.ingredients.group.CollapsedGroupIngredient; import mezz.jei.input.ClickedIngredient; import mezz.jei.util.CollapsedClickAction; +import mezz.jei.util.CountUtil; import mezz.jei.util.Translator; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; @@ -25,9 +26,8 @@ import java.util.List; /** - * Renders a collapsed group as a single ingredient list slot. - * Shows the first item with a count badge indicating total group size, - * plus a semi-transparent background to distinguish it from normal items. + * Renders a collapsed group shows the first two items with a count badge + * indicating total group size and background to distinguish it from normal itemstacks. */ public class CollapsedGroupRenderer implements IIngredientRenderer { /** Singleton registered with the ingredient type system — {@code collapsedStack} is null. */ @@ -68,11 +68,6 @@ public void render(Minecraft minecraft) { /** * Stateless render at an arbitrary position — shared by the instance render and * the {@link IIngredientRenderer} contract. - * For groups with 2+ items, mimics REI's stacked-card icon: each item is rendered at - * 0.75× scale (12 px), offset 4 px so both stay entirely within the 16×16 slot area. - * Back item (upper-right): screen origin (x+4, y+0), occupies (x+4..x+16, y..y+12) - * Front item (lower-left) : screen origin (x+0, y+4), occupies (x..x+12, y+4..y+16) - * Count badge is drawn at 0.75× scale in orange in the bottom-right corner. */ private static void renderAt(Minecraft minecraft, CollapsedGroupIngredient ingredient, int x, int y) { List> ingredients = ingredient.getDisplayIngredients(); @@ -88,48 +83,32 @@ private static void renderAt(Minecraft minecraft, CollapsedGroupIngredient ingre GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO ); - // Draw background tint to visually distinguish collapsed groups + // Background tint GuiScreen.drawRect(x, y, x + 16, y + 16, ingredient.getBackgroundColor()); GlStateManager.disableBlend(); if (ingredients.size() == 1) { - // Single item: render at full size renderElementAt(minecraft, ingredients.get(0), x, y, 1.0f); } else { - // 0.75 scale → 12 px icon. - // Back (upper-right): origin at (x+4, y+0) → occupies x+4..x+16, y..y+12 - // Front (lower-left) : origin at (x+0, y+4) → occupies x..x+12, y+4..y+16 + // Scaled item previews RenderItem renderItem = minecraft.getRenderItem(); - renderElementAt(minecraft, ingredients.get(1), x + 4, y + 0, 0.75f); // back - // Elevate zLevel so the front item's depth values are naturally in front of the - // back item's geometry. Using GL_LEQUAL (normal) keeps the front item's own - // internal face culling intact — GL_ALWAYS would break tile-entity models. + // Back + renderElementAt(minecraft, ingredients.get(1), x + 4, y + 0, 0.75f); float prevZLevel = renderItem.zLevel; renderItem.zLevel += 100; - renderElementAt(minecraft, ingredients.get(0), x + 0, y + 4, 0.75f); // front + // Front + renderElementAt(minecraft, ingredients.get(0), x + 0, y + 4, 0.75f); renderItem.zLevel = prevZLevel; } - // Count badge: 0.75× scale, orange, right-aligned at the bottom of the slot + // Count badge int count = ingredient.size(); if (count > 1) { + String countStr = CountUtil.minifyCountString(count); + float badgeScale = count <= 999 ? 0.75f : 0.5f; FontRenderer fontRenderer = minecraft.fontRenderer; - String countStr = String.valueOf(count); - GlStateManager.disableLighting(); - GlStateManager.disableDepth(); - GlStateManager.disableBlend(); - final float badgeScale = 0.75f; - // Convert desired screen position to scaled-coordinate space. - // Screen right edge: x+16 → scaled coord (x+16)/badgeScale - // Screen top of text: y+10 → scaled coord (y+10)/badgeScale - int textWidth = fontRenderer.getStringWidth(countStr); - int scaledRight = (int) ((x + 16) / badgeScale); - int scaledTop = (int) ((y + 10) / badgeScale); - GlStateManager.pushMatrix(); - GlStateManager.scale(badgeScale, badgeScale, 1.0f); - fontRenderer.drawStringWithShadow(countStr, scaledRight - textWidth, scaledTop, 0xFFAA00); - GlStateManager.popMatrix(); - GlStateManager.enableDepth(); + + CountUtil.renderStringAsCount(fontRenderer, countStr, x, y, 0xFFAA00, true, badgeScale); } drawCollapsedBorder(x, y, ingredient.getBorderColor()); @@ -137,7 +116,6 @@ private static void renderAt(Minecraft minecraft, CollapsedGroupIngredient ingre /** * Renders one ingredient at (x, y) at the given scale using the GL matrix stack. - * Delegates to renderItemAndEffectIntoGUI so all item types (2D, 3D, built-in) render correctly. */ private static void renderElementAt(Minecraft minecraft, IIngredientListElement element, int x, int y, float scale) { Object ingredient = element.getIngredient(); @@ -166,7 +144,7 @@ private static void drawCollapsedBorder(int x, int y, int borderColor) { GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO ); - // Small triangle indicator in the top-left corner to show it's collapsible + // top left triangle indicator GlStateManager.disableLighting(); GlStateManager.disableDepth(); GuiScreen.drawRect(x, y, x + 4, y + 1, borderColor); @@ -175,9 +153,6 @@ private static void drawCollapsedBorder(int x, int y, int borderColor) { GlStateManager.disableBlend(); } - // --- IIngredientRenderer implementation --- - // INSTANCE (null stack) is registered with the ingredient type system. - @Override public void render(Minecraft minecraft, int xPosition, int yPosition, @Nullable CollapsedGroupIngredient ingredient) { if (ingredient == null || ingredient.isEmpty()) { @@ -207,7 +182,7 @@ public void drawTooltip(Minecraft minecraft, int mouseX, int mouseY) { List> ingredients = collapsedStack.getDisplayIngredients(); if (ingredients.isEmpty()) return; - // Single-item group (e.g. search filtered to one result): show the item's native tooltip + // Single-item group - show the item's native tooltip if (ingredients.size() == 1) { new IngredientRenderer<>(ingredients.get(0)).drawTooltip(minecraft, mouseX, mouseY); return; @@ -215,8 +190,8 @@ public void drawTooltip(Minecraft minecraft, int mouseX, int mouseY) { FontRenderer font = minecraft.fontRenderer; final int COLS = 8; - final int SLOT = 18; // 16px icon + 1px padding each side - final int MAX_VISIBLE = COLS * 2 + 7; // 23 = rows of 8, 8, 7 + final int SLOT = 18; + final int MAX_VISIBLE = COLS * 2 + 7; int total = ingredients.size(); int shown = Math.min(total, MAX_VISIBLE); @@ -228,8 +203,7 @@ public void drawTooltip(Minecraft minecraft, int mouseX, int mouseY) { String header = TextFormatting.GOLD + collapsedStack.getDisplayName() + TextFormatting.GRAY + " (" + total + " items)"; - // In OPEN_GROUP mode, alt+click uses first item; show that as the hint. - // In FIRST_ITEM mode, alt+click expands; show that instead. + // OPEN_GROUP/FIRST_ITEM hint tooltip String hint = TextFormatting.YELLOW + Translator.translateToLocal( Config.getCollapsedClickAction() == CollapsedClickAction.OPEN_GROUP ? "hei.tooltip.collapsed.expand.firstItem" @@ -250,7 +224,7 @@ public void drawTooltip(Minecraft minecraft, int mouseX, int mouseY) { GlStateManager.disableLighting(); GlStateManager.disableDepth(); - // Draw tooltip background (MC-style dark purple box with gradient border) + // Tooltip background final int z = 300; int bg = 0xF0100010, bs = 0x505000FF, be = (bs & 0xFEFEFE) >> 1 | (bs & 0xFF000000); GuiUtils.drawGradientRect(z, tx-3, ty-4, tx+tw+3, ty-3, bg, bg); @@ -288,7 +262,7 @@ public void drawTooltip(Minecraft minecraft, int mouseX, int mouseY) { RenderHelper.disableStandardItemLighting(); GlStateManager.popMatrix(); - // "+N" overflow indicator in 8th slot of row 3 (only when there are hidden items) + // "+N" overflow indicator GlStateManager.disableDepth(); GlStateManager.disableLighting(); if (overflow > 0) { @@ -315,7 +289,7 @@ public ClickedIngredient getClickedIngredient() { if (ingredients.isEmpty()) { return null; } - // Return CollapsedStack directly — it is a registered IIngredientType + // Return CollapsedStack directly registered IIngredientType return ClickedIngredient.create(collapsedStack, area); } @@ -323,7 +297,6 @@ public boolean isMouseOver(int mouseX, int mouseY) { return area.contains(mouseX, mouseY); } - @SuppressWarnings("unchecked") private static void renderIngredient(Minecraft minecraft, int x, int y, IIngredientListElement element) { IIngredientRenderer renderer = element.getIngredientRenderer(); T ingredient = element.getIngredient(); From 1834af1c9d9a51bdc50d34441132af0f85b582ed Mon Sep 17 00:00:00 2001 From: Tommy Date: Tue, 5 May 2026 23:31:26 -0400 Subject: [PATCH 4/5] More comment changes. --- src/main/java/mezz/jei/render/CollapsedGroupRenderer.java | 7 +++---- src/main/java/mezz/jei/util/CountUtil.java | 1 - 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/mezz/jei/render/CollapsedGroupRenderer.java b/src/main/java/mezz/jei/render/CollapsedGroupRenderer.java index d62b4f68..03bbe27c 100644 --- a/src/main/java/mezz/jei/render/CollapsedGroupRenderer.java +++ b/src/main/java/mezz/jei/render/CollapsedGroupRenderer.java @@ -26,8 +26,8 @@ import java.util.List; /** - * Renders a collapsed group shows the first two items with a count badge - * indicating total group size and background to distinguish it from normal itemstacks. + * Renders a collapsed group showing the first two items scaled as a preview, background tint, + * and a count badge indicating number of items in the group. */ public class CollapsedGroupRenderer implements IIngredientRenderer { /** Singleton registered with the ingredient type system — {@code collapsedStack} is null. */ @@ -144,7 +144,7 @@ private static void drawCollapsedBorder(int x, int y, int borderColor) { GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO ); - // top left triangle indicator + // top left indicator GlStateManager.disableLighting(); GlStateManager.disableDepth(); GuiScreen.drawRect(x, y, x + 4, y + 1, borderColor); @@ -289,7 +289,6 @@ public ClickedIngredient getClickedIngredient() { if (ingredients.isEmpty()) { return null; } - // Return CollapsedStack directly registered IIngredientType return ClickedIngredient.create(collapsedStack, area); } diff --git a/src/main/java/mezz/jei/util/CountUtil.java b/src/main/java/mezz/jei/util/CountUtil.java index 33cbdc8b..d4eb7c87 100644 --- a/src/main/java/mezz/jei/util/CountUtil.java +++ b/src/main/java/mezz/jei/util/CountUtil.java @@ -41,7 +41,6 @@ public static void renderCountString(FontRenderer font, long count, int xPositio /** * Renders string as it would be if it was a count on an itemstack. - * Legacy method overload: uses a bool instead of a float for scale. * * @param font The font renderer * @param count The count From 8a94235fbd5b6372efc9c7f174e9d300c58d817c Mon Sep 17 00:00:00 2001 From: Tommy Date: Wed, 6 May 2026 00:46:52 -0400 Subject: [PATCH 5/5] List -> Int2ObjectMap --- .../mezz/jei/render/BookmarkListBatchRenderer.java | 8 ++++---- .../mezz/jei/render/IngredientListBatchRenderer.java | 10 ++++------ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/main/java/mezz/jei/render/BookmarkListBatchRenderer.java b/src/main/java/mezz/jei/render/BookmarkListBatchRenderer.java index b5616320..d1518f4a 100644 --- a/src/main/java/mezz/jei/render/BookmarkListBatchRenderer.java +++ b/src/main/java/mezz/jei/render/BookmarkListBatchRenderer.java @@ -1,5 +1,7 @@ package mezz.jei.render; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; @@ -188,7 +190,7 @@ public void setCollapsed(int startIndex, List collapsedL // Flatten: BookmarkItem → sub-elements (expanded) or single slot (collapsed). // itemToGroupIndex preserves the bookmark's group index for sub-elements that don't carry it themselves. List displayItems = new ArrayList<>(); - List displayItemGroups = new ArrayList<>(); + Int2ObjectMap displayItemGroups = new Int2ObjectOpenHashMap<>(); Map itemToGroupIndex = new HashMap<>(); for (IIngredientListElement element : collapsedList) { @@ -199,19 +201,17 @@ public void setCollapsed(int startIndex, List collapsedL int groupIndex = element.getGroupIndex(); if (isBookmarkItemExpanded(bookmarkItem)) { for (IIngredientListElement subElement : collapsed.getIngredients()) { + displayItemGroups.put(displayItems.size(), collapsed); displayItems.add(subElement); - displayItemGroups.add(collapsed); itemToGroupIndex.put(subElement, groupIndex); expandedElementToBookmark.put(subElement, bookmarkItem); } } else { displayItems.add(element); - displayItemGroups.add(null); itemToGroupIndex.put(element, groupIndex); } } else { displayItems.add(element); - displayItemGroups.add(null); itemToGroupIndex.put(element, element.getGroupIndex()); } } diff --git a/src/main/java/mezz/jei/render/IngredientListBatchRenderer.java b/src/main/java/mezz/jei/render/IngredientListBatchRenderer.java index a616b878..0dc95d04 100644 --- a/src/main/java/mezz/jei/render/IngredientListBatchRenderer.java +++ b/src/main/java/mezz/jei/render/IngredientListBatchRenderer.java @@ -1,6 +1,8 @@ package mezz.jei.render; import com.google.common.base.Preconditions; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import mezz.jei.api.ingredients.ISlowRenderItem; import mezz.jei.config.Config; @@ -159,7 +161,7 @@ public void setCollapsed(final int startIndex, List coll // This ensures expanded groups don't break pagination — firstItemIndex is an index into // the flattened view, which matches what collapsedSize() now returns. List displayItems = new ArrayList<>(); - List displayItemGroups = new ArrayList<>(); + Int2ObjectMap displayItemGroups = new Int2ObjectOpenHashMap<>(); for (IIngredientListElement obj : collapsedList) { if (obj instanceof CollapsedGroupIngredient) { CollapsedGroupIngredient collapsed = (CollapsedGroupIngredient) obj; @@ -168,26 +170,22 @@ public void setCollapsed(final int startIndex, List coll if (filterIngredients.size() == 1) { // Expanded but filtered to a single item: treat as a plain slot, no group border. displayItems.add(filterIngredients.get(0)); - displayItemGroups.add(null); } else { // Expanded: add each ingredient individually, track which belong to this group for (IIngredientListElement element : filterIngredients) { + displayItemGroups.put(displayItems.size(), collapsed); displayItems.add(element); - displayItemGroups.add(collapsed); } } } else if (collapsed.size() == 1) { // Single-item group: render as a plain ingredient slot without collapsed visuals. displayItems.add(collapsed.getDisplayIngredients().get(0)); - displayItemGroups.add(null); } else { // Collapsed: add the CollapsedStack itself as a single display item displayItems.add(collapsed); - displayItemGroups.add(null); } } else { displayItems.add(obj); - displayItemGroups.add(null); } }